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月加油找了。

相關文章
相關標籤/搜索