题目:

给定一个池塘里面有众多小蝌蚪(整数数组 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]] ------->重复



 运行情况

python母亲节colorama python母亲节_算法

根据暴力解题法,什么叫做暴力?,就是简单粗暴,万事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的寻找移动

python母亲节colorama python母亲节_for循环_02

 这时我们得到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循环的开销.比暴力解题快了不少.

有一次寻娃结束