LeetCode 1 Two Sum

题目-两数之和

【英文版】https://leetcode.com/problems/two-sum/
【中文版】https://leetcode-cn.com/problems/two-sum/

给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例

给定 nums = [2, 7, 11, 15], target = 9 
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
:type nums: List[int]
:type target: int
:rtype: List[int]

解法

暴力求解

遍历nums中的每一个元素x,遍历剩下nums中另一个元素j使得x+j=target
★ 时间复杂度:\(O(n^2)\)
  对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费\(O(n)\)的时间。因此时间复杂度为\(O(n^2)\)
★ 空间复杂度:\(O(1)\)

Brute Force -python-1

class Solution(object):
    def twoSum(self, nums, target):
        index1=0
        index2=0
        for i in range(len(nums)):
            ele1=nums[i]
            for j in range(i+1,len(nums)):
                lel2=nums[j]
                if ele1+lel2==target:
                    index1=i
                    index2=j
                    break
        return index1,index2

结果
执行用时 :4172 ms, 在所有 Python 提交中击败了26.93%的用户

内存消耗 :12.7 MB, 在所有 Python 提交中击败了25.36%的用户

代码可精简部分

  1. 无需定义变量index1、index2,直接在ele1+lel2==target直接return
  2. 无需定义变量ele1、lel2,直接使用nums[i]、nums[j]

     
Brute Force -python-2

class Solution(object):
    def twoSum(self, nums, target):
        for i in range(len(nums)):
            for j in range(i+1,len(nums)):
                if nums[i]+nums[j]==target:
                    return i,j

执行用时 :4996 ms, 在所有 Python 提交中击败了16.95%的用户

内存消耗 :12.4 MB, 在所有 Python 提交中击败了37.72%的用户

 

排序+二分法

先将数组从小到大排序\(O(nlogn)\),遍历nums中的每一个元素x,用二分法查找nums中是否存在元素target-x\(O(logn)\).

★ 时间复杂度:\(O(nlogn)\)
★ 空间复杂度:\(O(n)\)

Sort+bisection -python

class Solution(object):
    def twoSum(self, nums, target):
        nums_copy=nums.copy() # 保留原数组,sort函数会修改原数组
        nums.sort() # 排序-timsort算法

        vals=[] # 满足两数相加==target的数
        for i in range(len(nums)):
            start=i+1
            end=len(nums)-1
            while(start<=end):
                mid_s = int((start + end) / 2)
                if nums[mid_s]==target-nums[i]:
                    vals.append(nums[i])
                    vals.append(nums[mid_s])
                    break
                elif (nums[mid_s]<(target-nums[i])):
                    start=mid_s
                else:
                    end=mid_s
                mid_e = int((start + end) / 2)
                if mid_e==mid_s:
                    start+=1
        
        result=[]
        for i in range(len(nums_copy)):# 在原数组中找这两个数的索引
            if (nums_copy[i]==vals[0])or(nums_copy[i]==vals[1]):
                result.append(i)

        return result

执行用时 :132 ms, 在所有 Python 提交中击败了57.70%的用户

内存消耗 :12.5 MB, 在所有 Python 提交中击败了36.60%的用户

 

哈希表

• 以空间换速度
• 保持数组中的每个元素与其索引相互对应的最好方法:哈希表[1]
  哈希表支持以近似恒定的时间进行快速查找。用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到\(O(n)\)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为\(O(1)\)[2]
 

两遍哈希表

  在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i])是否存在于表中。注意,该目标元素不能是nums[i]本身![2:1]
★ 时间复杂度:\(O(n)\)
  我们把包含有n个元素的列表遍历两次。由于哈希表将查找时间缩短到\(O(1)\),所以时间复杂度为\(O(n)\)
★ 空间复杂度:\(O(n)\)
  所需的额外空间取决于哈希表中存储的元素数量,该表中存储了n个元素。

Two-pass Hash Table -python

class Solution(object):
    def twoSum(self, nums, target):
        dict={}
        for i in range(len(nums)):
            dict[str(nums[i])]=i
        for i in range(len(nums)):
            if str(target-nums[i])in dict and dict[str(target-nums[i])]!=i:
                return i,dict[str(target-nums[i])]

执行用时 :44 ms, 在所有 Python 提交中击败了92.95%的用户

内存消耗 :14.2 MB, 在所有 Python 提交中击败了5.01%的用户

  ###一遍哈希表 在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。 ★ 时间复杂度:$O(n)$, 我们只遍历了包含有$n$个元素的列表一次。在表中进行的每次查找只花费$O(1)$的时间。 ★ 空间复杂度:$O(n)$, 所需的额外空间取决于哈希表中存储的元素数量,该表最多需要存储`n`个元素。[^LeetCode] [^gif]
One-pass Hash Table -python-1

class Solution(object):
    def twoSum(self, nums, target):
        dict={}
        for i in range(len(nums)):
            if str(target - nums[i]) in dict and dict[str(target - nums[i])] != i:
                return i,dict[str(target-nums[i])]
            else:
                dict[str(nums[i])] = i

执行用时 :84 ms, 在所有 Python 提交中击败了59.23%的用户

内存消耗 :13.9 MB, 在所有 Python 提交中击败了5.01%的用户

代码可精简部分

  1. 直接用enumerate代替range(len(nums))和nums[i]
     
One-pass Hash Table -python-2

class Solution(object):
    def twoSum(self, nums, target):
        dict={}
        for i,val in enumerate(nums):
            if dict.get(target-val) is not None:
                return i,dict.get(target-val)
            else:
                dict[val]=i

执行用时 :44 ms, 在所有 Python 提交中击败了92.95%的用户

内存消耗 :13.1 MB, 在所有 Python 提交中击败了14.19%的用户

代码可修改部分

  1. 用尾递归代替循环遍历

     
One-pass Hash Table -python-3

class Solution(object):
    def twoSum(self, nums, target,i=0,dict={}):
        if dict.get(target-nums[i]) is not None:
            result=[dict.get(target - nums[i]),i]
            dict.clear()
            return result
        else:
            dict[nums[i]] = i
            i+=1
            return self.twoSum(nums,target,i,dict)

执行用时 :36 ms, 在所有 Python 提交中击败了99.37%的用户

内存消耗 :21 MB, 在所有 Python 提交中击败了5.01%的用户

• 易错点-形参&实参[3]


注意要在函数结束时清空字典的值,不然第二次调用该函数,字典有值导致结果不对。

 

如有错误或有其他解题思路,欢迎指出~~~φ(≧ω≦*)♪

参考


  1. 哈希表 ↩︎

  2. LeetCode题解 ↩︎ ↩︎

  3. 形参&实参 ↩︎

posted @ 2019-08-18 15:01  维夏十四  阅读(149)  评论(0编辑  收藏  举报