【回溯 - 1】46. 全排列

46. 全排列

难度:中等
力扣地址:https://leetcode.cn/problems/permutations/description/

在这里插入图片描述

问题描述

给定一个 不含重复数字 的数组 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]]

问题分析

如果这是一道数学题,我们用纸和笔很容易算出来;如果 nums 比较少,我们写多重循环也能算出来。

但现在我们不知道总共多少个数字,现在需要考虑所有可能的全排列结果。

为了方便理解,我们把这道问题修改为排座位的问题。请注意我们此处排座位不考虑任何其他状况,保证 绝对公平,如下图所示:

在这里插入图片描述
现在明确了我们要干的事情,我们需要考虑如何完成这份工作。换句话说,我们应该在何种情况下换座位,保证每位同学坐在某个位置的机会是相等的。

如上图中右边展开的简单树结构所示,总共3位同学的情况下,先确保其中一位同学的座位不变,然后让另外两位同学通过换座,保证公平。

那么现在总共 n 位同学,应该如何保证公平呢 ?

我们可以考虑使用递归的思想解决这个问题,即,先按住编号为 i 的同学座位不变,其他的人实现公平,然后将 编号为 i 的同学换下来,让其他人在 i 以前的位置上轮流坐。

那么应该如何使用代码表示呢 ?请结合下面的图片进行理解,图片左边是一个示意座次图,右边是代码内容,请结合代码内容换算整个流程。
在这里插入图片描述

代码实现

c++ 的实现:

