15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路
1.使用双指针思想解决范围内求值问题。先将数组排序,然后遍历数组,先选出最左边元素为准,从右边数组的两端取元素与最左边元素相加之和是否为0,如果大于0 则整体过大,将右端元素往左移一位,如果小于0 则整体过小,将左端元素往右移一位。其中元素需要双重去重。在最外层,选取最左边元素时和在两端指针缩进时,重复元素都需要去掉。
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
//遍历数组,从小到大取元素,然后从右边两个指针内取值判断
for (int i = 0; i < nums.length-2; i++) {
//对最左边的数进行去重,相同的数只参与一次完整的遍历。
if(i>0&&nums[i]==nums[i-1]){
continue;
}
//获取当前最小值,如果最小值比目标值大,说明没有合适的数组
int min=nums[i]+nums[i+1]+nums[i+2];
if(min>0){
break;
}
//获取当前最大值,如果最大值比目标值小,说明后面越来越小,需要改变i的值。
int max=nums[i]+nums[nums.length-1]+nums[nums.length-2];
if(max<0){
continue;
}
//选取前后两个指针
int l = i + 1, r = nums.length - 1;
while (l < r) {
//如果三个数相加为0,则需要对指针的边界去重
if (nums[l] + nums[r] + nums[i] == 0) {
//如果左边指针数值重复,则将指针移到重复中最右边的一个
while (l + 1 < nums.length && nums[l] == nums[l + 1]) {
l++;
}
//如果右边指针数值重复,则将指针移到重复中最左边的一个
while (r - 1 > 0 && nums[r] == nums[r - 1]) {
r--;
}
List<Integer> integers = new ArrayList<>();
integers.add(nums[i]);
integers.add(nums[l]);
integers.add(nums[r]);
list.add(integers);
//缩小指针范围,对现有的i再从新的指针范围中取值判断
l++;
r--;
} else if (nums[l] + nums[r] + nums[i] < 0) {
//将左边指针往右移
l++;
} else {
//将右边指针往左移
r--;
}
}
}
return list;
}
扩展
四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
1.思路和三数之和类似,在三数之后外面加一层嵌套循环,然后判断内存循环的数为外层循环的数加1,双指针的规则不变。
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
//遍历数组,从小到大取元素,然后从右边两个指针内取值判断
for (int j = 0; j < nums.length-3; j++) {
//对最左边的数进行去重,相同的数只参与一次完整的遍历。
if(j>0&&nums[j]==nums[j-1]){
continue;
}
//获取当前最小值,如果最小值比目标值大,说明没有合适的数组
int min1=nums[j]+nums[j+1]+nums[j+2]+nums[j+3];
if(min1>target){
break;
}
//获取当前最大值,如果最大值比目标值小,说明后面越来越小,需要改变j的值。
int max1=nums[j]+nums[nums.length-1]+nums[nums.length-2]+nums[nums.length-3];
if(max1<target){
continue;
}
for (int i = j+1; i < nums.length-2; i++) {
//对最左边的数进行去重,相同的数只参与一次完整的遍历。
if(i>j+1&&nums[i]==nums[i-1]){
continue;
}
//获取当前最小值,如果最小值比目标值大,说明没有合适的数组
int min2=nums[j]+nums[i]+nums[i+1]+nums[i+2];
if(min2>target){
break;
}
//获取当前最大值,如果最大值比目标值小,说明后面越来越小,需要改变i的值。
int max2=nums[j]+nums[i]+nums[nums.length-1]+nums[nums.length-2];
if(max2<target){
continue;
}
//选取前后两个指针
int l = i + 1, r = nums.length - 1;
while (l < r) {
//如果四个数相加为目标数,则需要对指针的边界去重
if (nums[l] + nums[r] + nums[i]+nums[j] == target) {
//如果左边指针数值重复,则将指针移到重复中最右边的一个
while (l + 1 < nums.length && nums[l] == nums[l + 1]) {
l++;
}
//如果右边指针数值重复,则将指针移到重复中最左边的一个
while (r - 1 > 0 && nums[r] == nums[r - 1]) {
r--;
}
List<Integer> integers = new ArrayList<>();
integers.add(nums[j]);
integers.add(nums[i]);
integers.add(nums[l]);
integers.add(nums[r]);
list.add(integers);
//缩小指针范围,对现有的i再从新的指针范围中取值判断
l++;
r--;
} else if (nums[l] + nums[r] + nums[i]+nums[j] < target) {
//将左边指针往右移
l++;
} else {
//将右边指针往左移
r--;
}
}
}
}
return list;
}
两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
1.遍历数组并将值和索引加入hashmap中并判断,如果hashmap中有和为target的数,则返回该数与hashmap中值的索引。
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> hashMap = new HashMap<>();
int[] ints = new int[2];
int index=0;
for (int i=0;i<nums.length;i++){
//map中是否包含与之和为target的,不包含就将值和索引存入map
if (!hashMap.containsKey(target-nums[i])) {
hashMap.put(nums[i], i);
}else {
//包含就返回这两个数的下标
ints[index] = hashMap.get(target - nums[i]);
ints[++index] = i;
break;
}
}
return ints;
}