全排列问题(LeetCode 46 47)

1 全排列问题

本篇文章主要介绍了全排列问题以及详细的解法。

给定一个数组求出其中的全排列。
其中的数组,可能带重复元素,也可能不带重复元素。

有详细思路以及递归树图解,语言包括C++JavaGo

下面先来看看简单的版本,不带重复元素的。

2 不带重复元素的全排列


题目链接。

2.1 思路

首先全排列是所有元素都需要选择的,也就是每次选择的时候,需要遍历一次数组中的所有元素。

考虑下样例中的[1,2,3],如果第一次选择了[1],那么下一次只能选择[2,3]之中的其中一个。继续,如果第二次选择了[2],那么只剩下[3]可以选择了。也就是说,每次选择的时候,都会将候选元素的个数减少一个。

具体的选择过程以及结果如下:

同理,如果第一次选择[2],图示如下:


如果第一次选择[3],图示如下:


完整递归树:

3.2 代码

回到代码实现上,需要一种方式去判断上一次选择了哪些,用于在本次选择的时候,去判断不选择上一次选择的元素。在C++中,可以考虑使用unordered_set<int>

class Solution {
private:
public:vector<vector<int> > permute(vector<int> &nums) {vector<vector<int> > res;// 长度int n = static_cast<int>(nums.size());// 存储当前选择的元素vector<int> cur;// 递归函数定义,只有一个selected_index参数,表示上一次选择的下标集合auto dfs = [&](this auto &&dfs, unordered_set<int> selected_index) -> void {// 如果当前选择的元素列表长度为n,表明找到了一个结果,存储并返回if (cur.size() == n) {res.push_back(cur);return;}// 枚举数组中的每个元素,也就是上面递归树中的按照水平视角来看的过程// 在第一层中,枚举选择的[1,2,3]// 第二层中,能选择上一层没有选择的元素// 第三层类似,能选择第二层没有选择的元素for (int i = 0; i < n; ++i) {// 如果上一次选择过,跳过// 注意这里的实现使用了下标,直接使用元素也是可以的,这里为了方便代码编写使用了下标if (selected_index.contains(i)) {continue;}// 选择了这个下标selected_index.insert(i);// 存储结果cur.push_back(nums[i]);// 选择下一层dfs(selected_index);// 回溯处理cur.pop_back();selected_index.erase(i);}};// 递归入口,一开始没有选择任何元素,传入一个空集合dfs({});return res;}
};

耗时14ms

由于这里的下标范围不大,可以考虑使用位运算来优化。下标范围只有1 <= n <= 6,可以直接使用一个int来存储选择的下标。另一方面,原来的实现中每次递归的时候都会复制一次unordered_set,所以改用了位运算实现能节省了unordered_set占用的空间,以及复制所需要的时间。

class Solution {
private:
public:vector<vector<int> > permute(vector<int> &nums) {vector<vector<int> > res;int n = static_cast<int>(nums.size());vector<int> cur;auto dfs = [&](this auto &&dfs, int selected_index) -> void {if (cur.size() == n) {res.push_back(cur);return;}for (int i = 0; i < n; ++i) {// 已经选择过if (selected_index & (1<<i)){continue;}// 存储该下标selected_index |= (1<<i);cur.push_back(nums[i]);dfs(selected_index);cur.pop_back();// 回溯selected_index &= ~(1<<i);}};dfs({});return res;}
};

耗时:

3 带重复元素的全排列

如果元素重复会怎么样呢?

题目链接。

如果元素重复,按照上面的算法,输入[1,2,2],就会输出:

1 2 2 
1 2 2 
2 1 2 
2 2 1 
2 1 2 
2 2 1 

这明显是不合题意的。

3.1 思路

为什么上面的算法会有问题呢?因为选择了重复的元素。

对于[1,2,2]来说,下面这样选择就重复了:


因此,需要跳过重复的2,具体来说,在同一层中,选择过的元素不能重复选择:

同样道理,如果第一层选择2:

这样第一层的第二个2就不能选择了,完整递归树如下:

3.2 代码实现

代码实现上和之前的类似,添加一个判断,判断同一层不选择之前选择过的元素即可。

class Solution {
private:
public:vector<vector<int> > permuteUnique(vector<int> &nums) {vector<vector<int> > res;int n = static_cast<int>(nums.size());vector<int> cur;auto dfs = [&](this auto &&dfs, int selected_index) -> void {if (cur.size() == n) {res.push_back(cur);return;}// 使用unordered_set保存已经选择过的值// 注意这里不能用下标了,因为需要保存的是值unordered_set<int> selected_digit;for (int i = 0; i < n; ++i) {if (selected_index & (1 << i)) {continue;}// 如果包含当前已经选择过的,跳过if (selected_digit.contains(nums[i])) {continue;}selected_index |= (1 << i);// 保存当前已经选择过的selected_digit.insert(nums[i]);cur.push_back(nums[i]);dfs(selected_index);cur.pop_back();selected_index &= ~(1 << i);// 回溯的时候不需要对selected_digit处理// 因为需要保存所有已经选择过的}};dfs({});return res;}
};

同理,在该题的范围下也能用位运算优化:

class Solution {
private:
public:vector<vector<int> > permuteUnique(vector<int> &nums) {vector<vector<int> > res;int n = static_cast<int>(nums.size());vector<int> cur;auto dfs = [&](this auto &&dfs, int selected_index) -> void {if (cur.size() == n) {res.push_back(cur);return;}int selected_digit = 0;for (int i = 0; i < n; ++i) {if (selected_index & (1 << i)) {continue;}// 如果包含当前已经选择过的,跳过// 注意值的范围是带负数的if (selected_digit & (1<<(nums[i] + 10))){continue;}selected_index |= (1 << i);selected_digit |= (1 << (nums[i]+10));cur.push_back(nums[i]);dfs(selected_index);cur.pop_back();selected_index &= ~(1 << i);}};dfs({});return res;}
};

4 其他语言版本——Java

4.1 不带重复元素

import java.util.*;public class Solution {private final List<List<Integer>> res = new ArrayList<>();private final LinkedList<Integer> cur = new LinkedList<>();private int[] nums;private int n;private void dfs(int selectedIndex) {if (cur.size() == n) {res.add(new ArrayList<>(cur));return;}for (int i = 0; i < n; i++) {if ((selectedIndex & (1 << i)) != 0) {continue;}selectedIndex |= (1 << i);cur.addLast(nums[i]);dfs(selectedIndex);cur.pollLast();selectedIndex &= ~(1 << i);}}public List<List<Integer>> permute(int[] nums) {this.nums = nums;this.n = nums.length;dfs(0);return res;}
}

4.2 带重复元素

import java.util.*;public class Solution {private final List<List<Integer>> res = new ArrayList<>();private final LinkedList<Integer> cur = new LinkedList<>();private int[] nums;private int n;private void dfs(int selectedIndex) {if (cur.size() == n) {res.add(new ArrayList<>(cur));return;}int selectedDigit = 0;for (int i = 0; i < n; i++) {if ((selectedIndex & (1 << i)) != 0) {continue;}if ((selectedDigit & (1 << (nums[i] + 10))) != 0) {continue;}selectedIndex |= (1 << i);selectedDigit |= (1 << (nums[i] + 10));cur.addLast(nums[i]);dfs(selectedIndex);cur.pollLast();selectedIndex &= ~(1 << i);}}public List<List<Integer>> permuteUnique(int[] nums) {this.nums = nums;this.n = nums.length;dfs(0);return res;}
}

5 其他语言版本——Go

5.1 不带重复元素

func permute(nums []int) [][]int {n, res, cur := len(nums), make([][]int, 0), make([]int, 0)var dfs func(int)dfs = func(selectedIndex int) {if len(cur) == n {curCopy := make([]int, len(cur))copy(curCopy, cur)res = append(res, curCopy)return}for i := 0; i < n; i++ {if (selectedIndex & (1 << i)) != 0 {continue}selectedIndex |= (1 << i)cur = append(cur, nums[i])dfs(selectedIndex)cur = cur[:len(cur)-1]selectedIndex &= ^(1 << i)}}dfs(0)return res
}

5.2 带重复元素

func permuteUnique(nums []int) [][]int {n, res, cur := len(nums), make([][]int, 0), make([]int, 0)var dfs func(int)dfs = func(selectedIndex int) {if len(cur) == n {curCopy := make([]int, len(cur))copy(curCopy, cur)res = append(res, curCopy)return}selectedDigit := 0for i := 0; i < n; i++ {if (selectedIndex & (1 << i)) != 0 {continue}if (selectedDigit & (1 << (nums[i] + 10))) != 0 {continue}selectedIndex |= (1 << i)selectedDigit |= (1 << (nums[i] + 10))cur = append(cur, nums[i])dfs(selectedIndex)cur = cur[:len(cur)-1]selectedIndex &= ^(1 << i)}}dfs(0)return res
}

6 附录

  • 姐妹篇:子集问题

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

11.PPT:世界动物日【25】

目录 NO12​ NO34 NO56​ NO789视频音频​ NO10/11/12​ NO12 设计→幻灯片大小→ →全屏显示&#xff08;16&#xff1a;9&#xff09;确定调整标题占位符置于图片右侧&#xff1a;内容占位符与标题占位符左对齐单击右键“世界动物日1”→复制版式→大小→对齐 幻灯片大小…

【漫话机器学习系列】083.安斯库姆四重奏(Anscombe‘s Quartet)

安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09; 1. 什么是安斯库姆四重奏&#xff1f; 安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09;是一组由统计学家弗朗西斯安斯库姆&#xff08;Francis Anscombe&#xff09; 在 1973 年 提出的 四组数据集。它们…

Postman接口测试:全局变量/接口关联/加密/解密

全局变量和环境变量 全局变量&#xff1a;在postman全局生效的变量&#xff0c;全局唯一 环境变量&#xff1a;在特定环境下生效的变量&#xff0c;本环境内唯一 设置&#xff1a; 全局变量&#xff1a; pm.globals.set("variable_key", "variable_value1&q…

ZZNUOJ(C/C++)基础练习1081——1090(详解版)

目录 1081 : n个数求和 &#xff08;多实例测试&#xff09; C C 1082 : 敲7&#xff08;多实例测试&#xff09; C C 1083 : 数值统计(多实例测试) C C 1084 : 计算两点间的距离&#xff08;多实例测试&#xff09; C C 1085 : 求奇数的乘积&#xff08;多实例测试…

STM32的HAL库开发---高级定时器

一、高级定时器简介 1、STM32F103有两个高级定时器&#xff0c;分别是TIM1和TIM8。 2、主要特性 16位递增、递减、中心对齐计数器(计数值:0~65535)16位预分频器(分频系数:1~65536)可用于触发DAC、ADC在更新事件、触发事件、输入捕获、输出比较时&#xff0c;会产生中断/DMA请…

数据库系统架构与DBMS功能探微:现代信息时代数据管理的关键

欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭&#xff5e; ??? 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。?? 希望在这里&#xff0c;我们能一起探…

优惠券平台(一):基于责任链模式创建优惠券模板

前景概要 系统的主要实现是优惠券的相关业务&#xff0c;所以对于用户管理的实现我们简单用拦截器在触发接口前创建一个单一用户。 // 用户属于非核心功能&#xff0c;这里先通过模拟的形式代替。后续如果需要后管展示&#xff0c;会重构该代码 UserInfoDTO userInfoDTO new…

搭建集成开发环境PyCharm

1.下载安装Python&#xff08;建议下载并安装3.9.x&#xff09; https://www.python.org/downloads/windows/ 要注意勾选“Add Python 3.9 to PATH”复选框&#xff0c;表示将Python的路径增加到环境变量中 2.安装集成开发环境Pycharm http://www.jetbrains.com/pycharm/…

模板的进阶

非类型模板参数 模板参数分类类型形参与非类型形参 。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在 class 或者 typename 之类的参数类型名称 。 非类型形参&#xff0c;就是用一个常量作为类 ( 函数 ) 模板的一个参数&#xff0c;在类 ( 函数 ) 模板中可将…

windows安装linux子系统【ubuntu】操作步骤

1.在windows系统中开启【适用于Linux的Windows子系统】 控制面板—程序—程序和功能—启用或关闭Windows功能—勾选适用于Linux的Windows子系统–确定 2.下载安装Linux Ubuntu 22.04.5 LTS系统 Ununtu下载链接 3.安装完Ununtu系统后更新系统 sudo apt update4.进入/usr/l…

【大数据技术】搭建完全分布式高可用大数据集群(Kafka)

搭建完全分布式高可用大数据集群(Kafka) kafka_2.13-3.9.0.tgz注:请在阅读本篇文章前,将以上资源下载下来。 写在前面 本文主要介绍搭建完全分布式高可用集群 Kafka 的详细步骤。 注意: 统一约定将软件安装包存放于虚拟机的/software目录下,软件安装至/opt目录下。 安…

万字详解 MySQL MGR 高可用集群搭建

文章目录 1、MGR 前置介绍 1.1、什么是 MGR1.2、MGR 优点1.3、MGR 缺点1.4、MGR 适用场景 2、MySQL MGR 搭建流程 2.1、环境准备2.2、搭建流程 2.2.1、配置系统环境2.2.2、安装 MySQL2.2.3、配置启动 MySQL2.2.4、修改密码、设置主从同步2.2.5、安装 MGR 插件 3、MySQL MGR 故…

Linux高级IO

文章目录 &#x1f965;IO的基本概念&#x1f347;钓鱼五人组&#x1f348;五种IO模型&#x1f349;高级IO重要概念同步通信 VS 异步通信阻塞 VS 非阻塞 &#x1f34a;其他高级IO&#x1f34b;阻塞IO&#x1f34b;‍&#x1f7e9;非阻塞IO &#x1f965;IO的基本概念 什么是IO…

摄像头模块烟火检测

工作原理 基于图像处理技术&#xff1a;分析视频图像中像素的颜色、纹理、形状等特征。火焰通常具有独特的颜色特征&#xff0c;如红色、橙色等&#xff0c;且边缘呈现不规则形状&#xff0c;还会有闪烁、跳动等动态特征&#xff1b;烟雾则表现为模糊、无固定形状&#xff0c;…

4.3 线性回归的改进-岭回归/4.4分类算法-逻辑回归与二分类/ 4.5 模型保存和加载

4.3.1 带有L2正则化的线性回归-岭回归 岭回归&#xff0c;其实也是一种线性回归&#xff0c;只不过在算法建立回归方程的时候1&#xff0c;加上正则化的限制&#xff0c;从而达到解决过拟合的效果 4.3.1.1 API 4.3.1.2 观察正则化程度的变化&#xff0c;对结果的影响 正则化力…

CSS outline详解:轮廓属性的详细介绍

什么是outline&#xff1f; outline&#xff08;轮廓&#xff09;是CSS中一个有趣的属性&#xff0c;它在元素边框&#xff08;border&#xff09;的外围绘制一条线。与border不同的是&#xff0c;outline不占用空间&#xff0c;不会影响元素的尺寸和位置。这个特性使它在某些…

设计模式.

设计模式 一、介绍二、六大原则1、单一职责原则&#xff08;Single Responsibility Principle, SRP&#xff09;2、开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;3、里氏替换原则&#xff08;Liskov Substitution Principle, LSP&#xff09;4、接口隔离原则&am…

硬件工程师思考笔记02-器件的隐秘角落:磁珠与电阻噪声

目录 引言 一、磁珠&#xff1a;你以为的“噪声克星”&#xff0c;可能是高频杀手 1. 磁珠的阻抗特性与误区 2. 案例&#xff1a;磁珠引发的5G射频误码率飙升 二、电阻&#xff1a;静默的噪声制造者 1. 电阻噪声的两种形态 2. 案例&#xff1a;ADC精度被电阻噪声“偷走” 三、设…

mysql 不是内部或外部命令,也不是可运行的程序或批处理文件

mysql 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 前言描述1、&#x1f331;环境变量配置&#xff08;高级系统设置&#xff09;&#xff1a;2、&#x1f331;环境变量配置&#xff08;系统属性&#xff09;&#xff1a;3、&#x1f331;环境变量配置&…

极客说|利用 Azure AI Agent Service 创建自定义 VS Code Chat participant

作者&#xff1a;卢建晖 - 微软高级云技术布道师 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&a…