class Solution {
public:
    void seatSet(vector<vector<int>>& results, vector<int>& nums, int start) {
        if (start == nums.size()) {
            // 已经排好座位,记录一下
            results.push_back(nums);
        } else {
            // 还有排好座位,我们给索引为 [start, nums.size()) 进行排座位
            // 注意公平,每个人坐某个位置的机会是均等的
            for (int i = start; i < nums.size(); i++) {
                // 暂时让 i 同学坐 start 的位置
                swap(nums[i], nums[start]);
                // 剩下的位置[start+1, nums.size()) 还没有排,递归继续排座位
                seatSet(results, nums, start + 1);
                // i 同学在 start 位置上的所有可能已经记录,所以需要把它换回原来位置
                swap(nums[i], nums[start]);
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> results;
        seatSet(results, nums, 0);
        return results;
    }
};

java 的实现:

public class Solution {


    private void seatSet(List<List<Integer>> results, int[] nums, int start) {
        if (start == nums.length) {
            // 已经排好座位,记录一下
            List<Integer> currentPermutation = new ArrayList<>();
            for (int num : nums) {
                currentPermutation.add(num);
            }
            results.add(currentPermutation);
        } else {
            // 还有排好座位,我们给索引为 [start, nums.size()) 进行排座位
            // 注意公平,每个人坐某个位置的机会是均等的
            for (int i = start; i < nums.length; i++) {
                // 暂时让 i 同学坐 start 的位置
                swap(nums, i, start);
                // 剩下的位置[start+1, nums.size()) 还没有排,递归继续排座位
                seatSet(results, nums, start + 1);
                // i 同学在 start 位置上的所有可能已经记录,所以需要把它换回原来位置
                swap(nums, i, start);
            }
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> results = new ArrayList<>();
        seatSet(results, nums, 0);
        return results;
    }

}

Python 实现

class Solution:
    def permute(self, nums):
        results = []
        self.seat_set(results, nums, 0)
        return results
    
    def seat_set(self, results, nums, start):
        if start == len(nums):
            # 已经排好座位,记录一下
            results.append(nums[:])  # 注意在 Python 中要使用 nums[:] 来复制列表
        else:
            # 还有排好座位,我们给索引为 [start, len(nums)) 进行排座位
            # 注意公平,每个人坐某个位置的机会是均等的
            for i in range(start, len(nums)):
                # 暂时让 i 同学坐 start 的位置
                nums[i], nums[start] = nums[start], nums[i]
                # 剩下的位置[start+1, len(nums)) 还没有排,递归继续排座位
                self.seat_set(results, nums, start + 1)
                # i 同学在 start 位置上的所有可能已经记录,所以需要把它换回原来位置
                nums[i], nums[start] = nums[start], nums[i]

  • 时间复杂度: O ( n × n ! ) O(n\times n!) O(n×n!),其中 n n n 为序列的长度。
  • 空间复杂度: O ( n ) O(n) O(n)

总结

回溯(Backtracking)是一种算法设计技术,用于逐步构建解决方案,并在发现当前路径不能通向有效解决方案时进行回退和修正。这里我希望把我在刷题过程中的思考过程,包括联想到排座位等等,记录下来,既便于今后回顾,温故知新,也希望能帮助到在这方面存在困惑的小伙伴们。

多谢支持 ~

Smileyan
2024.07.08 23:56

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

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

相关文章

ICMP隧道

后台私信找我获取工具 目录 ICMP隧道作用 ICMP隧道转发TCP上线MSF 开启服务端 生成后门木马 msf开启监听 开启客户端icmp隧道 执行后门木马&#xff0c;本地上线 ICMP隧道转发SOCKS上线MSF 开启服务端 生成后门木马 msf开启监听 开启客户端icmp隧道 ​执行后…

经常用借呗和花呗对征信有影响吗?

说起支付宝里的花呗和借呗&#xff0c;大伙儿肯定都不陌生&#xff0c;它们俩就像是支付宝里的信用贷款双胞胎&#xff0c;名字相近&#xff0c;性格却大相径庭。现在&#xff0c;这俩兄弟都乖乖地接入了央行的征信大家庭&#xff0c;你的每一次使用&#xff0c;都会被记录得清…

老师怎么快速发布成绩?

期末考试的钟声刚刚敲响&#xff0c;成绩单的发放却成了老师们的一大难题。每当期末成绩揭晓&#xff0c;老师们便要开始一项繁琐的任务——将每一份成绩单逐一私信给家长。这不仅耗费了大量的时间和精力&#xff0c;也让本就忙碌的期末工作变得更加繁重。然而&#xff0c;随着…

生产力工具|Endnote X9如何自动更新文件信息

一、以EndNote X9.2版本为例&#xff0c;打开EndNote文献管理软件。 二、在菜单栏找到“Edit→Preferences...”&#xff0c;点击打开&#xff0c;弹出一个“EndNote Preferences”窗口。 三、进行设置 在打开的窗口左侧选择“PDF Handing”&#xff0c;右边会出现自动导入文献…

SwiftUI知识点(二)

Animation import SwiftUIstruct AnimationBootcamp: View {State var isAnimation: Bool falsevar body: some View {VStack{Button("Button"){withAnimation(Animation.default//重复//autoreverses: true&#xff1a;A-B-A-B//false: A-B&#xff0c;A-B.repeat…

[图解]SysML和EA建模住宅安全系统-13-时间图

1 00:00:00,480 --> 00:00:02,280 首先&#xff0c;我们来看&#xff0c;图画在哪里 2 00:00:02,290 --> 00:00:04,380 这个图 3 00:00:04,390 --> 00:00:06,180 你看&#xff0c;它是描述&#xff0c;刚才讲的 4 00:00:06,190 --> 00:00:09,010 描述这个活动 …

STM32学习历程(day5)

EXTI外部中断 中断 中断就是在主程序运行过程中 出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;CPU会暂停当前的程序&#xff0c;去处理中断程序 处理完会返回被暂停的位置 继续运行原来的程序。 中断优先级 当有多个中断源同时申请中断时 CPU会根据…

设计模式之职责链模式(Chain of Responsibility Pattern)

1.概念 职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;&#xff1a;避免将请求发送者与接收者耦合在一起&#xff0c;让多个对象都有机会接收请求&#xff0c;将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理它为止…

单例模式(大话设计模式)C/C++版本

单例模式 C 饿汉 /* HM hungry man 饿汉 */ #include <iostream> using namespace std; class Singleton { private:Singleton() { cout << "单例对象创建&#xff01;" << endl; };Singleton(const Singleton &);Singleton &operator(c…

【ARMv8/v9 GIC 系列 2.4 -- GIC SGI 和 PPI 中断的启用配置】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC SGI 和 PPI 中断的使能配置GICR_ISENABLER0 操作使用举例SummaryGIC SGI 和 PPI 中断的使能配置 GICR_ISENABLER0寄存器(中断设置-使能寄存器0)用于启用相应的SGI(软件生成中断)或PPI(专用外设中断)向CPU接口的转发。每个…

Android多开应用软件系统设计

设计一个支持Android多开应用的软件系统&#xff0c;主要涉及到以下几个关键技术点和设计考虑&#xff1a; 1. 虚拟化技术 容器技术&#xff1a;与传统的虚拟机不同&#xff0c;可以采用更轻量级的容器技术&#xff0c;为每个应用实例创建独立的运行环境。这包括分配独立的用…

atcoder 357 F Two Sequence Queries (线段树板子)

题目&#xff1a; 分析&#xff1a; 线段树 代码&#xff1a; // Problem: F - Two Sequence Queries // Contest: AtCoder - SuntoryProgrammingContest2024&#xff08;AtCoder Beginner Contest 357&#xff09; // URL: https://atcoder.jp/contests/abc357/tasks/abc357_…

AI实时免费在线图片工具6:以图生相似图

1、以图生图&#xff0c;生成相似图 https://huggingface.co/spaces/diffusers/unofficial-SDXL-Turbo-i2i-t2i 间接实现&#xff1a;可以是图片先提取描述&#xff0c;再通过描述再去生成新图片 https://huggingface.co/spaces/gokaygokay/KolorsPlusPlus

JAVA基础-----128陷阱

一、何为128陷阱 Java中Integer类型在使用比较时的特殊行为------128陷阱&#xff0c;解释了当数值在-128到127范围内&#xff0c;由于valueOf方法的缓存机制导致地址相同&#xff0c;比较为真&#xff1b;超出这个范围则新分配内存&#xff0c;地址不同&#xff0c;比较为假。…

动态数据库设计

动态数据库设计是一种灵活的方法&#xff0c;用于构建能够适应不断变化的数据需求的数据库结构。它强调在不频繁修改数据库表结构的前提下&#xff0c;有效管理和存储多样化的数据。以下是实现动态数据库设计的一些关键技术点和策略&#xff1a; 实体-属性-值&#xff08;EAV&a…

安卓项目中so库选择

接上篇Android中常见SDK类型区别-CSDN博客 一些重要的加密算法或者核心协议一般都在C中编写&#xff0c;然后给java调用。这样可以避免反编译后查看到应用的源码。此时就需要了解一下NDK中的ABI&#xff08;Application Binary Interface的缩写&#xff0c;也就是应用二进制接…

代谢组数据分析一:代谢组数据准备

介绍 该数据集是来自于Zeybel 2022年发布的文章_Multiomics Analysis Reveals the Impact of Microbiota on Host Metabolism in Hepatic Steatosis_ [@zeybel2022multiomics],它包含了多种组学数据,如: 微生物组(粪便和口腔) 宿主人体学指标 宿主临床学指标 宿主血浆代谢…

8.8.8.8 IP地址的作用

在跟着韦东山老师的学习手册中看见了关于8.8.8.8 IP用于检测网络状态&#xff0c;然后搜索了关于此IP的相关作用如下&#xff1a; 公共DNS服务&#xff1a;8.8.8.8是Google提供的两个公共DNS服务器地址之一&#xff08;另一个是8.8.4.4&#xff09;。DNS&#xff08;域名系统&a…

GNN Algorithms(9): LLM Prompts--p-tuning、LoRA

目录 1. prompt-tuning background 2. Prompt Tuning 模型介绍 2.1 2021 prefix-tuning 2.2 2021 P-tuning v1 2.3 2021 Parameter-efficient prompt tuning (PET) 2.4 2022 P-tuning v2 2.5 2019 Adapter ​2.6 2021 LoRA (Low-Rank Adaptation) 2.7 2024 DoRA (…

LT8644EX 国产芯片 低功耗 数字交叉点开关 用于光纤网络交换 数字视频 数据存储网络

2.一般说明 LT8644EX是一个16x16数字交叉点交换机:具有16个差分CML兼容输入端和16个差动CML输出端。该LT8644EX是优化非归零(NRZ)与高达每端口6 Gbps的数据速率信令。每个端口提供可编程水平的输入均衡和可编程输出摆幅。tell 18171547226,该LT8644EX支持通过串行控制接口的独立…