LeetCode 1.Two Nums

LeonChen陳林峰發表於2017-10-14

[Chinese ver]1.兩數求和 。給定一個整數的陣列,返回兩個數字的索引使得這兩個數字加起來成為一個指定的目標值。
你可以假設每個輸入都至少有一個解決方案,並且你不能使用相同的元素兩次。

Example:

Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].複製程式碼

首先是一次錯誤的嘗試

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] testNums = new int[nums.length];
        int j = 0;
        for (int i=0;i<nums.length;i++){
            if(nums[i]<target){
                testNums[j] = nums[i];
                j=j+1;
            }
        }
        for(int l=0;l<j;l++){
            for(int k=l+1;k<j;k++){
                if(testNums[l]+testNums[k]==target){
                    return new int[]{l,k};
                }
            }
        }
      return null; 
    }
}複製程式碼

出錯啦!!
出錯啦!!

沒理解好題意,這種解法最後得到的是我們自定義陣列的序號,而不是題目要求的序號,暈倒。。。後來發現一個更嚴重的問題。。他沒有說過不能有負數!!!!也就不需要判斷陣列裡的值是否大於和。。。

然後重新構思一下,先來個基本的解法

方法一:暴力迴圈

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        for (int i=0;i<nums.length;i++){
           for(int k=i+1;k<nums.length;k++){
                if(nums[i]+nums[k]==target){
                    return new int[]{i,k};
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}複製程式碼

至於結果嘛,最簡單的自然也高效不到哪裡。

效率
效率

分析
這個方法的原理很簡單,就是將每一個值與其他的值迴圈遍歷,看是否有符合條件的情況發生。稍微要注意的是 for(int k=i+1;k<nums.length;k++) 這裡k=i+1是為了避免前面迴圈過的情況再次迴圈一遍。
時間複雜度 : O(n^2) 。n個元素都要迴圈遍歷陣列內其餘的元素,所以是n^2。
空間複雜度 : O(1) .

方法二:兩次迴圈的 hash table

public class Solution {
   public int[] twoSum(int[] numbers, int target) {
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for (int i = 0; i < numbers.length; i++) {
        map.put(numbers[i],i);
    }
   for (int i = 0; i < numbers.length; i++) {
       int requestNum = target - numbers[i];
        if (map.containsKey(requestNum)&&map.get(requestNum)!=i) {
            return new int[]{i,map.get(requestNum)};
        }
    }
    throw new IllegalArgumentException("No two sum solution");
}
}複製程式碼

可以看到效率有了很大的提升。

效率
效率

分析
這個方法的原理其實就是使用hash table 來將時間成本來替換空間成本,將一次複雜度為O(n)的迴圈變為接近O(1)的查詢,為什麼是接近呢,因為如果hash table發生大量的碰撞,就會導致複雜度向O(n)靠近。我們將陣列裡每一個元素的值作為key存入hash table,而將其序列號作為對應的value存入hash table,然後遍歷陣列查詢是否有對應的值在hash table 的key中,有則取出該key對應的value。
時間複雜度 : O(n)
空間複雜度 : O(n)

方法三:一次迴圈的 hash table

public class Solution {
   public int[] twoSum(int[] numbers, int target) {
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for (int i = 0; i < numbers.length; i++) {
       int requestNum = target - numbers[i];
        if (map.containsKey(requestNum)) {
            return new int[]{map.get(requestNum),i};
        }
        map.put(numbers[i],i);
    }
    throw new IllegalArgumentException("No two sum solution");
}
}複製程式碼

效率稍微提高了一些。。但是有些不能接受啊。怎麼還只是在中間的位置。。。然後試了幾次,大概在40%到50%徘徊,前面那些是用什麼算的。。。為何那麼快。理論上來說至少需要迴圈比對結果一次,也就是需要O(n)的複雜度。

效率
效率

分析
這個方法的原理其實就是方法二的一個改善,因為我們不需要將全部的陣列都放入hash table ,我們最終的目的是為了得到兩個相加等於目標數的值的序號即可,所以我們在將陣列裡的值放入hash table 的時候就進行比對,一旦得到所需要的值立即結束迴圈。
時間複雜度 : O(n)
空間複雜度 : O(n)

如果你有更好的辦法或者對我這裡的描述有其他看法,請聯絡我。謝謝

更多
github.com/LeonChen102…

相關文章