资讯专栏INFORMATION COLUMN

Two Sum系列 Leetcode解题记录

andong777 / 3315人阅读

摘要:如果没有,就到里面复杂度分析就是,因为只扫了一遍数组。复杂度分析当然就是最坏情况了,也是标准的双指针复杂度。复杂度分析这种题应该不太需要分析复杂度吧,能实现就行。复杂度分析还是最后再说两句所以可以看出,很多题目思路一致,换汤不换药。

Two Sum

友情提示:篇幅较长,找题目的话,右边有目录,幸好我会MarkDown语法。
改成了系列模式,因为类似的题不少,本质上都是换壳,所以在同一篇文章里面把这类问题汇总一下。先说2 Sum。这道题非常出名,因为这是leetcode的第一道题。很多人说一些公司招聘的时候,这道题专门出给他们想招进来的人,因为这不是放水,简直就是洪水。要做的就是已知一个数组,和一个target值。返回两个目标的index,这两个目标求和就是target值。

解决思路

这题不难,只需要熟悉hashtable即可。在hashtable里面,key是差,value是index。比如例子中的[2,7,11,15],target是9。那么在2的时候就存入7 0,下一位找到7的时候,之前有个差值是7,那么就返回7对应的index,0,以及当前这个7的index,就是1。

code
public class Solution {
    public int[] twoSum(int[] nums, int target) {
        //创建一下数组,要存两个index的数组。
        int[] result = new int[2];
        //这里用hashtable也行,看心情。
        Map map = new HashMap();
        //扫一遍数组,一边扫一边存
        for(int i = 0; i < nums.length; i++){
            int cur = nums[i];
            //这里搞出来个差值,其实差值是在没找到之后添加到map里面的。
            int toFind = target - cur;
            //如果发现之前需要这个差值,那就找index。
            if(map.containsKey(cur)){
                result[0] = map.get(cur);
                result[1] = i;
                return result;
            }
            //如果没有,就put到map里面
            else{
                map.put(toFind, i);
            }
        }
        return result;
    }
}
复杂度分析

就是O(n),因为只扫了一遍数组。

最后再说两句

虽然这题非常简单,但是14年秋天第一次看到这题的时候感觉还是难到爆炸,无从下手。因为一丝编程基础都没有,现在两年过去了,用脚趾都能敲出来。其实行业之间努力其次,路径非常重要,在这里感谢一下点拨我的老乡和兄弟们。而且重新写了几次,连变量命名都是一样的。

Two Sum II - Input array is sorted 解决思路

这题用sorted当做题目,好比路边的一些职业勾引男性行人,非常直接的就意味着二分搜索。一次查一半,所以刚开始只用到了二分搜索。但是有个问题,二分搜索的步子太大,可能把目标值跳过,那么还要借鉴双指针的全盘扫描的特点。

code
public class Two_Sum_II {
    public int[] twoSum(int[] numbers, int target) {
        int[] result = new int[2];
        //这里用二分搜索,我常用start和end来命名两头,middle是中间。
        int start = 0;
        int end = numbers.length-1;
        //这个while循环条件很巧妙,二分搜索建议固定一个模板,这个就挺好固定的。
        while (start + 1 < end) {
            //看,我刚说的是实话,而且这里middle的计算方法是防止越界。
            int middle = start + (end-start)/2;
            if (numbers[start] + numbers[end] < target) {
                //这里需要判断,到底是跳一半还是走一步,就再加个判断。
                if (numbers[middle] + numbers[end] < target) {
                    start = middle;
                }
                else {
                    start++;
                }
            }
            else if(numbers[start] + numbers[end] > target) {
                if (numbers[middle] + numbers[start] > target) {
                    end = middle;
                }
                else {
                    end--;
                }
            }
            else {
                break;
            }
        }
        //题目中保证了有结果,还不是zero-based,那么就把result两项都续一秒。
        result[0] = start+1;
        result[1] = end+1;
        return result;
    }
}
复杂度分析

