总结
解n数之和的核心思想:在数组有序的情况下使用双指针(无序则手动排序),将n数之和转化为求n-1个数之和。
当有重复元素时,在循环过程中遵循以下两点:
- 每一种循环枚举到的下标
必须大于上一重循环枚举到的下标; - 同一重循环中,如果当前元素与上一个元素相同,则跳过当前元素
经典n数之和
towSum
两数之和 I:数组有序
左右指针
两数之和II:数组无序
1、前缀和
2、排序双指针
BST中求两数之和
解析:
虽然中序遍历有序,但是无法像数组那样使用双指针。
怎么办?——前缀和
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
Set<Integer>set=new HashSet<>();
public boolean findTarget(TreeNode root, int k) {
if(root==null){
return false;
}
if(set.contains(k-root.val)){
return true;
}else{
set.add(root.val);
}
return findTarget(root.left,k) || findTarget(root.right,k);
}
}
三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>>ans;
for(int i=0;i<nums.size()-2;i++){
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int left=i+1,right=nums.size()-1;
while(left<right){
int sum=nums[i]+nums[left]+nums[right];
if(sum==0){
ans.push_back({nums[i],nums[left],nums[right]});
while(left<right && nums[left]==nums[left+1]) left++;
left++;
while(left<right && nums[right]==nums[right-1]) right--;
right--;
}else if(sum<0){
left++;
}else{
right--;
}
}
}
return ans;
}
};
四数之和
四数之和
由于数字存在重复,所以对重复结果的处理很关键,也很易错。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
if (nums == null || nums.length < 4) {
return ans;
}
Arrays.sort(nums); //关键
int length = nums.length;
for (int i = 0; i < length - 3; i++) {
//关键点:跳过相同的数字
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// //数组是升序的,之后不可能有和为target的了
// if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
// return ans;
// }
//
// if (nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
// continue;
// }
for (int j = i + 1; j < length - 2; j++) {
//关键点:跳过相同的数字
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
//
// if (nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
// continue;
// }
int left = j + 1, right = length - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
ans.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
//存在重复,需要跳过相同的数字
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) { //不等于时不跳过相同数字
left++;
} else {
right--;
}
}
}
}
return ans;
}
}
相同的需要跳过
//关键点:跳过相同的数字
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
不然出现下面的情况

sum!= target时,如果跳过相同数字
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
就会出现下面的情况:

nSum
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
/**
*
* @param nums 数组
* @param n n个数的和
* @param target 目标和
* @return
*/
public List<List<Integer>> nSum(int[] nums, int n,int target) {
Arrays.sort(nums);
return getNSum(nums, n, 0, target);
}
private List<List<Integer>> getNSum(int[] nums, int n, int st, int target) {
int len=nums.length;
List<List<Integer>>curList=new ArrayList<>();
if (n==2){ //twoSum 双指针
int left =st, right = len - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
curList.add(Arrays.asList(nums[left], nums[right]));
//存在重复,需要跳过相同的数字
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) { //不等于时不跳过相同数字
left++;
} else {
right--;
}
}
}else if (n>2){ // n > 2 时,递归计算 (n-1)Sum 的结果
for(int i=st;i<len-n+1;i++){
if (i>st && nums[i-1]==nums[i]){
continue;
}
List<List<Integer>>subList=getNSum(nums,n-1,i+1,target-nums[i]);
for (List<Integer> list : subList) {
List<Integer>t=new ArrayList<>();
t.add(nums[i]);
t.addAll(list);
curList.add(t);
}
}
}
return curList;
}
}
变形
小于 K 的两数之和
class Solution {
public int twoSumLessThanK(int[] nums, int k) {
int n=nums.length;
//这些特殊情况能考虑到的都写上
if(n<2){
return -1;
}
Arrays.sort(nums);
if(nums[0]+nums[1]>k){
return -1;
}
int i=0,j=nums.length-1;
int max=-1;
while (i<j){
int sum=nums[i]+nums[j];
if(sum<k){
max=Math.max(max,sum);
i++;
}else {
j--;
}
}
return max;
}
}
最接近的三数之和
添加链接描述
一样的配方
import java.util.Arrays;
class Solution {
public int threeSumClosest(int[] nums, int target) {
int n = nums.length;
if (n < 3) {
return 0;
}
Arrays.sort(nums);
int ans = 0;
int min = Integer.MAX_VALUE;
for (int i = 0; i < n - 2; i++) {
if(i>0 && nums[i-1]==nums[i]){
continue;
}
int left = i + 1, right = n - 1;
while (left < right) {
int temp = nums[i] + nums[left] + nums[right];
if (temp == target) {
return target;
}else if (temp < target) {
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
} else if (temp > target){
while (left < right && nums[right - 1] == nums[right]) {
right--;
}
right--;
}
if (Math.abs(temp - target) < min) {
min = Math.abs(temp - target);
ans = temp;
}
}
}
return ans;
}
}
有效三角形的个数
添加链接描述
法一:双指针
有效三角形:两边之和大于第三边
等效于三数之和
时间复杂度O(n^2),lc却超时
import java.util.Arrays;
class Solution {
int ans=0;
public int triangleNumber(int[] nums) {
int n=nums.length;
if(n<3){
return 0;
}
Arrays.sort(nums);
for (int i = 0; i < n-2; i++) {
if(nums[i]==0){
continue;
}
int left=i+1,right=n-1;
while (left<right){
int sum=nums[i]+nums[left];
if (sum>nums[right]){
ans+=right-left;
}else {
right--;
}
}
}
return ans;
}
}
法二:二分
朴素思想就是先对数组排序,然后从前往后找两个数,再找第三个数,如果前两个数的和>第三个数,则可以组成三角形。
遍历所有情况统计即可,但是三层循环O(n^3),超时。
优化:
第三层循环可以优化成二分查找
import java.util.Arrays;
class Solution {
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int n=nums.length;
int ans=0;
for (int i=0;i<n-2;i++){
for (int j=i+1;j<n-1;j++){
int sum=nums[i]+nums[j];
//找到[j+1,n-1]范围内,小于sum的所有数中的最大数k,那么[j+1,k]就都符合要求
int left=j+1,right=n-1;
while (left<right){
int mid=(right-left+1)/2+left;
if(sum>nums[mid]){
left=mid;
}else {
right=mid-1;
}
}
if(nums[left]<sum) {
//nums[left]>=sum则说明一个符合的都没有
ans += left - j;
}
}
}
return ans;
}
}
四数之和2
四数之和2:变形
和前面的解法不再一样,本题采用两两分组求和的思想
class Solution {
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
Map<Integer,Integer> map=new HashMap<>();
for (int a : A) {
for (int b : B) {
map.put(a+b,map.getOrDefault(a+b,0)+1); //记录和为a+b的次数
}
}
int ans=0;
for (int c : C) {
for (int d : D) {
if(map.containsKey(0-c-d)){
ans+=map.get(0-c-d);
}
}
}
return ans;
}
}
时间 n^2
空间 n^2
473

被折叠的 条评论
为什么被折叠?



