k sum

例题1. 1 两数之和 Easy
例题2. 167 两数之和,输入的是有序数组 Easy
例题3. 15 三数之和 Medium
例题4. 16 最接近的三数之和 Medium
例题5. 18 四数之和 Medium

例题1. 1 两数之和 Easy

例题2. 167 两数之和,输入的是有序数组 Easy

例题3. 15 三数之和 Medium

【题目】

在这里插入图片描述
在这里插入图片描述

【方法1:三重循环】

时间复杂度o(n^3)。略。

【方法2:排序+双指针】

首先对数组进行排序。对于每一个nums[i],使用双指针找到两个数,和为-nums[i]。
时间复杂度:排序为o(nlogn);一共n个数,对每个数查找为o(n),n个数查找时间为o(n2)。总时间复杂度为o(n2)。

代码如下:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new LinkedList<List<Integer>>();
        if(len <=2) return res;     //返回空列表,不要返回null.

        Arrays.sort(nums);
        int i,j;
        for(int cur=0;cur<len;cur++){
            if(nums[cur]>0) break;        //因为已经排好序,当nums[cur]>0时,后面的肯定都>0,肯定没有解了。
            if(cur>0 && nums[cur] == nums[cur-1]) continue;  //为了防止重复。详见解释1。

            i=cur+1;j=len-1;      //i要从cur+1开始。如果i从cur的前面开始的话(比如位置a),那就会与cur=a时一个结果重复。详见解释2。
            while(i<j){
                if(nums[i]+nums[j] == -nums[cur]){
                    //得到了一个结果。
                    List li = new LinkedList<>();
                    li.add(nums[cur]); li.add(nums[i]); li.add(nums[j]); res.add(li);
					
					//跳过重复的nums[i]或nums[j],防止重复。详见解释3。
                    while(i<j && nums[i+1] == nums[i]){
                        i++;  
                    }
                    i++;
                    /*这段代码有没有都行。因为光走i就可以打破平衡了。
                    while(i<j && nums[j-1]== nums[j]){
                        j--;
                    }
                    j--;
                    */
                }else if(nums[i]+nums[j]<-nums[cur]){
                    i++;
                }else{
                    j--;
                }
            }
        }

        return res;
    }
}

解释1:
比如:
在这里插入图片描述
当cur为1时,nums[cur]=-1,找到了一组答案为[-1, 0, 1]。
此时cur++,变为2,nums[cur]还是-1。这样就会得到重复答案[-1, 0, 1]

解释2:
比如
在这里插入图片描述
假设a+b+c = 0。当前的cur指向b。如果i是从头开始找的话,会得到结果[a, b, c]。
因为cur是从0遍历过来的,所以在cur指向b之前,已经指向过a:
在这里插入图片描述
当时呢,肯定就会有答案[a, b ,c]。
这样就导致重复。

解释3:
比如
在这里插入图片描述
此时,nums[cur]为-2。nums[i]=0,nums[j]=2。得到一个结果[-2, 0 , 2]
当i++的时候,nums[i]还是0,还是会得到重复结果[-2, 0, 2]。
所以做法就是让i把后面的0跳过去。i跳过0值,j是不是跳过2已经无所谓了。

运行结果:
在这里插入图片描述

例题4. 16 最接近的三数之和 Medium

【题目】

在这里插入图片描述
在这里插入图片描述

【分析】

与三数之和一样。首先排序,然后双指针。
找的过程中维护一个全局的变量:当前与target距离最近的三数和。
时间复杂度o(n2)

【代码】

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        int len = nums.length;

        Arrays.sort(nums);

        int i,j;
        int res=nums[0]+nums[1]+nums[2];    //保存到目前为止距离target最近的三数之和
        int tmperr = Math.abs(target - res);  //保存到目前为止的最小误差

        for(int cur=0; cur<len; cur++){
            i=cur+1;j=len-1;
            while(i<j){
                int sum = nums[cur]+nums[i]+nums[j];
                if(sum == target){
                    return target;
                }else if(sum < target){
                    i++;
                }else{
                    j--;
                }
                if(Math.abs(target-sum) < tmperr){
                    res = sum;
                    tmperr = Math.abs(res - target);
                }
            }
        }
        return res;
    }
}

结果:
在这里插入图片描述

例题5. 18 四数之和 Medium

【题目】

在这里插入图片描述
在这里插入图片描述

【分析】

转化成3 sum问题。
首先排序,然后通过一层循环固定一个数cur。然后解决 a+b+c = target-cur的3 sum问题。
解决3sum问题,就是一层循环固定一个数,然后通过双指针找。

【代码】

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        int len = nums.length;
        List<List<Integer>> res = new LinkedList<List<Integer>>();
        if(len <=3) return res;

        Arrays.sort(nums);
        if(nums[len-1]+nums[len-2]+nums[len-3]+nums[len-4]<target) return res; //最大的四个数都不够大,肯定就没有了。

        int i,j,k;
        for(int cur = 0; cur<len-3 ;cur++){
            if(cur>0 && nums[cur]==nums[cur-1]) continue;
            if(nums[cur]+nums[cur+1]+nums[cur+2]+nums[cur+3] > target) break; //最小的四个数都不够小,肯定没有了。

            for(i=cur+1;i<len-2;i++){
                if(i>cur+1 && nums[i] == nums[i-1]) continue;
                if(nums[i]+nums[i+1]+nums[i+2]> target-nums[cur]) break; //最大的三个数不够大,就没有了。
                if(nums[len-1]+nums[len-2]+nums[len-3]< target-nums[cur]) break; //最小的三个数不够小,就没有了。
                
                j = i+1;
                k = len-1;
                while(j<k){
                    if(nums[j]+nums[k] == target-nums[cur]-nums[i]){
                        List<Integer> li = new LinkedList<Integer>();
                        li.add(nums[cur]);li.add(nums[i]);li.add(nums[j]);li.add(nums[k]);res.add(li);

                        while(j<k && nums[j+1]==nums[j]){
                            j++;
                        }
                        j++;
                    }else if(nums[j]+nums[k]< target -nums[cur] - nums[i]){
                        j++;
                    }else{
                        k--;
                    }
                }
            }
        }
        return res;
    }
}

代码跟3sum问题差不多,细节可以参考哪里的解释。

结果:
在这里插入图片描述

发布了6 篇原创文章 · 获赞 0 · 访问量 181

猜你喜欢

转载自blog.csdn.net/siyuchuanmei2020/article/details/104183746