首页 > 代码库 > LeetCode 第一题,Two Sum

LeetCode 第一题,Two Sum

今天早上起来去机房的路上还在想,一直不做算法题老是觉得不踏实。做做题总是让自己觉得自己真的在做做学习。。。。


这也算是一种强迫症吧。

那就从今天开始做做LeetCode,因为好久没做过了,所以第一题还是看了别人的题解和思路,算是找找感觉。


总的来说第一题是个水。。。。



题目还原

Two Sum

Given an array of integers, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

中文意思就是给你一个整数数组和一个目标值,找出这个数组中的两个元素的和为该目标值的元素的下标,输出为index1和index2,按从小到大的序输出。且假定每个数组都恰好包含一个解,即使,恰好每个给定的数组都有且只有一种符合的结果。

解答

首先,做这种的题目一般都是需要排序的,为了速度,也为了方便查找。

当然,不排序也可以做,,不过那个复杂度貌似有点不合理。

虽然给定的数组不是按序给定的,但是,我们要知道,在这个算法的世界里面,有序的话,一切都很简单了。

有序的价值啊。可惜我的生活现在还是有些混乱无章。。。。

但是因为最后要返回的是原数组的元素下标,因此,我们必须要在使用原数组的空间做一个副本。。。。

然后就是打副本,首先假设副本是从小到大,至于怎么打,我想到一种方法:

选取左右两个游标,取二者之和,若和等于目标值,将这个所代表的元素在原数组查找确定下标,然后保存在index中。留待返回。

若和大于目标值,则让右下标往左移;

若和小于目标值,则让左下标往右移。

直至左右相等之前为止。

因此实现的代码如下:

/*************************************************************************
> File Name: twosum.cpp
> Author: SuooL
> Mail: 1020935219@qq.com
> Created Time: 2014年07月31日 星期四 20时14分32秒
************************************************************************/

class Solution
{
    public:
    vector<int> twoSum(vector<int> &numbers, int target)
    {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        vector<int> num = numbers;
        std::sort(num.begin(), num.end());

        int length = numbers.size();
        int left = 0;
        int right = length - 1;
        int sum = 0;

        vector<int> index;

        while(left < right)
        {
            sum = num[left] + num[right];

            if(sum == target)
            {
                // find the index from the old array
                for(int i = 0; i < length; ++i)
                {
                    if(numbers[i] == num[left])
                    {
                        index.push_back(i + 1);
                    }
                    else if(numbers[i] == num[right])
                    {
                        index.push_back(i + 1);
                    }
                    if(index.size() == 2)
                    {
                        break;
                    }
                }
                break;
            }
            else if(sum > target)
            {
                --right;
            }
            else
            {
                ++left;
            }
        }

        return index;
    }
};

Python代码:

#!/usr/bin/env python
# coding=utf-8
class Solution:
    # @return a tuple, (index1, index2)
    def twoSum(self, num, target):
     
        numbers = sorted(num)

        length = len(num)
        left = 0
        right = length - 1

        index = []

        while left < right: 
            sums = numbers[left] + numbers[right]

            if sums == target:
                for i in range(length):
                    if num[i] == numbers[left]:
                        index.append(i + 1)
                    elif num[i] == numbers[right]:
                        index.append(i + 1)
                    
                    if len(index) == 2:
                        break

                break
            elif sums > target:
                right -= 1
            else:
                left += 1

        result = tuple(index)

        return result
下面的一个类似的解法是网络上的,用了最新的C++11标准。表示膜拜。

/*************************************************************************
> File Name: twosum_c11.cpp
> Author:SuooL 
> Mail: 1020935219@qq.com
> Created Time: 2014年07月31日 星期四 20时18分45秒
************************************************************************/

class Solution 
{
    public:
    typedef pair<int, size_t> valoffset_pair_t;


