1.判断数组中三数之和
2.判断数组中最接近的三数之和
3.排序数组中剔除重复的数
4.数组序列字典序下一个排列
5.旋转数组中搜索特定值
6.排序数组中起始位置和结束位置
7.排序数组中搜索或者插入特定值
8.最大连续子数组和
9.快速排序
10.寻找数组中超过一半的元素
1.判断数组中三数之和
1.给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
- 关键点提炼:
--- 三个元素
--- a+b+c=0
--- 不能重复
采用双向链表的思路。解决这类问题,都是先对数组排序,然后根据有序数组的特性使用双向链表来解决问题。一个指针在start,一个指针在end,然后根据判断来决定start后移还是end前移。
// Author:jefflee1314
import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] num = new int[]{-1,0,1,2,-1,-4};
System.out.println(instance.threeSum(num));
}
}
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int iNum =Integer.MAX_VALUE ;
int jNum;
int kNum;
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < nums.length && nums[i] <= 0; i++) {
if (iNum == nums[i]) {
continue;
}
iNum = nums[i];
int j = i + 1;
int k = nums.length - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
} else {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
result.add(list);
jNum = nums[j];
do {
j++;
if (j>=k) {
break;
}
} while (jNum == nums[j]);
kNum = nums[k];
do {
k--;
if (j>=k) {
break;
}
} while (kNum == nums[k]);
}
}
}
return result;
}
}
2.判断数组中最接近的三数之和
2.给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
// Author:jefflee1314
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] num = new int[]{-1,2,1,-4};
System.out.println(instance.threeSumClosest(num,1));
}
}
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0] + nums[1] + nums[2];
for (int i = 0; i < nums.length - 2; i++) {
int left = i + 1, right = nums.length - 1;
while (left < right) {
int threeSum = nums[i] + nums[left] + nums[right];
if (threeSum > target)
right--;
else if (threeSum < target)
left++;
else
return target;
res = Math.abs(threeSum - target) > Math.abs(res - target) ? res : threeSum;
}
}
return res;
}
}
3.排序数组中剔除重复的数
3.给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
- 示例:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
数组完成排序后,我们可以放置两个指针 i 和 j,其中 i 是慢指针,而 j 是快指针。只要 nums[i] = nums[j],我们就增加 j 以跳过重复项。
当我们遇到 nums[j] != nums[i]时,跳过重复项的运行已经结束,因此我们必须把它(nums[j])的值复制到 nums[i + 1]。然后递增 i,接着我们将再次重复相同的过程,直到 j到达数组的末尾为止。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{1,1,2,2,2,3,3,4,4,4,4,4,5,5,6,6,7};
int result = instance.removeDuplicates(nums);
for(int i=0;i<result;i++) {
System.out.println(nums[i]);
}
}
}
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
类似题目:给定一个数组 nums和一个值 val,你需要原地移除所有数值等于 val的元素,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组,并在使用 O(1) 额外空间的条件下完成。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{0,1,2,2,3,0,4,2};
int result = instance.removeElement(nums,2);
for(int i=0;i<result;i++) {
System.out.println(nums[i]);
}
}
}
class Solution {
public int removeElement(int[] nums, int val) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != val) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
4.数组序列字典序下一个排列
4.实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
Next_Permutation.gif
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
//int[] nums = new int[]{1,5,8,4,7,6,5,3,1};
int[] nums = new int[]{5,1,1};
for(int i=0;i<nums.length;i++) {
System.out.print(nums[i]);
System.out.print(" ");
}
System.out.println();
instance.nextPermutation(nums);
for(int i=0;i<nums.length;i++) {
System.out.print(nums[i]);
System.out.print(" ");
}
}
}
class Solution {
public void nextPermutation(int[] nums) {
if(nums.length<=1)return;
int i=nums.length-2;
while(i>=0 && nums[i]>=nums[i+1]){
i--;
}
if(i<0){
reverseArr(nums, 0, nums.length-1);
return;
}
int j=i;
int startNums = nums[i];
while(j<nums.length-1&& nums[j+1]>startNums) {
j++;
}
swapArr(nums, i, j);
reverseArr(nums, i+1, nums.length-1);
}
public void reverseArr(int[] nums, int start,int end) {
while(start<end){
int temp = nums[start];
nums[start]=nums[end];
nums[end]=temp;
start++;
end--;
}
}
public void swapArr(int[] nums, int i,int j) {
int temp = nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
5.旋转数组中搜索特定值
5.假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
思路:首先找到旋转的点(这个过程时间复杂度是O(logN)),这样这个index左边和右边都是有序的,分别对左边和右边的数组进行二分查找来确定最终的target所在的索引。二分查找的时间复杂度也是O(logN)
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{1,2,3,4,5,6};
for(int i=0;i<nums.length;i++) {
System.out.print(nums[i]);
System.out.print(" ");
}
System.out.println();
int result = instance.search(nums, 5);
System.out.println(result);
}
}
class Solution {
public int search(int[] nums, int target) {
int i=0;
int j=nums.length-1;
int mid = (i+j)/2;
while(i<j&&nums[i]>nums[j]){
if(nums[i]<nums[mid]){
i=mid+1;
}
if(nums[mid]<nums[j]){
j=mid-1;
}
mid=(i+j)/2;
}
int result = binarySearch(nums, 0, i-1,target);
if (result==-1) {
result = binarySearch(nums,i,nums.length-1, target);
}
return result;
}
public int binarySearch(int[] nums,int start, int end, int target) {
int mid=(start+end)/2;
while(start<=end){
if(nums[mid]<target){
start=mid+1;
}
if(nums[mid]>target) {
end=mid-1;
}
if(nums[mid]==target) {
return mid;
}
mid=(start+end)/2;
}
return -1;
}
}
6.排序数组中起始位置和结束位置
6.给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。**
算法的要求是 O(log n),这实际上已经给我们暗示了,要求你在有序数组中使用二分查找法,本体不太难,重要的是把握一些边界条件,这个需要十分仔细。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{1,1,1,2,4,4,4,5,5,5,5,6,7,8,8,9,9,9,9,9,9,10};
int target = 9;
int[] result = instance.searchRange(nums,target);
for(int i=0;i<result.length;i++){
System.out.print(result[i]);
System.out.print(" ");
}
System.out.println();
}
}
class Solution {
public int[] searchRange(int[] nums, int target) {
int start=-1;
int end=-1;
int[] result = new int[]{start,end};
if(nums.length==0||(nums.length==1&&nums[0]!=target)){
return result;
}
int targetIndex = binarySearch(nums, target, 0, nums.length-1);
System.out.println("targetIndex="+targetIndex);
//1.get left index
if(targetIndex==0){
start=0;
} else {
int i=0;
int j=targetIndex-1;
int searchIndex=binarySearch(nums, target,i,j);
int finalResult = -1;
while(searchIndex!=-1){
finalResult = searchIndex;
if(searchIndex==0){
break;
}else if(searchIndex-1==0 && nums[0]==target){
finalResult=0;
break;
} else {
i=0;
j=finalResult-1;
if(i<j){
searchIndex=binarySearch(nums, target,i,j);
}else{
break;
}
}
}
if(finalResult==-1){
start=targetIndex;
}else {
start=finalResult;
}
}
System.out.println("start="+start);
//2.get right index
if(targetIndex==nums.length-1){
end=nums.length-1;
} else {
int i=targetIndex+1;
int j=nums.length-1;
int searchIndex=binarySearch(nums, target,i,j);
int finalResult = -1;
while(searchIndex!=-1){
finalResult = searchIndex;
if(searchIndex==nums.length-1){
break;
}else if(searchIndex+1==nums.length-1 && nums[nums.length-1]==target){
finalResult=nums.length-1;
break;
}else {
i=finalResult+1;
j=nums.length-1;
if(i<j){
searchIndex=binarySearch(nums, target,i,j);
}else{
break;
}
}
}
if(finalResult==-1){
end=targetIndex;
}else {
end=finalResult;
}
}
System.out.println("end="+end);
result[0]=start;
result[1]=end;
return result;
}
public int binarySearch(int[] nums, int target, int start, int end) {
int mid=(start+end)/2;
while(start<=end){
if(nums[mid]<target){
start=mid+1;
}
if(nums[mid]>target){
end=mid-1;
}
if(nums[mid]==target){
return mid;
}
mid=(start+end)/2;
}
return -1;
}
}
7.排序数组中搜索或者插入特定值
7.给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{1,3,5,6};
int target=2;
int result = instance.searchInsert(nums,target);
System.out.println(result);
}
}
class Solution {
public int searchInsert(int[] nums, int target) {
return binarySearch(nums,target);
}
public int binarySearch(int[] nums, int target) {
int start=0;
int end=nums.length-1;
int mid=(start+end)/2;
while(start<=end){
if(nums[mid]==target){
return mid;
}
if(nums[mid]<target){
start=mid+1;
}
if(nums[mid]>target){
end=mid-1;
}
mid=(start+end)/2;
}
return start;
}
}
8.最大连续子数组和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] nums = new int[]{-2,1,-3,4,-1,2,1,-5,4};
int result = instance.maxSubArray(nums);
System.out.println(result);
}
}
class Solution {
public int maxSubArray(int[] nums) {
int curSum = Integer.MIN_VALUE;
int maxSum = Integer.MIN_VALUE;
for(int i=0;i<nums.length;i++) {
if(curSum >= 0) {
curSum += nums[i];
}else {
curSum = nums[i];
}
if(curSum > maxSum) {
maxSum = curSum;
}
}
return maxSum;
}
}
9.快速排序
class Solution {
public int partition(int[] arr, int start, int end) {
int num = arr[start];
int i=start;
int j=end+1;
while(true) {
while(arr[++i]<num) {
if(i==end)break;
}
while(arr[--j]>num) {
if(j==start)break;
}
if(i>=j)break;
swap(arr, i,j);
}
swap(arr, start, j);
return j;
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void quicksort(int[] arr, int start, int end) {
if(start>=end) return;
int privt = partition(arr, start, end);
quicksort(arr, start, privt-1);
quicksort(arr, privt+1, end);
}
}
10.寻找数组中超过一半的元素
假设一个数组中存在一个元素,超过这个数组长度的一半,请找出这个元素。
// Author:jefflee1314
public class Main {
public static void main(String[] args) {
Solution instance = new Solution();
int[] array = new int[]{7,7,1,6,7,8,2,7,3,7,7,7,9};
int result = instance.getMuchNum(array);
System.out.println("result = " + result);
}
}
class Solution {
public int partition(int[] arr, int start, int end) {
int num = arr[start];
int i=start;
int j=end+1;
while(true) {
while(arr[++i]<num) {
if(i==end)break;
}
while(arr[--j]>num) {
if(j==start)break;
}
if(i>=j)break;
swap(arr, i,j);
}
swap(arr, start, j);
return j;
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public int getMuchNum(int[] arr) {
int mid = (0+arr.length-1)/2;
int privt = partition(arr, 0, arr.length-1);
while(mid-1>privt || mid+1<privt) {
printArray(arr);
System.out.println("\tprivt = " + privt +", mid = " + mid);
if(mid-1>privt) {
privt = partition(arr, privt+1, arr.length-1);
}else if(mid+1<privt){
privt = partition(arr, 0, privt-1);
}
}
return arr[privt];
}
public void printArray(int[] arr) {
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]);
System.out.print(" ");
}
}
}
上面这种思路是从快速排序的思路中得出的,既然超过数组中存在超过一半的元素,那么在有序的情况下取中间元素一定是这个元素,但是如果全部使用快排,那么一种会超时,时间复杂度是O(NlogN),复杂的情况下甚至能到O(N^2)。部分快排也是不行的,因为最好的情况下是第一次partition的时候就找到了正确的位置,此时也经历了O(N)了,但是正常情况下不会一次就成功的,那么多次的情况下,实际上最终的复杂度是O(mN),m>1
下面提供一种新的思路:已知数组中存在超过一半的元素,那么我们使用两个变量,一个变量Num标记当前元素,另外一个变量count标记当前元素出现的次数。遍历数组,如果后一个元素不同,那么count--,当count==0,Num赋值为当前元素。这样可以保证在遍历完成之后,Num肯定是我们要找的元素。
class Solution {
public int getMuchNum(int[] arr) {
int Num = arr[0];
int count = 0;
for(int i=0;i<arr.length;i++) {
if(count == 0) {
Num = arr[i];
count++;
}else if(arr[i] != Num) {
count--;
}else {
count++;
}
}
return Num;
}
}
网友评论