三数之和
方法:排序+双指针
基本思想:将三个数下标分别设置为i,L,R,在循环过程中要保证三个数的相对顺序,即i < L < R。
i:遍历数组的下标
L:初始值为i+1
R:初始值为数组大小-1
循环中每次第一个数字i都是确定的,L为最靠近i的左指针,R为数组最右侧的右指针,当L<R时进行内部循环,如果三数之和>0,R指针向左移(R值减小),如果三数之和<0,L指针向后移(L值增大),如果三数之和=0,将其存入结果中。
难点:判断重复
对于i:每次外层循环的开始判断此时的i和上一次循环的i是否相等,如果相等直接跳过此轮循环。(因为上一趟循环已经将i值确定的所有组合都遍历过了)
对于L,R:当内层循环找到三数之和=0的时候,判断L右侧的值是否等于L值,如果相等就将L向右移动,R情况类似。(因为此时i已经确定,所以L和R任何一方都不能出现重复数字)
//leetcode-15
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new ArrayList<>();
Arrays.sort(nums);
//i,L,R分别为三个数字
for(int i = 0; i < nums.length; i++){
if(nums[i] > 0) return lists;
if(i > 0 && nums[i] == nums[i - 1]) continue;
int L = i + 1;
int R = nums.length - 1;
while(L < R){
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[L]);
list.add(nums[R]);
lists.add(list);
while(L < R && nums[L + 1] == nums[L]){
L++;
}
while(L < R && nums[R - 1] == nums[R]){
R--;
}
L++;
R--;
}
else if(sum > 0){
R--;
}
else{
L++;
}
}
}
return lists;
}
}
四数之和
和三数之和类似,三数之和循环中确定的是第一个数的值,四数之和循环中确定的是前两个数的和值,内层循环同样通过L,R双指针降低时间复杂度。判断重复时注意同一重循环中,如果当前元素与上一个元素相同,则跳过当前元素。
//leetcode-18
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> lists = new ArrayList<>();
Arrays.sort(nums);
//i,j,left,right为四个不重复的数
for(int i = 0; i < nums.length; i++){
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
for(int j = i + 1; j < nums.length; j++){
if(j > i + 1 && nums[j] == nums[j - 1]){
continue;
}
int left = j + 1;
int right = nums.length - 1;
while(left < right){
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum == target){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[left]);
list.add(nums[right]);
lists.add(list);
while(left < right && nums[right - 1] == nums[right]){
right--;
}
while(left < right && nums[left + 1] == nums[left]){
left++;
}
right--;
left++;
}
else if(sum > target){
right--;
}
else{
left++;
}
}
}
}
return lists;
}
}
未完待续。。。