LeetCode #1 - Two Sum

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode #1 - Two Sum相关的知识,希望对你有一定的参考价值。

题目链接:https://leetcode.com/problems/two-sum/description/

LeetCode第一号题目,难度是easy,通过率却只有35%。

看完题目描述感觉确实简单:

给你一个整数数组nums,以及一个整数target,要求从nums中找到两个和为target的数,返回这两个数的下标(indices)。

你可以认为每个输入有且仅有唯一解,且nums中每个数字只能使用一次。

样例:

给出 nums = [2, 7, 11, 15], target = 9,

因为 nums[0] + nums[1] = 2 + 7 = 9,
所以结果是 [0, 1].

 

久别竞赛思维,再加上看到难度是easy(选择性地忽略了35%的通过率orz),以为这里对程序时间复杂度不会有太高要求,自然而然想到了暴力搜索,于是很快写好了如下的python代码:

def twoSum(nums, target):
    for i in range(len(nums) - 1):
        for j in range(len(nums) - i - 1):  
            if nums[i] + nums[i + j + 1] == target:  
                indic = [i, i + j + 1]  
                break
    return indic

简单直接易懂的代码。

提交,结果TLE。通过了前18个案例,卡在最后一个,nums元素达到12599个。按理说10^8离10^9还差个量级啊,python慢也不至于慢到把这个量级补上吧…?可是在本地pycharm上测试了一下,运行时间竟然达到惊人的12s!不仅补上了,还超出一个量级!这才再次看到这题惨淡的通过率。。。

只能改进算法了,然而高中搞竞赛时候就没把数据结构和算法学好,又好几年没碰,完全没有思路。无奈之下求助于官方solution,第一种解法就是java实现的暴搜。第二种就是哈希了。对哈希还是有点印象的,用空间换时间,达到O(1)的查找。可是怎么实现呢,自己写哈希函数的话,又有点捉急了-_-||。再看官方给出的代码,是直接使用java内置的hashmap类实现的。

诶?python不是也自带一个基于哈希算法的数据类型——字典类型(dict)嘛,那就用dict来实现一下吧。

回顾一下暴搜的思路:选定一个元素nums[i],然后从它后面一个元素开始挨个尝试,看两者加起来是不是等于target。既然target这个东西是已知的,那我们在选定nums[i]以后是否可以不挨个试,而是直接查询nums数组里有没有 target - nums[i] 这个东西(就是互补部分,complement),如果查询到,就得到一个解,根据题目描述这是唯一解,就可以直接break返回了。说到查询数组里有没有一个数,用python的话第一反应是通过in判断,可不要忘了这个方法其实就是遍历数组,如果这么做那就又回到暴搜上了。

如何用dict来实现呢?我们先遍历一次nums,把nums[i]作为key,下标 i 作为value,存储到字典d中。然后再次遍历nums,对每个nums[i],查询它的complement是否在字典d里,如果存在,说明得到唯一解,输出当前元素的下标 i 和complement的下标即d[target - nums[i]]两个数组成的列表。

但这还没完,有一种特殊情况是target由两个相等的数相加得到,如nums = [3, 3], target = 6。在python中,字典类型的key是唯一的,不允许有重复。所以我们需要单独写一段代码应对这种情况。完整代码如下:

def twoSum(nums, target):
    if target / 2 in nums:       #判断特殊情况
        l = []
        for i in range(len(nums)):
            if nums[i] == target / 2:
                l.append(i)
        if len(l) == 2:        #还得判断是否得到了两个数,因为还有这种情况:[3, 2, 4] --> 6
            return l
           
    d = {}
    for i in range(len(nums)):
        d[nums[i]] = i
    for i in range(len(nums)):
        if nums[i] != target - nums[i]:    #还是[3, 2, 4] --> 6这种情况,没有这一条件将输出[0, 0]
continue if d.get(target - nums[i]) != None: return [i, d[target - nums[i]]]

提交,AC。

其实我们可以只用一次循环,边填字典边查询:虽然字典没编写完,但我们利用已经编好的部分可以查到当前元素的complement是否在前面出现过了。不同之处在于,上面的两次循环得到解时,i 是在nums中位置靠前的数的下标,而一次循环得到得到解时 i 是位置靠后的数的下标,所以最后返回值里两个表达式也要颠倒一下。代码如下:

    #前面的部分不变
    for i in range(len(nums)):
        if nums[i] == target - nums[i]:
            continue
        d[nums[i]] = i
        if d.get(target - nums[i]) != None:
            return [d[target - nums[i]], i]

不过少这一次循环对程序运行时间根本没啥影响。


以上是关于LeetCode #1 - Two Sum的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode] 1 Two Sum

[LeetCode] 1 Two Sum

LeetCode之371. Sum of Two Integers

leetcode 1.Two sum

Leetcode 1. Two Sum (Python)

LeetCode #1 - Two Sum