leetcode刷题笔记——15. 三数之和

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

前置题目及其题解——1.两数之和

前置题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

前置题目分析:数组nums种不同位置的两个数之和等于target,两数可以相等。

前置题目题解1:len为数组长度,从0到len-2(倒数第二个元素)位置,依次作为第一个加数,选取后续元素为第二个加数,和为target则返回

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int len = nums.length;
        for(int i=0;i<len-1;i++){
            for(int j=i+1;j<len;j++){
                if(target == nums[i] + nums[j]){
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

前置题目题解2:

遍历整个数组,对于每次取到的数nums[i],如果HashMap中包含target-nums[i],返回i与target-nums[i]对应的下标(因此下标需要保存,这里我们可以放到map的value中)。不包含则将nums[i]放入map中。

考虑到map的key不能重复,否则会覆盖掉原本的value,而当出现相等两数和为target时,第二个数还未放入map就返回了,因此不冲突。

此外,HashMap的内部数据结构为Hash+链表/红黑树,在常数时间可以找到bucket,然后很短时间内找到对应的Entry。很短时间为>8次比较(链表长度大于8裂变)或log2(N)次比较,N为红黑树节点数。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int len = nums.length;
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<len;i++){
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]), i};
            } else {
                map.put(nums[i], i);
            }
        }
        return new int[0];
    }
}

题解——15. 三数之和

题目分析:

存在特殊情况,nums长度小于3返回为空;长度等于3且三数之和为0则返回这3个数,否则也返回空。

“不重复的三元组”的含义:对于三元组(a,b,c),不能再有元素种类和对应个数相同但顺序不同的三元组如(b,c,a)或(c,b,a)等。

如何保证不重复:

1.先使得数组整体有序,保证从前到后依次不放回取出的3个元素的大小顺序关系是唯一的。

2.对于三元组的每一位,不取和上一次相等的数据,不论=0成立与否。

对于三重循环的优化:在二重循环里边,直接将第三位的元素从最后一个开始取,当第三位元素的位置>第二位元素的位置时,如果三个数的和大于0,则第三位元素的位置持续左移。左移最终会出现三种情况:

①第三位元素和第二位元素重合。此时无论大于、小于还是等于0,均是使用了重复元素,说明在前两位确定的情况下,第三个数只能使三数之和大于0。

②第三位元素仍然大于第二位元素的位置,但和小于0了。说明前两位确定的情况下,第三个数只能使三数之和大于或小于0,再减小也只会使得和更小,左移第三位元素无济于事。

③第三位元素仍然大于第二位元素的位置,且和等于0。此时就可以将这三个元素保存并使第二位元素继续循环了。继续左移第三位元素,要么仍为0,元素重复,要么小于0,均没有意义。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int len = nums.length;
        if(len < 3){
            return res; 
        }
        int sum = 0;
        if(len == 3){
            for (int i=0;i<len;i++){
                sum += nums[i];
            }
            if (sum == 0){
                List<Integer> list = new ArrayList<Integer>();
                list.add(nums[0]);
                list.add(nums[1]);
                list.add(nums[2]);
                res.add(list);
            }
            return res;
        }
        Arrays.sort(nums);
        for(int first=0;first<len-2;first++){
            if(first>0&& nums[first]==nums[first-1]){
                continue;
            }
            int third = len-1;
            for(int second = first+1;second<len-1;second++){
                if(second > first+1 && nums[second]==nums[second-1]){
                    continue;
                }
                while (third > second && nums[first] + nums[second] + nums[third] > 0){
                    third--;
                }
                if(third == second){
                    break;
                }
                if(nums[first] + nums[second] + nums[third] == 0){
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    res.add(list);
                }
            }
        }
        return res;
    }
}

引申题目之一——16. 最接近的三数之和

引申题目之一链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

引申题目之一分析:本题目不关心数组是否重复,而是关心三数之和与target的接近程度,说明重复的数组对结果无影响,为了简洁,不进行无意义的计算,仍然可以延续15题不重复的操作理念。知识在目标上变为接近target,那么有几点是不同的:

①当发生second=third的情况时,third+1必然不越界并且必然是>target的,此时的三数之和与target有比较的价值,看接近程度与之前保存的接近程度哪个更接近。

②存在和=target时,是最接近的,直接返回target即可,他也就是三数之和;

