Leetcode-D43-数组-40. 组合总和 II&47. 全排列 II

本文介绍了如何使用回溯算法解决全排列和组合总和问题,详细阐述了解决39、40、47题的过程。在全排列问题中,通过递归记录路径实现细节记忆;在组合总和问题中,避免重复选择并进行剪枝操作。文章还探讨了处理重复元素的方法,以及在全排列II问题中如何处理竖向重复。在实现过程中,注意排序、剪枝和处理重复元素的关键步骤,以提高效率并得到正确结果。
摘要由CSDN通过智能技术生成

一、复习

先复习一下昨天写的回溯问题,从全排列这个简单的开始。
46、 全排列
其实这种回溯挺好玩的,不同于动态规划,他可以记住answer的细节,并且都在路径上(我们用不断递归path来记录这个值,最后把最终答案append到res中)。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(path,res,nums):
            if len(nums)==0:
                res.append(path)
                return
            for i in nums:
                tmp_nums=[]
                for j in nums:
                    if j!=i:
                        tmp_nums.append(j)
                dfs(path+[i],res,tmp_nums)
            
        path=[]
        res=[]
        dfs(path,res,nums)
        return res

                

在这里插入图片描述

39、 组合总和

自己写出来了!好棒啊!
就是有三个问题:
(1)定义递归函数后,调用函数,不要马虎写在函数里面
(2)为了避免不和前面的序列重复,前面选过的,在后面就不能选了,因此设置了index,每一个分支只能从自己对应的index开始选。即最左边都可以选,再网游一个,就不能选第一个了;不能选第二个,逐渐缩小范围。因为不分顺序,所以最左边像一个全集,而最右边就像一个只有一个元素的集合。这样分支肯定不是所有可能情况,比如说最小的或者中等的单独一个的集合,但是这种集合其实在前面也讨论可能性了,前面并不排除最小和中等独当一面。
(3)为了避免过于复杂,涉及到剪枝,所以不要忘记排序!

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def dfs(index,size,candidates,res,path,target):
            for i in range(index,size):
                residue = target-candidates[i]
                if residue<0:
                    break
                elif residue==0:
                    path+=[candidates[i]]
                    res.append(path)
                    return
                else:
                    dfs(i,size,candidates,res,path+[candidates[i]],residue)

        path=[]
        res=[]
        candidates.sort()
        dfs(0,len(candidates),candidates,res,path,target)
        return res

40. 组合总和 II

1、刚开始又忘记要sort()了,但是还有一个问题,重复的被我省略了

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def dfs(index,size,res,path,target,candidates):
            if index==size:
                return
            for i in range(index,size):
                if i!=0 and candidates[i]==candidates[i-1]:
                    continue
                residue = target-candidates[i]
                if residue<0:
                    break
                elif residue==0:
                    res.append(path+[candidates[i]])
                    return
                else:
                    dfs(i+1,size,res,path+[candidates[i]],residue,candidates)
        res =[]
        path = []
        candidates.sort()
        dfs(0,len(candidates),res,path,target,candidates)
        return res

在这里插入图片描述

注释掉又出现重复的问题
在这里插入图片描述

2、加入了一句判断:看这个数是否在路径中已经存在。因为我们允许竖着重复,不允许横着重复

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def dfs(index,size,res,path,target,candidates):
            if index==size:
                return
            for i in range(index,size):
                if i!=0 and candidates[i]==candidates[i-1] and candidates[i] not in path:
                    continue
                residue = target-candidates[i]
                if residue<0:
                    break
                elif residue==0:
                    res.append(path+[candidates[i]])
                    return
                else:
                    dfs(i+1,size,res,path+[candidates[i]],residue,candidates)
        res =[]
        path = []
        candidates.sort()
        dfs(0,len(candidates),res,path,target,candidates)
        return res

3、直接去重超时了,看看答案叭
高赞给的方法是这样的,直呼绝绝子:
if i>index and candidates[i]==candidates[i-1]:
如果不是一行中的第一个(i>index)并且还有等于前面的数值,即前面已经讨论过,就忽略了。如果是一行中的第一个,等于前面的数值,相当于是下一层分支的情况,可以保留。用是否为一层的第一个来区分,真的绝绝子,而这个判断条件为i>index。
在这里插入图片描述

三、47. 全排列 II

和40题相似,比40还要简单一些,也是横着不能要(结果重复了),但是竖着可以要(因为数组中包含了重复的数值,这些重复的数值,可以被使用相应的次数)

又忘记sort了。如果不sort,就无法有效排除那些重复的组合,因为那些重复的组合就不相邻,那判断就无效了。

这道题比较烦人的还是数组的删除元素。如果直接删除的话,nums没遍历完成就没元素了,所以要先深拷贝出来。注意:删除后,整体的size会变,当size=0时,就添加路径,并返回。

这题判断横着重复还是竖着重复和上题相似。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def dsf(size, path, res, nums):
            for index in range(size):
                if index >0 and nums[index] == nums[index - 1]:
                    continue
                else:
                    tmp_num = []
                    for i in nums:
                        tmp_num.append(i)
                    new_path = tmp_num[index]
                    tmp_num.pop(index)
                    size = len(tmp_num)
                    if size==0:
                        res.append(path+[new_path])
                        return
                    dsf(size, path + [new_path], res, tmp_num)

        path = []
        res = []
        nums.sort()
        dsf(len(nums), path, res, nums)
        return res


在这里插入图片描述

3、看下别人怎么删除列表中的元素的。
用的是这种切片nums[:i]+nums[i+1:],开辟了新的内存空间

在这里插入图片描述
在这里插入图片描述

把自己的改成切片居然超时了,我不理解。奥,删除操作没成功,应该从index+1开始。【小声:好智障啊】

在这里插入图片描述

成功了!用切片实现了删除操作。但我感觉这道题的思路还不清晰,毕竟前面磕磕绊绊的~鹅鹅鹅,明天再做一遍,整理一下思路。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def dsf(size, path, res, nums):
            for index in range(size):
                if index >0 and nums[index] == nums[index - 1]:
                    continue
                else:
                    new_path = nums[index]
                    tmp_num =nums[:index]+nums[index+1:]
                    size = len(tmp_num)
                    if size==0:
                        res.append(path+[new_path])
                        return
                    dsf(size, path + [new_path], res, tmp_num)

        path = []
        res = []
        nums.sort()
        dsf(len(nums), path, res, nums)
        return res


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值