当然就是最坏情况O(n)了,也是标准的双指针复杂度。不过二分搜索方法是它最优情况是O(nlgn)。

最后再说两句

不得不说,这个题目把二分搜索和双指针拼在一起非常有创意。只用二分搜索让我多交了一个submit,好题一个。

Two Sum III - Data structure design
Design and implement a TwoSum class. It should support the following operations: add and find.

add - Add the number to an internal data structure.
find - Find if there exists any pair of numbers which sum is equal to the value.

For example,
add(1); add(3); add(5);
find(4) -> true
find(7) -> false

锁住的题,罕见的一道design题目和Google没关系,tag只有Linkedin,怪不得被收购了。

解决思路

这是一道入门级别的design题目,当然第一次遇到design这个词我就像脑血栓般浑身发抖。不过好在它脱胎于Two Sum,本质上还是不难的,我们要做的就是套上design的外壳,解决掉它。值得注意的是,一个数字只能用1次,所以还是要记录一下数字出现的次数的。

code
public class TwoSumIII {
    //用一个List当容器装数字,用Map来记录数字出现的次数
    List list = new ArrayList<>();
    Map map = new HashMap();
    // Add the number to an internal data structure.
    public void add(int number) {
        list.add(number);
        //非常常规的往map里记录出现个数的写法
        if (map.containsKey(number)) {
            map.put(number, map.get(number)+1);
        }
        else {
            map.put(number,1);
        }
    }

    // Find if there exists any pair of numbers which sum is equal to the value.
    public boolean find(int value) {
        for (int i = 0; i < list.size(); i++) {
            int cur = list.get(i);
            int toFind = value - cur;
            //这里还是Two Sum的求法,取一个,找另一个。值得注意的是需要看求和的两个数是不是相等。
            if (cur != toFind) {
                //根据leetcode测试,在map里面找比在list找目标数字更快一些。
                if (map.containsKey(toFind)) {
                    return true;
                }
            }
            else {
                if (map.get(cur) > 1) {
                    return true;
                }
            }
        }
        return false;
    }
}
复杂度分析

这种题应该不太需要分析复杂度吧,能实现就行。每次都是遍历一遍List,所以就是O(n)。

最后再说两句

写的时候发现其实遍历一下Map也行,可以省去一个list。但我偏偏不省,因为List不要钱,能加就加,而且看着也方便,一个map用于不同的用途,可能会引起线程冲突。出来混,多一事不如少一事。

3 Sum

这题用脚后跟看都是2Sum的follow up。就是在一个数组里面挑3个数字,这三个数字的和为0就行。需要注意的是triplet这个单词的拼写和发音,还有不能有重复的triplet,不能重复这一点还是有点儿小麻烦的

解决思路

既然是follow up,解决思路也就是follow up。follow up是什么意思呢,我们翻译一下,follow就是跟随,up就是上面。就是跟随上面的意思,我们往上看,上面只有2Sum一题,那我们就跟随一下。A+B是2Sum,A+B+C是3Sum,那么稍加修改A+(B+C)就成了这两道题连接的桥梁。所以这题的基本思路就是套了个壳子而已。
值得一提的是,此题可能有重复数字,而且要求不能有重复结果,所以使用双指针法。前面这句的不是很理所当然,在这里就当经验记录一下了,强行解释就是指针可以跳过重复的数字,而且求和也很容易。

