又是一年秋招季,不复习算法题可不行,力扣复习第一篇文章献给两数之和
两数之和
两数之和,数量比较少,我们知道一个sum,然后我们枚举一个数A,另一个数自然就是sum-B,因此无序解法占用的时间和空间都不算多,如果上升到三个数、四个数,无序数组就不那么好做了,还是使用有序数组解法更加通用
题目:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标
无序数组
暴力双循环不要想了,我们遍历一次数组,能够都所有数字进行一次枚举,每个枚举的值记为A,而目标值为target,那么我们需要找的哪一个值就是target-A。
我们需要使用一个缓存,存储已经枚举过的数,每当我们枚举到一个数需要做出两个处理:
【1】判断这个数对应的target-A是否已经存在,如果存在说明已经找到了,退出
【2】否则,将这数暂存起来
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> temp = new HashMap<>();
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
int num=target-nums[i];
if(temp.containsKey(num)){
res[0]=temp.get(num);
res[1]=i;
break;
}else {
//值做键,索引做值
temp.put(nums[i],i);
}
}
return res;
}
我们使用map将过去枚举过的数的值与下标存储起来
有序数组
因为数组是有序的,那么元素从左到右,必然是依次变大的,我们维护一对指针,一个指向左侧,一个指向右侧。每轮迭代,我们就将这两个指针划分出的区域缩小一个单位,因此在时间复杂度O(n)下,我们就可以检索出这两个数。
public int[] twoSum(int[] numbers, int target) {
int a=0;
int b=numbers.length-1;
while (a<b){
if(numbers[a]+numbers[b]==target)break;
else if(numbers[a]+numbers[b]<target){
a++;
}else b--;
}
return new int[]{a+1,b+1};
}
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
我们依然按照有序数组两数之和的思路,我们能不能三数之和,通过限定其中一个数来降维作两数之和呢?
我们在外循环维护一个数,然后内循环就可以看作两数之和的解法了
for(int i=0;i<nums.length-2;i++){
int first = nums[i];
int j=i+1;
int k=nums.length-1;
每轮迭代,固定一个first,然后选定一对指针j和k,那么目标值就是-first。
由于题中要求不能出现重复的三元组,因此我们还需要进行去重操作——保证i总是指向相对靠右的那个“重复值”,而j总是指向相对靠左的那个“重复值”
外循环同样需要去重,保证i总是指向靠右的“重复值”
三数之和,最外层循环每进行一轮迭代,都将产生一个三元组。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums.length<3)return res;
Arrays.sort(nums);
for(int i=0;i<nums.length-2;i++){
int first = nums[i];
int j=i+1;
int k=nums.length-1;
while(j<k){
if(nums[j]+nums[k]==-first){
ArrayList<Integer> list = new ArrayList<>();
list.add(first);list.add(nums[j]);list.add(nums[k]);
res.add(list);
while(j+1<k&&nums[j]==nums[j+1])j++;
while(j<k-1&&nums[k-1]==nums[k])k--;
j++;k--; //并没有结束,还需要接着找
}else if(nums[j]+nums[k]>-first){
k--;
}else{
j++;
}
}
//结算后去重
while (i + 1 < num.length - 2 && num[i+1] == num[i]) ++i;
}
return res;
}
注意,外循环不要一开始就i++去重,因为内循环需要会使用到对应的位置,当内循环计算完毕后,保证内循环已经使用过才可以去重。如-2 -2 2 4 0可以由-2 -2 4 和 -2 2 0
四数之和
其实是一个套路,只要都是从2、3之和变式过来的,N数之和也能这么做
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res =new ArrayList<>();
if(nums.length<4)return res;
Arrays.sort(nums);
for (int left_1 = 0; left_1 < nums.length-3; left_1++) {
for (int left_2 = left_1+1; left_2 < nums.length-2; left_2++) {
int temp=target-nums[left_1]-nums[left_2];
int left_3=left_2+1;
int right_3=nums.length-1;
while (left_3<right_3){
if(nums[left_3]+nums[right_3]>temp){
right_3--;
}else if(nums[left_3]+nums[right_3]<temp){
left_3++;
}else {
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[left_1]);
list.add(nums[left_2]);
list.add(nums[left_3]);
list.add(nums[right_3]);
res.add(list);
while (left_3+1<right_3&&nums[left_3+1]==nums[left_3])left_3++;
while (right_3-1>left_3&&nums[right_3-1]==nums[right_3])right_3--;
right_3--;
left_3++;
}
}
while (left_2+1<nums.length-2&&nums[left_2+1]==nums[left_2])left_2++;
}
while (left_1+1<nums.length-3&&nums[left_1+1]==nums[left_1])left_1++;
}
return res;
}
其实也可以使用另一种去重方法:容器API,但是时间复杂度开始while比较快,毕竟使用contains需要从头比较一遍
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[left_1]);
list.add(nums[left_2]);
list.add(nums[left_3]);
list.add(nums[right_3]);
if(!res.contains(list))res.add(list);
right_3--;
left_3++;