Two Sum系列 Leetcode解题记录

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<Integer, Integer> map = new HashMap<Integer, Integer>();
        //扫一遍数组,一边扫一边存
        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),由于只扫了一遍数组。less

最后再说两句

虽然这题很是简单,可是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,怪不得被收购了。code

解决思路

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

code

public class TwoSumIII {
    //用一个List当容器装数字,用Map来记录数字出现的次数
    List<Integer> list = new ArrayList<>();
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    // 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)。three

最后再说两句

写的时候发现其实遍历一下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<List<Integer>> threeSum(int[] nums) {
        //首先把输出写出来
        List<List<Integer>> 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<Integer> 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<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> 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月加油找了。

相关文章
相关标签/搜索