[從零單刷Leetcode] 1. Two Sum

先從Easy開始吧,畢竟菜。

原題:

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

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) {
        for(int i = 0; i<nums.length-1; i++ ){
            int num1 = nums[i];
            int num2 = target-num1;
            for(int j = i+1; j<nums.length;j++){
                if(num2==nums[j]){
                    return new int[]{i,j};
                }
            }
        }
        return new int[]{1,1};
    }

}

用了2個for循環,窮舉了所有的可能,直到找到正確的結果。
看下runtime:

runtime

可以看出效率是比較低的。
首先遍歷數組,時間復雜度是Θ(n),每個循環里面又需要遍歷一部分的數字,時間復雜度是Θ(n),總的時間復雜度是Θ(n2)。
那么如何提升呢?首先數組是肯定要遍歷一次的,那么只能在每一步的操作上面思考了。
for(int i = 0; i < nums. length; i++)
當循環到 nums[i] 的時候,需要快速的確定是否存在與其和為target的數,如何做到使這一步的復雜度小于Θ(n)呢?看了下提示,那就是使用HashMap。
因為HashMap查找的時間復雜度是Θ(1),所以整體的時間復雜度可以降到Θ(n)!

第二次提交
public class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> exist = new HashMap();// map<數字,序號>
        for(int i = 0 ; i < nums.length; i++){
            if(exist.keySet().contains(nums[i])){
                return new int[]{exist.get(nums[i]), i};
            }
            exist.put(target - nums[i], i);
        }
        return new int[]{0,0};
    }

}
第二次提交

可以看到,runtime明顯提升了一大截。

最后看看最快的是怎么做的,只用了3ms:

the fastest
public class Solution {
    public int[] twoSum(int[] nums, int target) {
        
        short[] map = new short[20050];//新建一個map數組做映射
        int size = 50;
        for (int i = 0; i < nums.length; i++) {
            map[nums[i] + size] = (short) (i + 1);
            int diff = target - nums[i + 1] + size;
            if (diff < 0) continue;
            short d = map[diff];
            if (d > 0)
                return new int[]{d - 1, i + 1};
        }
        return null;
        
        
    }
}

看起來一臉懵逼,循環里面的語句一句句分析一下。
map[nums[i] + size] = (short) (i + 1);
每遍歷一個數組元素,把其序號+1保存在map[nums[i] + size] 中。(為什么保存i + 1而不保存i,是因為如果i == 0的話就不能分辨出是默認值還是有保存過了)相當于建立了 nums[i] + size -> i + 1 [1] 的映射關系(事實上,nums[i] + size很可能是負數,理論上不應該這么寫)。
int diff = target - nums[i + 1] + size;
這個diff是什么含義?target - nums[i + 1] 好理解,其和數組第i + 1項的和是target,滿足題目要求。假設這個數存在于nums數組中,且他的序號為k,即:
nums[k] == target - nums[i + 1],
那么:
diff == nums[k] + size,
眼熟啊!從[1]式中可以看出,在map數組中:
nums[k] + size -> k + 1,
即是在map數組中的對應關系是
diff -> k + 1

short d = map[diff];
if (d > 0)
    return new int[]{d - 1, i + 1};

這樣分析過來,這句話就好理解了:如果對于nums[i + 1]來說,和其和為target的數nums[k]已經遍歷過并保存在了map中,那么map中一定會有:
diff -> k + 1
這個對應關系,即 d == map[diff] == k + 1。我們知道java中short默認值為0,而 k + 1必然是大于0的,所以如果 d > 0,說明:
nums[i + 1] + nums[k] == target,
滿足題目條件,因為d == k + 1,所以返回:
return new int[]{d - 1, i + 1};
邏輯并不是很難,本質上和hashmap有異曲同工之妙,但是有個magic numberint size = 50;這個根據數據集來的數,看來是針對測試集精心準備的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容