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: [email protected]
> 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: [email protected]
> 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)的不能过,可以给自己优化程序的理由。继续加油!

提交记录:


LeetCode 第一题,Two Sum,布布扣,bubuko.com

时间: 2024-10-09 17:51:27

LeetCode 第一题,Two Sum的相关文章

leetcode第一刷_Combination Sum Combination Sum II

啊啊啊啊,好怀念这种用递归保存路径然后打印出来的题目啊,好久没遇到了. 分了两种,一种是可以重复使用数组中数字的,一种是每个数字只能用一次的.其实没有多大区别,第一种每次进入递归的时候都要从头开始尝试,第二种要找一个标记的数组,把已经用到过的排除掉,就像生成全排列时的做法一样.跟我一样用引用保存中间结果的话,要注意回退的情况.第二种回退时,要把用到的那个数也恢复为可用,就完全像全排列时做的一样.破例贴两个题的代码,因为他们是在是不值得用两片文章来写. class Solution { publi

LeetCode第一题:Evaluate Reverse Polish Notation

时间:2014.06.11 地点:基地 -------------------------------------------------------------------- 一.题目 Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, /. Each operand may be an integer or another express

leetcode第一刷_Path Sum II

测试策略:静态测试还是动态测试? [对话场景] 成功发布某个软件版本之后,项目团队召开了项目的经验教训总结大会.在会议期间,项目经理小项和测试经理小测进行了如下的对话: 小项:"小测,我们的项目时间压力很大,测试执行是我们的关键路径,测试团队是否可以在测试执行阶段投入更多的人力和物力?"限定时间和人力资源同等条件. 小测:"啊!假如增加我们的测试执行时间,在整个周期不变的情况下,我们就需要压缩前期的学习和评审投入的时间和工作量,是吗?" 小项:"是的,你看

LeetCode 第一题剪彩自己的leetCode之路

Reverse Words in a String Given an input string, reverse the string word by word. For example, Given s = "the sky is blue", return "blue is sky the". click to show clarification. Clarification: What constitutes a word? A sequence of no

乘风破浪:LeetCode真题_040_Combination Sum II

乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum II 2.1 问题 2.2 分析与解决 通过分析我们可以知道使用递归就可以解决问题,并且这次我们从头遍历一次就不会出现多次使用某一个元素了. class Solution { List<List<Integer>> ans; public List<List<Intege

leetcode第一题(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

LeetCode第一题以及时间复杂度的计算

问题描述:给定一组指定整数数组,找出数组中加和等于特定数的两个数. 函数(方法)twoSum返回这两个数的索引,index1必须小于index2. 另外:你可以假设一个数组只有一组解. 一个栗子: Input: numbers={2, 7, 11, 15}, target=9 Output: index1=1, index2=2 算法实现如下: 1 /** 2 * 时间复杂度O(n) 3 * @param array 4 * @param target 5 * @return Map<Integ

leetcode第一题

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]

LeetCode 第一题 两数之和

题目描述 给定一个整数数组 nums?和一个目标值 target,请你在该数组中找出和为目标值的那?两个?整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同样的元素. 示例: 给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1] 方法一:暴力法 暴力法很简单,遍历每个元素 xx,并查找是否存在一个值与 target - xtarget?x