code
public class ThreeSum {
    public List> threeSum(int[] nums) {
        //首先把输出写出来
        List> result = new ArrayList<>();
        //双指针出马之前数组通常需要排序
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            int cur = nums[i];
            //这个本意是重复数字的话可以跳过。因为之前的数字已经打头过了,重复的就没必要打头了。
            if (i > 0 && cur == nums[i-1]) {
                continue;
            }
            //双指针出马,这里注意了我一般命名成left和right。
            int left = i+1;
            int right = nums.length-1;
            //这里注意了开始2Sum过程。
            while (left < right) {
                int two_sum = nums[left] + nums[right];
                if (two_sum < -1*cur) {
                    //说明加和小了,那把左指针往右移动
                    left++;
                }
                else if (two_sum > -1 * cur) {
                    //大了那就往左移动
                    right--;
                }
                else {
                    List list = new ArrayList<>();
                    list.add(cur);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    //把这次记录的结果加到result里面
                    result.add(list);
                    //下回测试corner case的时候就一群0,这次4个0就吃亏了,忘了这个while循环,所以要跳过重复数字。
                    while(left+1 < right && nums[left] == nums[left+1]){
                        left++;
                    }
                    while (right-1 > left && nums[right] == nums[right-1]) {
                        right--;
                    }
                    //跳过之后,继续挪动一下。
                    left++;
                    right--;
                }
            }
        }
        return result;
    }
}
复杂度分析

这个排序的复杂度是O(nlgn),循环的复杂度就是O(n^2),所以就是循环的复杂度n方。

最后再说两句

其实这种数组题,无论多么的熟练,都要在纸上先勾画一下思路,尤其是这道题里面重复数字的处理,其实也可以用个set来去重,但那样毕竟颜值不行,不符合我自拍的一贯水准。跳过相等数字,最后再挪动一下,code里面不好分析,在纸上画画一目了然。

3 Sum Smaller
Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target.

For example, given nums = [-2, 0, 1, 3], and target = 2.
Return 2. Because there are two triplets which sums are less than 2:
  [-2, 0, 1] [-2, 0, 3]

Follow up:
Could you solve it in O(n2) runtime?

这题居然是锁住的,company tag只有一个Google,所以把题目内容贴上来。

解决思路

这题说老实话让我很困惑,为啥这题都能当一道题出了。其实就是3Sum稍微变一下,然后返回个个数而不是一群triplet。而且要求是O(n2),那类比3Sum的双指针方法可以满足。

code

public class Three_Sum_Smaller {

public int threeSumSmaller(int[] nums, int target) {
    //双指针是一定要排序的,否则后面根据大小挪动指针就没有意义了。
    Arrays.sort(nums);
    int result = 0;
    for (int i = 0; i < nums.length-2; i++) {
        int left = i+1;
        int right = nums.length-1;
        int cur = nums[i];
        while (left < right) {
            int two_sum = nums[left] + nums[right];
            //这里需要注意如果满足条件,接下来不需要移动指针,直接把中间所有的可能性都加起来就可以
            if (cur + two_sum < target) {
                result += right - left;
                left++;
            }
            else {
                //只有和大了,才要让右边指针左移,让整体小一些。
                right--;
            }
        }
    }
    return result;
}

}

复杂度分析

直接O(n2)了,就是两重循环,多说一句,双指针就是把O(n2)降成O(n)的,外面再套一层循环,就是O(n2)了。

最后再说两句

这题会做了,Google是不是能过一轮了啊。就注意小于的情况直接求result就行。

3 Sum Closet

还是一个数组,还有个目标数。返回距离目标最近的一个和,这个和是3个数的和。

解决思路

和上面一样,双指针,看大小。

code
public class ThreeSumClosest {
    public int threeSumClosest(int[] nums, int target) {
        if(nums == null || nums.length < 3){
            return 0;
        }
        int res = 0;
        int diff = Integer.MAX_VALUE;
        Arrays.sort(nums);

        for(int cur = 0; cur < nums.length-2; cur++){
            int left = cur+1;
            int right = nums.length-1;
            int tempTar = target-nums[cur];
            while(left < right){
                int sum = nums[left] + nums[right];
                int value = Math.abs(sum-tempTar);
                //找到更小的就更新。
                if(value <= diff){
                    diff = value;
                    res = nums[cur] + nums[left] + nums[right];
                }
                if(sum < tempTar){
                    left++;
                    
                }
                else if(sum > tempTar){
                    right--;
                }
                else{
                    return target;
                }
            }
        }
        return res;
    }
}
复杂度分析

还是O(n2).

最后再说两句