③当和<target时,也有比较的价值。

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        int distance = Integer.MAX_VALUE, newDistance = Integer.MAX_VALUE;
        int res = 0;
        int len = nums.length;
        if(len == 3){
            return nums[0] + nums[1] + nums[2];
        }
        Arrays.sort(nums);
        for(int first=0;first<len-2;first++){
            if(first>0&& nums[first]==nums[first-1]){
                continue;
            }
            int third = len-1;
            for(int second = first+1;second<len-1;second++){
                if(second > first+1 && nums[second]==nums[second-1]){
                    continue;
                }
                while (third > second && nums[first] + nums[second] + nums[third] > target){
                    third--;
                }
                if(third == second){
                    newDistance = Math.abs(nums[first] + nums[second] + nums[third+1] - target);
                    // 无效代码
                    // if (newDistance == 0){
                    //     return target;
                    // }
                    if(newDistance < distance){
                        distance = newDistance;
                        res = nums[first] + nums[second] + nums[third+1];
                    }
                    break;
                }
                if(nums[first] + nums[second] + nums[third] == target){
                    return target;
                }
                // 小于
                newDistance = Math.abs(nums[first] + nums[second] + nums[third] - target);
                if(newDistance < distance){
                    distance = newDistance;
                    res = nums[first] + nums[second] + nums[third];
                }
            }
        }
        return res;
    }
}

引申题目之二——18. 四数之和

引申题目之二链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

引申题目之二分析:

1.到了四数之和,理论上需要四重循环才能完成。

2.考虑到时间复杂度,可以在双重循环确定第一元素nums[first]和第二位元素nums[second]后,第三位初始值为nums[second+1],第四位初始值元素为nums[len-1],当和大于target时,第四位元素左移,和小于target时,第三位元素右移,当等于target时,记录数据,第三和第四位分别右移、左移至与当前数不同的位置,时刻注意first<second<third<fourth<=len-1。

3.在第一位元素确定后,与紧邻的后三位元素之和大于target,说明此情况下无解,直接跳出;与最后三位之和小于target时,说明第一位元素太小,需要右移。第二位元素同理,可以通过提前的边界条件判定减少后续处理。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        int len = nums.length;
        if(len < 4){
            return res; 
        }
        int onlyFourSum = 0;
        if(len == 4){
            for (int i=0;i<len;i++){
                onlyFourSum += nums[i];
            }
            if (onlyFourSum == target){
                List<Integer> list = new ArrayList<Integer>();
                list.add(nums[0]);
                list.add(nums[1]);
                list.add(nums[2]);
                list.add(nums[3]);
                res.add(list);
            }
            return res;
        }
        Arrays.sort(nums);
        for(int first=0;first<len-3;first++){
            if(first>0&& nums[first]==nums[first-1]){
                continue;
            }
            if(nums[first] + nums[first+1] + nums[first+2] + nums[first+3] > target){
                break;
            }
            if(nums[first] + nums[len-1] + nums[len-2] + nums[len-3] < target){
                continue;
            }
            for(int second = first+1;second<len-2;second++){
                if(second > first+1 && nums[second]==nums[second-1]){
                    continue;
                }
                if(nums[first] + nums[second+1] + nums[second+2] + nums[second] > target){
                    break;
                }
                if(nums[first] + nums[len-1] + nums[len-2] + nums[second] < target){
                    continue;
                }
                int third = second+1, fourth = len-1;
                while(third < fourth){
                    int sum = nums[first] + nums[second] + nums[third] + nums[fourth];
                    if(sum > target){
                        fourth--;
                    } else if (sum <target){
                        third++;
                    } else {
                        // 优化①相关代码
                        // List<Integer> list = new ArrayList<Integer>();
                        // list.add(nums[first]);
                        // list.add(nums[second]);
                        // list.add(nums[third]);
                        // list.add(nums[fourth]);
                        res.add(Arrays.asList(nums[first], nums[second], nums[third], nums[fourth]));
                        while(third < fourth && nums[third] == nums[third+1]){
                            third++;
                        }
                        third++;
                        while(third < fourth && nums[fourth] == nums[fourth-1]){
                            fourth--;
                        }
                        fourth--;
                    }
                }
            }
        }
        return res;

    }
}

引申题目之二优化:

①new ArrayList<Integer>();会占用较多空间,因此使用Arrays.asList。

②nums=[0,0,0,1000000000,1000000000,1000000000,1000000000],target=1000000000.输出

[]而预期是[[0,0,0,1000000000]]。int类型范围是-2,147,483,648~2,147,483,647。巧妙地调整计算顺序可以使数据始终保持在int的范围内,但时间紧急的话还是建议直接使用(long)向上转型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值