python 回溯法、字典序生成、递归交换 实现全排列【力扣46题】

题目描述

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

输入格式
  • 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]]

方法一:回溯算法

解题步骤
  1. 初始化:创建一个空列表 res 来存储所有排列结果。
  2. 递归函数:编写一个递归函数,使用当前序列 path 和选择列表 nums 来生成排列。
  3. 结束条件:如果 path 的长度等于 nums 的长度,则将 path 添加到结果列表 res 中。
  4. 循环选择:遍历 nums 中的每个数字,如果数字未被选择,则加入到 path,并继续递归,最后撤销选择。
完整代码
def permute(nums):
    """
    使用回溯算法生成数组的全排列
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的全排列
    """
    def backtrack(path, options):
        # 如果当前路径的长度等于输入数组的长度,说明找到了一种排列
        if len(path) == len(nums):
            res.append(path[:])
            return
        
        # 遍历当前可选择的数字
        for i in range(len(options)):
            # 选择当前数字
            path.append(options[i])
            # 继续递归填下一个数
            backtrack(path, options[:i] + options[i+1:])
            # 撤销选择
            path.pop()

    res = []
    backtrack([], nums)
    return res

# 示例调用
print(permute([1, 2, 3]))  # 输出: [[1, 2, 3], [1, 3, 2], ..., [3, 2, 1]]
算法分析
  • 时间复杂度:(O(n! \times n)),生成所有排列需要 (n!) 次调用,每次调用处理列表需要 (O(n)) 时间。
  • 空间复杂度:(O(n)),递归深度为 (n),使用了额外的空间存储当前排列。

方法二:字典序生成算法

解题步骤
  1. 排序:先将数组排序。
  2. 查找:从右向左查找第一个升序的相邻元素对 (i, j),即 nums[i] < nums[j]
  3. 交换与反转:如果找到,再从右向左查找第一个大于 nums[i] 的元素 nums[k],交换 nums[i]nums[k],然后反转 i+1 到末尾的元素。
  4. 重复:重复以上步骤直到完全逆序。
完整代码
def permute(nums):
    """
    使用字典序算法生成数组的全排列
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的全排列
    """
    def next_permutation(perm):
        # 找到升序的边界
        i = len(perm) - 2
        while i >= 0 and perm[i] >= perm[i + 1]:
            i -= 1
        if i == -1:
            return False
        
        # 找到需要交换的位置
        j = len(perm) - 1        while perm[j] <= perm[i]:
            j -= 1
        perm[i], perm[j] = perm[j], perm[i]
        
        # 反转i后面的部分
        perm[i + 1:] = perm[i + 1:][::-1]
        return True
    
    nums.sort()
    res = [nums[:]]
    while next_permutation(nums):
        res.append(nums[:])
    return res

# 示例调用
print(permute([1, 2, 3]))  # 输出: [[1, 2, 3], [1, 3, 2], ..., [3, 2, 1]]
算法分析
  • 时间复杂度:(O(n! ✖️ n)),尽管每次生成下一个排列只需要 (O(n)) 时间,但总共有 (n!) 个排列。
  • 空间复杂度:(O(1)),除了输出数组外,空间使用是常数。

方法三:递归交换

解题步骤
  1. 递归函数定义:定义一个递归函数,使用当前索引 start 来处理排列。
  2. 递归终止条件:如果 start 等于数组长度,记录当前排列。
  3. 递归交换:从 start 开始,将每个元素交换到 start 位置,然后递归处理 start + 1
完整代码
def permute(nums):
    """
    使用递归交换方法生成数组的全排列
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的全排列
    """
    def backtrack(start):
        if start == len(nums):
            res.append(nums[:])
            return
        for i in range(start, len(nums)):
            nums[start], nums[i] = nums[i], nums[start]
            backtrack(start + 1)
            nums[start], nums[i] = nums[i], nums[start]

    res = []
    backtrack(0)
    return res

# 示例调用
print(permute([1, 2, 3]))  # 输出: [[1, 2, 3], [1, 3, 2], ..., [3, 2, 1]]
算法分析
  • 时间复杂度:(O(n! ✖️ n)),需要进行 (n!) 次调用,每次调用中进行的操作与 (n) 成正比。
  • 空间复杂度:(O(n)),递归深度最大为 (n),使用了额外的空间存储当前排列状态。

总结

为了直观地比较解决LeetCode题目46 "全排列"的三种方法,下面的表格将展示它们在不同技术维度的性能和特点,包括时间复杂度、空间复杂度以及各自的优势和劣势。