所以可以看出,很多题目思路一致,换汤不换药。都是双指针扫数组,非常容易。

4 Sum

这次是4个,就是找四个数,它们的和是目标数。

解决思路

这次就是3 Sum套了个壳而已,方法都是一样的。

code
public class FourSum {
    public List> fourSum(int[] nums, int target) {
        List> res = new ArrayList<>();
        //象征性的说,如果没有4个数,那还玩个球啊
        if(nums.length < 4) return res;
        //双指针排序,都看腻了吧
        Arrays.sort(nums);
        for(int i = 0; i < nums.length-3; i++){
            //去掉重复的数字,就是打头不能相同
            if(i > 0 && nums[i] == nums[i-1]) continue;
            for(int j = i+1; j < nums.length-2; j++){
                //再去掉一遍
                if(j > i+1 && nums[j] == nums[j-1]) continue;
                //实力打脸,以后还是要left和right,low和high太low了。
                int low = j+1, high = nums.length-1;
                while(low < high){
                    int sum = nums[i] + nums[j] + nums[low] + nums[high];
                    if(sum == target){
                        //这里新建一个list也行。
                        res.add(Arrays.asList(nums[i],nums[j], nums[low], nums[high]));
                        while(low+1 < high && nums[low+1] == nums[low]){
                            low++;
                        }
                        while(high-1 > low && nums[high-1] == nums[high]){
                            high--;
                        }
                        low++;
                        high--;
                    }
                    else if(sum < target){
                        low++;
                    }
                    else{
                        high--;
                    }
                }
            }
        }
        return res;
    }
}
复杂度分析

O(n3),因为是三重循环。

最后再说两句

这个系列结束了,没想到从2 Sum可以延伸这么长。不过核心思想都差不多,一些细节处,比如去掉重复数字这种手法需要多加熟练。
这个9月加油找了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65061.html

相关文章

  • LeetCode 167:两数之和 II - 输入有序数组 Two Sum II - Input a

    摘要:公众号爱写给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值和,其中必须小于。示例输入输出解释与之和等于目标数。 公众号: 爱写bug(ID:icodebugs) 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。...

    张春雷 评论0 收藏0
  • LeetCode 167:两数之和 II - 输入有序数组 Two Sum II - Input a

    摘要:公众号爱写给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值和,其中必须小于。示例输入输出解释与之和等于目标数。 公众号: 爱写bug(ID:icodebugs) 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。...

    Me_Kun 评论0 收藏0
  • LeetCode - 001 - 两数之和(two-sum

    摘要:解法返回目录解题代码执行测试解题思路使用双重循环破解。解法返回目录解题代码执行测试知识点遍历数组,返回遍历项,返回当前索引。 Create by jsliang on 2019-05-16 22:19:13 Recently revised in 2019-05-17 14:22:40 Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 小伙伴们的 star 是我持续更新的动...

    habren 评论0 收藏0
  • LeetCode 攻略 - 2019 年 7 月下半月汇总(100 题攻略)

    摘要:月下半旬攻略道题,目前已攻略题。目前简单难度攻略已经到题,所以后面会调整自己,在刷算法与数据结构的同时,攻略中等难度的题目。 Create by jsliang on 2019-07-30 16:15:37 Recently revised in 2019-07-30 17:04:20 7 月下半旬攻略 45 道题,目前已攻略 100 题。 一 目录 不折腾的前端,和咸鱼有什么区别...

    tain335 评论0 收藏0
  • LeetCode 攻略 - 2019 年 7 月上半月汇总(55 题攻略)

    摘要:微信公众号记录截图记录截图目前关于这块算法与数据结构的安排前。已攻略返回目录目前已攻略篇文章。会根据题解以及留言内容,进行补充,并添加上提供题解的小伙伴的昵称和地址。本许可协议授权之外的使用权限可以从处获得。 Create by jsliang on 2019-07-15 11:54:45 Recently revised in 2019-07-15 15:25:25 一 目录 不...

    warmcheng 评论0 收藏0

发表评论

0条评论

andong777

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<