• leetcode(力扣) 46. 全排列(回溯)


    46.全排列

    题目描述

    给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

    示例 1:
    输入:nums = [1,2,3]
    输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

    示例 2:
    输入:nums = [0,1]
    输出:[[0,1],[1,0]]

    示例 3:
    输入:nums = [1]
    输出:[[1]]

    思路分析

    又是一道经典回溯题,不过回溯类型和之前的有所区别,之前做的组合 和子集,今天这个是排列问题。
    组合和排列的区别,排列里可以有[1,2],[2,1]这种,但是组合的话 这两种算一种。
    所以在排列问题里,就不需要组合和子集里的startindex了。

    老规矩, 回溯三步走;

    1.确定函数参数:
    这题没参数~~~~~哈哈

    2.确定结束条件
    画个树就能看出来,假设收集路径上的点的数组为path,答案集为res,则当res和path的长度一样时,代表达到叶子节点,也就是答案节点。

    3.循环体

    这道题就这里和之前不一样了,没有了startindex,循环直接从0到len(nums)。

    题目中所给的nums里不包含重复元素,所以不需要考虑这个了。

    那么写出来的代码就是这样的:

    for i in range(len(nums)):      
         path.append(nums[i])
         backtrack()
         path.pop()
    
    • 1
    • 2
    • 3
    • 4

    有没有什么问题呢,跑一下,果然有问题。因为每次都是从0开始,按照套路都知道,for是控制每一层的循环取值的,当进入循环递归到下一层的时候,i还是从0开始,会造成什么现象呢 ,就是出现[1,1,1]这样的结果,因为每次都是从0开始啊。

    这时候如果找个startindex会出现什么情况呢,遍历到2的时候就是[2,3],而不会出现我们在排列里的[2,1,3] 这种组合了。因为startindex是告诉下一层遍历的起始点嘛。

    所以这里实际上相当于舍弃startindex,转而加入了一种去重机制。

    很简单,直接判断当前遍历的元素是否在记录的path中。如果在就不考虑这个元素了。 比如刚才的[1,1,1] 在不用startindex的循环中,每一层的每一次都会考虑所有的nums= [1,2,3]里的数值,第一次取1,下一层依旧从[1,2,3]里选,如果没有去重,则又会取1,加入去重之后选择了2,然后选3,正好可以达到目的。

    完整代码

    class Solution:
        def permute(self, nums: List[int]) -> List[List[int]]:
                res = []
                path = []
                def backtrack():
                    if len(path) == len(nums):
                        res.append(path[:])
                        return
                    for i in range(len(nums)):
                        if nums[i] in path:
                            continue
                        path.append(nums[i])
                        backtrack()
                        path.pop()
                backtrack()
                return  res
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    47.全排列 II

    做这个题之前先做 40. 组合总和 II,这两道题的去重思路基本一样。

    思路分析:

    这道题在上一道的基础上做了一些更改,这道题所给的数组中有重复的数值了。

    上一道题中所给的数组没有重复的值,所以使用非常简单的方法,直接判断了一下当前遍历的元素在path数组里有没有,没有的话再继续操作,而这道题没法用这个方法了,比如[1,1,2]这里有两个1,但是个合法的答案,因为这两个1是不同的元素,只是值相等。

    所以面临两个问题。

    • 一个是要在答案内去重。 不同位置但值相同的元素 不算重复元素。
    • 一个是在答案间去重。

    这里的去重思路和之前的组合总和2的去重思路差不多。

    用一个temp数组记录当前遍历的值。
    比如 temp = [False,False,False] nums = [1,1,2]
    当遍历0号元素时, temp[0] = True ,那么在该0号元素的下面分支中(纵向),temp[0]都会是True,取0号元素之后的下一层取1号元素 那么temp[1] = True。
    此时 temp = [True,True,False]。

    这样的话就可以控制单个答案内不取重复元素了,取该元素之前只需要判断一下对应位置的temp是不是为True就行了。

    接下来就是答案间的去重了,其实和之前组合总和2里判断startindex的思路一样。

    答案间就意味着树切换分支了,在程序开始前先对所给数组排序,因为是排列问题,所以排序之后也没事,如果在遍历的时候检查到树切换分支了,那就意味着是另一个答案了,毕竟一个分支一个叶子节点,一个叶子节点一个答案。

    如何判断是否切换分支了?
    看temp数组,如果temp[i-1] = False 而temp[i] = True 则肯定是切换分支了,因为同一个分支下temp[i] 肯定也是True,这里不理解画个图就懂了。

    所以对于排序后的数组,如果当前值和前一个值相同,则两种可能,

    • 有可能是同一个分支的下层,也就是单个答案内的重复值,但不同的位置,此时不做处理。
    • 另一种可能就是切换了分支了,是答案间的结果,此时需要去重。

    两个去重放到一起就是:

    if not temp[i]:  # 单个答案内去重
          if i>0 and nums[i] == nums[i-1] and not temp[i-1]: # 答案间去重
    
    • 1
    • 2

    完整代码

    class Solution:
        def permuteUnique(self, nums: List[int]) -> List[List[int]]:
                # res用来存放结果
                res = []
                temp = [False] * len(nums)
                path = []
                nums.sort()
                def backtrack():
                    # 终止条件
                    if len(path) == len(nums):
                        res.append(path[:])
                        return
                    for i in range(len(nums)):
                        if not temp[i]:
                            if i>0 and nums[i] == nums[i-1] and not temp[i-1]:
                                continue
                            temp[i] = True
                            path.append(nums[i])
                            backtrack()
                            path.pop()
                            temp[i] = False
                backtrack()
                return res
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    《精通特征工程》学习笔记(5):数据(特征)降维
    2023-09-30 关于知识付费的思考与实践
    8.12 Day40---Spring&SpringMVC&SpringBoot面试题
    QML相关bug记录
    Ubuntu18.04搭建K8s集群(MAC+VMWare)
    [021] [STM32] FSMC外设详解及模拟驱动LCD编程
    cocosCreator获取手机剪切板内容
    ASEMI高压二极管CL08-RG210参数,CL08-RG210封装
    Git学习笔记(四)远程仓库
    react基础知识3
  • 原文地址:https://blog.csdn.net/qq_38737428/article/details/127575783