特征方法一:回溯算法方法二:字典序算法方法三:递归交换法
时间复杂度(O(n! \times n))(O(n! \times n))(O(n! \times n))
空间复杂度(O(n))(O(1))(O(n))
优势- 灵活且直观
- 易于实现和理解
- 非递归,避免栈溢出
- 效率高于普通回溯
- 实现简单
- 直接在输入数组上操作
劣势- 空间消耗较大
- 深度递归可能导致栈溢出
- 实现复杂度较高
- 不如回溯直观
- 多次交换操作复杂
- 递归依旧可能栈溢出
适用场景- 需要所有可能解时
- 教学和基础算法学习
- 需要按字典顺序生成全排列
- 大规模数据处理
- 内存使用敏感
- 喜欢简洁代码风格

综合分析

方法一(回溯算法)

  • 回溯算法是解决排列、组合问题的经典方法,非常直观,可以灵活地处理各种约束条件。其主要缺点是在深度递归时可能会消耗较多内存,特别是在解空间非常大时。

方法二(字典序算法)

  • 字典序算法提供了一种非递归的解决方案,能够有效地按照字典顺序生成下一个排列,适用于需要顺序输出或者大数据量的问题。然而,这种方法在实现上较为复杂,可能不太直观。

方法三(递归交换法)

  • 递归交换法通过直接在输入数组上进行操作,避免了额外的空间使用,简化了代码的复杂性。但与回溯类似,它的缺点是递归深度大时仍可能导致栈溢出问题,且多次交换操作可能相对复杂。

在选择合适的方法时,可以根据具体问题的需求、可用资源以及开发时间来做决策。例如,对于教学或基础学习,推荐使用回溯算法;对于工业级应用,考虑字典序算法;而对于内存敏感或喜欢简洁代码的场景,递归交换法可能是一个好选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559349.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用Python进行大规模数据处理

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行大规模数据处理&#xff1a;Hadoop与Spark的对比 随着数据量的不断增长&…

web网站搭建实验

综合练习&#xff1a;请给openlab搭建web网站 网站需求&#xff1a; 1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料 和缴费网站&#xff0c;基于&#xff0c;www.openlab.com/data网站…

Android RecyclerView的LayoutManager配置

RecyclerView的item布局方式依赖于其配置的布局管理器。不同的布局管理器可以实现不同的界面效果。 LayoutManager介绍 RecyclerView可以通过setLayoutManager设置布局管理器&#xff0c;该方法的源码如下&#xff1a; /*** Set the {link LayoutManager} that this RecyclerV…

JAVA 项目<果园之窗>_1

这几天有空看能不能把水果店管理系统整出来&#xff0c;目标是整个网页版本的&#xff0c;以我的电脑做服务器&#xff0c;数据存在mysql中 以我目前的理解整个项目大致可分为前端部分、后端部分、数据库部分&#xff0c;也就这三个部分 目前打开并运行了一个别人的项目&#…

Linux下SPI设备驱动实验:验证读写SPI设备中数据的函数功能

一. 简介 前面文章实现了 SPI设备驱动框架&#xff0c;并在此基础上添加了字符设备驱动框架&#xff0c;实现了读 / 写SPI设备中数据的函数&#xff0c;文章如下&#xff1a; Linux下SPI设备驱动实验&#xff1a;向SPI驱动框架中加入字符设备驱动框架代码-CSDN博客 Linux下…

2024年学浪的缓存怎么导出来

在自我成长的道路上&#xff0c;越来越多的朋友选择通过精选课程来提升自己。然而&#xff0c;面对那些服务期限有限的课程&#xff0c;怎样才能把握住知识的光芒&#xff0c;让它照亮未来的每一个角落&#xff1f;本文就教大家如何利用工具下载学浪app平台的课程 工具我已经打…

【ARM 裸机】I.MX 启动方式之启动头文件 2

接上一节&#xff1a;【ARM 裸机】I.MX 启动方式之启动头文件 1&#xff1b; 2.3、DCD DCD&#xff0c;Device Configuration Data &#xff0c;就是配置 6ULL 寄存器的&#xff0c;DCD 数据最大限制 1768 字节&#xff1b; CCGR0 是不是很熟悉&#xff1f;对&#xff0c;在…

C++奇迹之旅:深入理解赋值运算符重载

文章目录 &#x1f4dd;赋值运算符重载&#x1f320; 运算符重载&#x1f309;特性 &#x1f320; 赋值运算符重载&#x1f320;传值返回&#xff1a;&#x1f320;传引用赋值&#xff1a;&#x1f309;两种返回选择&#x1f309;赋值运算符只能重载成类的成员函数不能重载成全…

