本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是LeetCode的26篇文章,我们来实战一下全排列问题。

在之前的文章当中,我们讲过八皇后、回溯法,也提到了全排列,但是毕竟没有真正写过。今天的LeetCode46题正是让我们生成给定元素的全排列。

题意很简单,只有一句话,给定一个没有重复元素的序列,让我们返回这个序列所有的全排列,并且我们不需要考虑这些排列的顺序。

回溯法

我们在之前的文章当中分析过,全排列问题,可以看成是搜索问题,从而近似成八皇后问题。在八皇后问题当中,我们枚举的是棋盘的每一行当中的皇后放置的位置,而全排列其实也一样,我们要枚举每一个元素放置的位置。不过八皇后当中要求皇后除了不能同行同列之外还不能同对角线,而我们排列元素可以忽略这个要求。也就是说我们把每一行皇后放置的列号看成是每个元素摆放的位置,并且忽略同对角线的限制的话,那么八皇后问题和全排列问题就完全一样了。

如果还不理解,可以参考一下下图,我们给皇后编号,把皇后同样看成是序列当中的元素,那么八皇后的摆放位置刚好可以映射成一种排列。映射的方式非常简单,就是我们忽略行的信息,依次记录下皇后摆放的列号。

如果你能想通这两个看似完全不同的问题当中的相似之处,说明你对搜索问题的理解已经有些入门了。

思路清楚了,总之我们要枚举皇后摆放的状态。你可以按顺序遍历位置,然后枚举各个位置上放置的皇后,也可以顺序遍历皇后,枚举当前皇后可以放置的位置。两者是等价的,你可以根据自己的理解进行操作。

一般来说我喜欢遍历位置,枚举皇后。因为会引起冲突的是皇后,而不是位置。我们往往要判断皇后之间的关系以及皇后的状态,所以我们枚举皇后会比较贴合思路

所以我们把之前八皇后的代码拿过来稍作修改即可,为了放置一个皇后重复放置在多个位置,我们需要存储皇后的状态,即有没有放置过。一般竞赛当中这种标记的变量称为flag,如果标记多个那就是flag数组。更多细节我们来看代码:

class Solution:
    def dfs(self, nums, n, i, cur, ret, flag):
        if i == n:
            ret.append(cur.copy())
            return
        for p in range(n):
            # 遍历所有元素
            # 如果p元素已经放置过了,跳过
            if flag[p]:
                continue
            # 当前位置放置p
            cur.append(nums[p])
            # flag[p]置为True
            flag[p] = True
            # 递归
            self.dfs(nums, n, i+1, cur, ret, flag)
            # 回溯
            cur.pop()
            flag[p] = False
        
    def permute(self, nums: List[int]) -> List[List[int]]:
        ret = []
        n = len(nums)
        # 记录元素i有没有放置过
        flag = [False for _ in range(n)]
        self.dfs(nums, n, 0, [], ret, flag)
        return ret

代码很短,细节也不多,只要理解了我们是按照顺序遍历位置,然后对于每一个位置遍历可以放置的元素,然后递归回溯即可。基本上可以说是模板题,如果理解有难度的话,可以看一下之前详解八皇后问题的文章:

LeetCode 31:递归、回溯、八皇后、全排列一篇文章全讲清楚

其他方法

回溯法是这个问题的标准解法,那么这题还有没有其他方法呢?

其实是有的,也不难,在LeetCode31题的文章,也就是上面那个链接的文章当中我们解决了一个叫做下一个排列的问题。在这道题当中,我们给定一个序列,要求返回在它所有的全排列当中刚好字典序比它大1的排列,这个方法称为next_permutation。

关于next_permutation的计算方法也在链接里,如果有忘记的或者是最近关注的可以点下链接回顾一下,计算方法是完全一样的,我就不再重复了。

LeetCode 31:递归、回溯、八皇后、全排列一篇文章全讲清楚

如果还记得这道题的话就好办了,我们使用它很容易解出当前的问题。因为我们只需要获得给定序列的最小排列,然后不停地调用这个方法就好了,直到没有更大的序列退出即可。从最小的序列一直获取到最大的,当然就是全排列了。

在LeetCode31题当中,这是一个inplace的方法,没有返回值。并且当序列达到最大的时候,会自动再从最小的开始。我们需要稍稍修改一下,加上一个返回值,表示当前的序列是否是最大的。如果序列达到最大,说明我们可以不用继续往下寻找了,我们return一个True,表示可以退出了,否则我们return False,表示还有其他结果。

本质上我们是从最小的排列开始,不停地用一个叫做get_next的方法获取比当前序列大的下一个序列,当没有更大的序列的时候,说明我们已经获得了所有的排列,那么直接返回结果即可。如果忽略get_next当中的逻辑,这个代码其实只有几行:

其实这是一个取巧的办法,利用之前的思路我们完全不用思考,几乎可以无脑得到答案。但是从另外一个角度来说,这也是算法的魅力,毕竟通往终点的路往往不止一条。

最后我们来看下代码,如果你不懂怎么算next_permutation光看注释是很难看懂的,划到上面的链接看看吧。

