2sum是Leetcode上很经典的题,它可以延伸到3sum,4sum以及Ksum,在面试的时候很多面试官喜欢考这样的类型问题。在后面的博客中我会讲解它的延伸情况,这里先来看看2sum。
先贴一下Leetcode上面(Leetcode 1)关于2sum的描述:
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], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
常规的Brute Force解(两层循环,复杂度较高)容易超时
这个题可以从两个角度来思考:
(1) 若不对原始数组进行排序,用一遍循环通过Hash来找出满足条件的解
(2) 若对原数组进行排序,用首尾指针依次向中间靠拢,来找出满足条件的解
下面对上面两个思路分别进行解析:
对于第一个思路,由于要使得
a
i
+
a
j
=
a_i+a_j=
ai+aj= target,也就是说只需在遍历
i
i
i 时,判断 target
−
a
i
-a_i
−ai在数组中即可。
对于数组 nums = [2, 7, 11, 5],通过一遍循环从第0号元素2开始,先将2存入一个空的Hash表中(Hash的key为nums中的元素,Hash的value为对应索引序号),那么{2:0}就被存起来了,再遍历到第1号元素7,计算target减去当前元素所得值是否在Hash表的键中,如果在则返回Hash表的那个键对应的序号和当前这个序号即可。
Python代码如下:
def twoSum(nums, target):
dicts = {}
for i in range(len(nums)):
if target-nums[i] in dicts:
return [dicts[target-nums[i]], i]
dicts[nums[i]] = i
对于第二个思路,先对 nums 进行排序,由于需要保存排序前的索引,所以 nums = [2, 7, 11, 5] 排序后写成 lists = [[2,0], [5,3], [7,1], [11,2]] 的形式,其中 lists[i][0] 表示 nums 中的元素,而 lists[i][1] 表示 nums 中元素对应的原始索引。因为排序会导致索引变化,所以需要将原始索引在排序时一起存起来。
先介绍一个数学思维:“有序数组的首尾渐进相夹”(即缩小包围圈),这不仅在这道题中求和会用到,而且它也是数据结构中的二分查找的核心。对于这道题的求和,称首指针为left,尾指针为right,如果 [left]+[right] >target([left]表示 left 对应的元素值),则需要使得 [left]+[right] 减小,即 right 要左移(因为数组元素是递增的,左边的元素要小于等于右边的元素),相应地, 如果 [left]+[right] <target,则需要使得 [left]+[right] 增大,即 left 要右移。
对 nums = [2, 7, 11, 5] 排完序后得到 [2, 5, 7, 11] (相应排序前序号为 [0, 3, 1, 2]),让左右指针从两头开始寻找,初始情况下 left = 0,right=3(注:这里的3是最大索引)。计算它们对应的元素之和,即 2+11 = 13 要大于 target,说明元素的和需要减小,故 left 不变,right要变成 right-1,这样就变成计算 2+7,而 2+7 刚好等于 target, 所以结束循环。循环是结束了,但是并不能返回 [left, right],因为排过序,所以应该返回排序前的索引序号,即返回 [lists[left][1], lists[right][1]]。
Python 代码如下:
def twoSum(nums, target):
lists = []
for i in range(len(nums)):
lists.append([nums[i], i])
lists = sorted(lists, key = lambda s:s[0])
l = 0; r = len(nums)-1
while(l<r):
if lists[l][0]+lists[r][0]==target:
return [lists[l][1], lists[r][1]]
elif lists[l][0]+lists[r][0]<target:
l+=1
else:
r-=1
做题目注重的是思维,一个题目可以有多种解,需要通过做题目的过程去体会方法,这样才能真正学到东西。