用Gold-yolo模块改进yolov8模型

gold-yolo论文&#xff1a; https://arxiv.org/pdf/2309.11331.pdf gold-yolo代码&#xff1a; https://github.com/huawei-noah/Efficient-Computing/tree/master/Detection/Gold-YOLO 一 gold模块简介 Gold-Yolo是华为诺亚方舟实验室2023年发布的工作&#xff0c;主要优化检…

Linux程序的地址空间,进程终止

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 一.程序的地址空间 1.1程序的地址空间的引入 我们知道frok可以创建…

重塑我们对随机性在计算中的作用的理解

2023年图灵奖&#xff0c;最近刚刚颁给普林斯顿数学教授 Avi Wigderson&#xff01;作为理论计算机科学领域的领军人物&#xff0c;他对于理解计算中的随机性和伪随机性的作用&#xff0c;作出了开创性贡献。 Avi Wigderson 的履历 自 1999 年以来&#xff0c;Wigderson 一直担…

Python五子棋VS人机对战

上一次编写了一个python五子棋游戏,但是属于玩家之间的对战。今天介绍五子棋和人机对战。本博文目的是教学和一些毕业设计。 目前电脑下棋逻辑算法还是比较简单的,不能和市面上五子棋相提并论,请大家理想对待! 代码: import pygame import sys import tkinter as tk fro…

再拓信创版图-Smartbi Insight V11与东方国信CirroData数据库完成兼容适配认证

近日&#xff0c;思迈特商业智能与数据分析软件 [简称&#xff1a;Smartbi Insight] V11与北京东方国信科技股份有限公司 &#xff08;以下简称东方国信&#xff09;CirroData-OLAP分布式数据库V2.14.1完成兼容性测试。经双方严格测试&#xff0c;两款产品能够达到通用兼容性要…

python语言零基础入门——注释、print()函数、input()函数

目录 一、注释 1.块注释 2.行内注释 3.多行注释 二、打印变量 1.print()函数&#xff1a;输出/打印指定内容 2.input()函数&#xff1a;输入指定内容 三、编程题&#xff1a;个人名片 一、注释 1.块注释 以#开始&#xff0c;直到本行结束都是注释为了保证代码的可读性…

初步学习node.js文件模块

环境已安装好&#xff1b; 写一个read1.js如下&#xff1b; var fs require("fs"); var data ;// 创建一个流 var stream1 fs.createReadStream(test1.jsp); stream1.setEncoding(UTF8);// 绑定data事件 stream1.on(data, function(mydata) {data mydata; });/…

Unity ECS

一&#xff1a;前言 ECS与OOP不同&#xff0c;ECS是组合编程&#xff0c;而OOP的理念是继承 E表示Entity&#xff0c;每个Entity都是一个有唯一id的实体。C表示Component&#xff0c;内部只有属性&#xff0c;例如位置、速度、生命值等。S表示System&#xff0c;驱动实体的行为…

Leetcode. 12 整数转罗马数字

罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例…

原来我一直被骗了!Burp suite诱导劫持攻击【附工具】

一、点击劫持 点击劫持是一种基于界面的攻击&#xff0c;用户通过点击诱饵网站中的一些其他内容被诱骗点击隐藏网站上的可操作内容。举例来说&#xff0c;一个网络用户可能会访问一个诱饵网站&#xff08;可能是通过电子邮件提供的链接&#xff09;&#xff0c;并点击一个按钮以…

C语言---贪吃蛇(二)---逻辑和代码的实现

文章目录 前言1.准备工作2.蛇的相关属性3.游戏流程设计3.1.游戏开始(GameStart)3.1.1.设置光标位置3.1.2.隐藏光标3.1.3.打印欢迎界面3.1.4.创建地图3.1.5.初始化蛇身3.1.6.创建食物 3.2.游戏运行(GameRun)3.2.1.打印信息栏3.2.2.蛇身的移动3.2.2.1.判断下一个结点是否为食物3.…

【Linux】iptables的应用

iptables 防火墙 防火墙是一种网络安全系统&#xff0c;它位于内部网络与外部网络&#xff08;如互联网&#xff09;之间&#xff0c;通过实施预定义的安全策略来控制网络间的通信。防火墙的主要目标是保护内部网络资源免受未经授权的访问、攻击或潜在威胁&#xff0c;同时允…
最新文章