class Solution:
    def get_next(self, nums: List[int]):
        """
        Do not return anything, modify nums in-place instead.
        """
        # 长度
        n = len(nums)
        # 记录图中i-1的位置
        pos = n - 1
        for i in range(n-1, 0, -1):
            # 如果降序破坏,说明找到了i
            if nums[i] > nums[i-1]:
                pos = i-1
                break
                
        for i in range(n-1, pos, -1):
            # 从最后开始找大于pos位置的
            if nums[i] > nums[pos]:
                # 先交换元素,在进行翻转
                nums[i], nums[pos] = nums[pos], nums[i]
                # 翻转[pos+1, n]区间
                nums[pos+1:] = nums[n:pos:-1]
                return False
        return True
        
        
    def permute(self, nums: List[int]) -> List[List[int]]:
        ret = []
        # 从小到大排序,获得最小排列
        nums = sorted(nums)
        ret.append(nums.copy())
        # 如果还有下一个排列则继续调用
        while not self.get_next(nums):
            # 要.copy()是因为Python中存储的引用,如果不加copy
            # 会导致当nums发生变化之后,ret中存储的数据也会变化
            ret.append(nums.copy())
        return ret

今天的问题并不难,只是Medium难度,并且题目的题意还是之前见过的,主要是给大家加深一下回溯算法的映像用的,没什么太多的新内容。

文章的内容就是这些,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。

LeetCode46 回溯算法求全排列,这次是真全排列的更多相关文章

  1. Johnson-Trotter(JT)算法求全排列

    Johnson-Trotter算法描述 算法 JohnsonTrotter(n) //实现用来生成排序的 Johnson-Trotter 算法 //输入:正整数n(代表序列1,2,···,n) //输 ...

  2. LeetCode通关:连刷十四题,回溯算法完全攻略

    刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...

  3. 3、回溯算法解题套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  4. 46. Permutations 回溯算法

    https://leetcode.com/problems/permutations/ 求数列的所有排列组合.思路很清晰,将后面每一个元素依次同第一个元素交换,然后递归求接下来的(n-1)个元素的全排 ...

  5. 回溯算法-C#语言解决八皇后问题的写法与优化

    结合问题说方案,首先先说问题: 八皇后问题:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 嗯,这个问题已经被使用各种语言解 ...

  6. 求全排列Permutation

    是在教材(<计算机算法设计与分析(第4版)>王晓东 编著)上看见的关于求全排列的算法: 我们可以看一下书上怎么写的: #include<bits/stdc++.h> using ...

  7. 8皇后问题SQL求解(回溯算法)

    问题 八皇后问题是一个古老而著名的问题,是回溯算法的典型例题.该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一 ...

  8. LeetCode37 使用回溯算法实现解数独,详解剪枝优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 数独是一个老少咸宜的益智游戏,一直有很多拥趸.但是有没有想过,数独游戏是怎么创造出来的呢?当然我们可以每一关都人工设置,但是显然这工作量非常 ...

  9. C#LeetCode刷题-回溯算法

    回溯算法篇 # 题名 刷题 通过率 难度 10 正则表达式匹配   18.8% 困难 17 电话号码的字母组合   43.8% 中等 22 括号生成   64.9% 中等 37 解数独   45.8% ...

随机推荐

  1. Java常见的类——Number和Math类

    但我们使用数据时,我们一般使用内置数据类型,列如:byte,int,long,double等,但是在实际开发过程中,我们一般遇到的是需要使用对象,这时,我们就可以使用 Java 专门为每一个数据类型提 ...

  2. 7-7 jmu_python_是否是数 (10 分)

    本题要求从键盘输入一个字符串,判断该串是否属于整数.浮点数或者复数的表示 输入格式: 输入一个字符串 输出格式: 输出yes或no 输入样例: -299 输出样例: yes a = input() t ...

  3. Parcel上手——又一个打包工具

    Parcel是什么? 极速零配置Web应用打包工具 说到打包工具,大多人应该都用过Webpack,Parcel也是这一类工具. Parcel相比Webpack有什么优势? 配置简单 打包速度快 以下是 ...

  4. moment太重? 那就试试miment--一个超轻量级的js时间库

    介绍 Miment 是一个轻量级的时间库(打包压缩后只有1K),没有太多的方法,Miment的设计理念就是让你以几乎为零的成本快速上手,无需一遍一遍的撸文档 由来 首先 致敬一下Moment,非常好用 ...

  5. Spring配置cache(concurrentHashMap,guava cache、redis实现)附源码

    在应用程序中,数据一般是存在数据库中(磁盘介质),对于某些被频繁访问的数据,如果每次都访问数据库,不仅涉及到网络io,还受到数据库查询的影响:而目前通常会将频繁使用,并且不经常改变的数据放入缓存中,从 ...

  6. 网络编程概念 和OSI七层结构简介

    什么是网络编程 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 编写基于网络的应用程序的过程序称之为网络编程 学习网络编程就是要学习利用网络来与另一台计算机相互传输数据 ...

  7. seo搜索优化教程10-黑帽SEO

    为了使大家更方便的了解及学习网络营销推广.seo搜索优化,星辉科技强势推出seo搜索优化教程.此为seo教程第十课 学习黑帽SEO并不是教大家如何作弊,而是想让大家避免使用黑帽SEO手法,从而导致被搜 ...

  8. 开发RTSP 直播软件 H264 AAC 编码

    上一篇对摄像头预览,拍照做了大概的介绍,现在已经可以拿到视频帧了,在加上 RTSP 实现,就是直播的雏形,当然还要加上一些 WEB 管理和手机平台的支援,就是一整套直播软件. 介绍一些基础概念:RTP ...

  9. Oracle 11.2 RAC on Redhat 6.5 安装最佳实践

    本文讲述了在Redhat 6.5 上安装Oracle 11.2 RAC的详细步骤,是一篇step by step指南,全文没有什么技术难度,只要一步步跟着做就一定能安装成功. 环境介绍 分类 项目 说 ...

  10. golang bufio.NewScarme

    // dup1 输出标准输入中出现次数大于1的行,前面是次数 package main import ( "bufio" "fmt" "os" ...