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%。。。

各位大神们,求一个更好的代码。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值