    vector<int> twoSum(vector<int> &numbers, int target) 
    {
        vector<valoffset_pair_t> new_array;
        // Preallocate the memory for faster push_back
        new_array.reserve(numbers.size());

        // generate the new array
        int index=0;
        for(auto i : numbers)
        new_array.push_back(valoffset_pair_t(i, ++index));
        // Note: here the index is already increased, so 
        // the new indices are not zero based

        // sort it!
        sort(begin(new_array), end(new_array), 
             [&](const valoffset_pair_t& v1, const valoffset_pair_t& v2) -> bool
             {
                 return v1.first < v2.first;
             }
            );

        // now find the right pair using two pointers
        auto p_left=begin(new_array);
        auto p_right=end(new_array); 
        p_right--;  // make it locate at the last position
        int sum = p_left->first + p_right->first;

        // it is guaranteed to have one exact solution
        while(sum!=target)
        {
            sum = p_left->first + p_right->first;
            if (sum > target)
            p_right--;
            else if (sum < target)
            p_left++;
            else
            break;
        }
        return vector<int>(
            {min(p_left->second, p_right->second),
             max(p_left->second, p_right->second)});
    }
};

同时,最后看到了,只用了O(n)的hashmap方法,思路是循环一次,每次都判断当前数组索引位置的值在不在hashtable里,不在的话,加入进去,key为数值,value为它的索引值;在的话,取得他的key,记为n(此时n一定小于循环变量i),接下来再在hashtable中查找(target-当前数值)这个数,利用了hashtable中查找元素时间为常数的优势,如果找到了就结束,此处需要注意的是,如果数组中有重复的值出现,那么第二次出现时就不会加入到hashtable里了,比如3,4,3,6;target=6时,当循环到第二个3时,也可以得到正确结果。

代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) 
	{
        int i, sum;
        vector<int> results;
        map<int, int> hmap;
        for(i=0; i<numbers.size(); i++)
		{
			// if the number doesn`t existed in the hashtable
            if(!hmap.count(numbers[i]))     
			{
				// insert the number and its index
                hmap.insert(pair<int, int>(numbers[i], i));
            }
			// find the number which equal to target - numbers[i]
            if(hmap.count(target-numbers[i]))
			{
                int n=hmap[target-numbers[i]];
                if(n<i)
				{
                    results.push_back(n+1);
                    results.push_back(i+1);
                    //cout<<n+1<<", "<<i+1<<endl;
                    return results;
                }

            }
        }
        return results;
    }
};

Python:

class Solution:
	# @return a tuple, (index1, index2)
	def twoSum(self, num, target):
		length = len(num)
		index = []
		hash_tab = {}
		for i in range(length):
			if( not hash_tab.has_key(num[i])) :
				pair = {num[i] : i}
				hash_tab.update(pair)
			if( hash_tab.has_key(target - num[i] )) :
				n = hash_tab[target-num[i]]
				if n< i :
					index.append(n + 1)
					index.append(i + 1)
					result = tuple(index)
					return result
		return result

Summary

先想到的就是两层遍历法,但是显然时间复杂度太高,是O(N^2),不符合要求.
于是就应该想如何降低复杂度,首先应该想将逐个比较转变为直接查找,即首先计算出 target与当前元素的差,然后在序列中寻找这个差值,这样首先就把问题简化了,而寻找的过程可以先对序列进行快排,然后二分查找,这样整体的复杂度就降低为 O(N*logN) 了;查找最快的方法是利用一个 map容器存储每个元素的索引,这样取得某个特定元素的索引只需要常数时间即可完成,这样就更快了,最多只需遍历一次序列,将元素及其索引加入map中,在遍历的过程中进行对应差值的查找,如果找到了就结束遍历,这样时间复杂度最多为 O(N),It‘s amazing!

总之,这是自己做的第一道leetcode题,感觉比其他oj要严格一些,比如这题有严格的执行时间要求,O(N^2)的不能过,可以给自己优化程序的理由。继续加油!

提交记录: