题目:
给定一个池塘里面有众多小蝌蚪(整数数组 nums) 他们已经排队好了,有一位青蛙妈妈(一个目标值 :0),请你帮助青蛙妈妈找到他的亲生孩子(青蛙:生的时候我也没想过要找啊),假设青蛙妈妈身上带有一个整数mom,池塘中每个小蝌蚪身上各有一个整数(有的小蝌蚪拥有一样的数),生活压力太大了,最近得以改善,所以青蛙妈妈这次把自己的娃都找到,且每次找三个组成一组,一组一组找知道找完,一组亲生娃身上所带的整数相加正好等于青蛙妈妈身上的整数mom。
例1
池塘中所有小蝌蚪所带整数构成的数组为: nums = [-4, 5, 2, -1]
青蛙妈妈的目标值为:mom = 0
你需要找出一组娃(三个)个娃:-4,5,-1
返回:[[-4, 5, -1]]
例2
众多小蝌蚪所带整数构成的数组为: nums = [-1, 3, -2, 1,0]
青蛙妈妈的目标值为:mom = 0
你需要找出一组娃(三个)个娃:-1,3,-2和-1,1,0
返回:[[-1,3,-2],[-1,1,0]]
例3
众多小蝌蚪所带整数构成的数组为: nums = [1, 3, -9, 1,0]
青蛙妈妈的目标值为:mom = 0
你需要找出一组娃(三个)个娃:无(没有亲生娃)
返回:[]
注:返回的列表不能含有相同的子列表
题目分析:给定一个数组nums,要求找出三个数相加为0,并找出所有结果。
老样子,按平常思路
暴力解题:
从数组中一个一个找出第一第二第三个孩子,将其所带整数相加看是否为0
假设三个小蝌蚪分别为i,j,k,使用for循环找出
for i in range(len(nums)):
for j in range(i + 1, len(nums)): # j从i+1开始,避免重复选到同一个娃
for k in range(j + 1, len(nums)): # k从j+1开始,避免重复选到同一个娃
if nums[i] + nums[j] + nums[k] == 0:
li = [nums[i], nums[j], nums[k]] # 一组娃
lst.append(li)---------------------------------------------------------------进运行到这
输出:
[[-1, 1, 0], [-1, 1, 0],------>重复
[-1, 0, 1], [-1, -2, 3],
[-1, 0, 1], [1, -2, 1]
[0, -2, 2], [-2, 0, 2]] ------->重复
运行情况
根据暴力解题法,什么叫做暴力?,就是简单粗暴,万事for循环就是暴力解题的精髓.
我们就可以使用for循环一个一个找出第一第二个子列表看它们是否相同,相同则移除一个
# 去重
for x in range(len(lst)):
for y in range(x + 1, len(lst)):
if y < len(lst) and lst[x] == lst[y]: # 因为去重时列表长度改变所以加上限制条件
lst.remove(lst[y]) # y < len(lst)
else:
continue
还有一个问题就是我们可以看到第二处重复, [0, -2, 2], [-2, 0, 2],重复但两个列表并不相等利用上面的去重操作无法去除此类重复,怎么办呢?
观察这两个列表,所含元素一样,但顺序不一样,对于顺序我们便可以利用sort()函先将列表排序然后再去重就好了
完整代码如下
def findChild(nums):
lst = [] # 包含结果的列表
if len(nums) < 3: # 小于三的列表直接返回[]
return(lst)
else:
for i in range(len(nums)):
for j in range(i + 1, len(nums)): # j从i+1开始,避免重复选到同一个娃
for k in range(j + 1, len(nums)): # k从j+1开始,避免重复选到同一个娃
if nums[i] + nums[j] + nums[k] == 0:
li = [nums[i], nums[j], nums[k]] # 一组娃
li.sort() # 从小到大排列li,方便后面的去重复
lst.append(li)
lst.sort() # 排列结果列表,方便后面的去重复
for x in range(len(lst)): # 去重
for y in range(x + 1, len(lst)):
if y < len(lst) and lst[x] == lst[y]:
lst.remove(lst[y])
else:
continue
return lst
暴力解题,简单明了,一分析花费时间约为O(n^3),n为列表长度.下面我们用另一种方法优化解题速度.
排序+循环+指针
别看见指针就慌其实很容易理解的.
寻找三个数,和为零,不重复.第一第二不都容易操作,那么第三步不循环的本质时什么呢?
怎样获取到一个不重复的子列表[a, b, c],而不是[a,c,b]或[b,c,a]呢?类似暴力解法的分析,三个列表仅元素顺序不同,如何固定元素顺序,使用排序
1.先将nums排序,使其中的元素从小到达排序
这样我们有nums=[a<b<=b<c<d<=d<=d<e........]
接着我们可以使用for循环先找出第一个娃a,接下来两娃的寻找就可以会到双指针问题.
在尾部放一个指针 k ,那首指针 j 放哪才好,放到娃a的后一位,
因为娃使用for循环时是从列表首位置开始
将首指针 j 放到娃a的后一位,这样指针 j 就可以跟着娃a的寻找移动
这时我们得到a+b+c=0,当a,b固定时我们看k指针,因为nums已从小到达排序,ab向右移动时a,b均增大a1 > a, b1 > b,因为a1+b1+c1=0,所以有c1 < c,k指针往左移,则k-1
当指针 j , k ,重合后遍历完可推出循环
def findChild(nums):
nums.sort()
result_list = list()
for i in range(len(nums)): # 选娃a
if i > 0 and nums[i] == nums[i - 1]: # 需要和上一次的娃不相同
continue # 如果相同选下一个
k = len(nums) - 1 # k指针初始指向数组末尾
# 选娃b
for j in range(i + 1, len(nums)):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
while j < k and nums[j] + nums[k] > -nums[i]:# 需要限制 b 的指针在 c 的指针的左侧
k -= 1 # 等同于nums[j] + nums[k] + nums[i] > 0
if j == k: # 如果指针重合退出循环
break
if nums[j] + nums[k] == -nums[i]: # 等同于nums[j] + nums[k] + nums[i] == 0
result_list.append([nums[i], nums[j], nums[k]])
return result_list
花费的时间约为O(n^2),n为列表长度,主要为两个for循环的开销.比暴力解题快了不少.
有一次寻娃结束
---------------------------------------------------------end------------------------------------------------------------------