1. 程式人生 > >Leetcode 第 1 題(Two Sum)

Leetcode 第 1 題(Two Sum)

Leetcode 第 1 題

昨天去逛了逛這個網站,感覺挺有意思。準備有空的時候就做一兩道。

第一道題目是這樣的:

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.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

UPDATE (2016/2/13):
The return format had been changed to zero-based indices. Please read the above updated description carefully.

這個題目相對來說很簡單。最直接的做法是遍歷這個陣列中的任意兩個元素。計算他們的和是否滿足要求。下面貼一個最原始,最粗暴的解法:

class Solution {
    public:
    vector<int> twoSum(vector<int> &nums, int target) 
    {
        vector
<int>
result; int N = nums.size(); for (int i = 0; i < N - 1; i++) { for (int j = i+1; j < N; j++) { if (numbers[i] + numbers[j] == target) { result.push_back(i+1); result.push_back(j+1
); return result; } } } return result; } };

兩重迴圈,演算法的時間複雜度為 O(n2)。 在這個演算法的框架下,唯一可以優化的地方在於那條判斷語句:

nums[i] + nums[j] == target

每次判斷時都要重新計算一下兩個元素的和。實際上這步求和計算可以剩下來。下面是改進後的程式碼:

class Solution {
    public:
        vector<int> twoSum(vector<int>& nums, int target) {
            vector<int> ret;
            int N = nums.size();
            for(int i = 0; i < N-1; i++)
            {
                int val = target - nums[i];
                for(int j = i+1; j < N; j++)
                {
                    if(nums[j] == val)
                    {
                        ret.push_back(i);
                        ret.push_back(j);
                        return ret;
                    }
                }
            }
        }
    };

這樣改進之後,運算速度大約可以提高 30%。

不過還是挺慢的。理論上來說,遍歷一遍這個陣列就能獲得全部資訊了。沒有必要反覆的遍歷這麼多次。

舉個例子:

Given nums = [2, 7, 11, 15], target = 9

含顯然,[2, 7, 11, 15] 與它配對的陣列是 [7, 2, -2, -6]。 這個陣列是可以提前算出來的。之後我們只要在這個數組裡查詢有沒有我們需要的數字就好了。選擇好合適的資料結構,這個查詢的工作的時間複雜度可以做到O(1)這麼低。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int, int> lookup;
        int N = nums.size();
        for(int i = 0; i < N; i++)
        {
            lookup.insert(pair<int, int>(nums[i], i));
        }

        for(int i = 0; i < N; i++)
        {
            int value = target - nums[i];
            if(lookup.count(value) && lookup[value] != i)
            {
                vector<int> ret;
                ret.push_back(i);
                ret.push_back(lookup[value]);
                return ret;
            }
        }
    }
};

改成這個程式碼之後執行速度提高了一個數量級。但是也只是打敗了27.73% 的程式碼提交者。所以還是有改進的餘地。

想一想,這個程式碼可以改進的地方也就是這兩個迴圈了。現在第一個迴圈用來建立map。第二個迴圈使用這個 map。 其實用一個迴圈就可以。因為答案由兩個陣列成,當我們的map 資料不全時,可能會錯過一次配對,但是肯定不會錯過第二次配對的。按照這個思路,程式碼進一步修改為:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int, int> lookup;
        int N = nums.size();
        for(int i = 0; i < N; i++)
        {
            int value = nums[i];
            if( lookup.count(target - value) )
            {
                vector<int> ret;
                ret.push_back(lookup[target - value]);
                ret.push_back(i);
                return ret;
            }
            lookup.insert(pair<int, int>(value, i));
        }
    }
};

現在這個程式碼已經可以打敗 45% 的答題者了。剩下的可以優化的地方已經不多了,折騰了一會兒,一直沒能突破這個 45%。。。

各位大神們,求一個更好的程式碼。