力扣第四十七题——全排列II
内容介绍
给定一个可包含重复数字的序列
nums
,按任意顺序 返回所有不重复的全排列。示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2],[1,2,1],[2,1,1]]示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
完整代码
int* vis;void backtrack(int* nums, int numSize, int** ans, int* ansSize, int idx, int* perm) {if (idx == numSize) {int* tmp = malloc(sizeof(int) * numSize);memcpy(tmp, perm, sizeof(int) * numSize);ans[(*ansSize)++] = tmp;return;}for (int i = 0; i < numSize; ++i) {if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {continue;}perm[idx] = nums[i];vis[i] = 1;backtrack(nums, numSize, ans, ansSize, idx + 1, perm);vis[i] = 0;}
}int cmp(void* a, void* b) {return *(int*)a - *(int*)b;
}int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {int** ans = malloc(sizeof(int*) * 2001);int* perm = malloc(sizeof(int) * 2001);vis = malloc(sizeof(int) * numsSize);memset(vis, 0, sizeof(int) * numsSize);qsort(nums, numsSize, sizeof(int), cmp);*returnSize = 0;backtrack(nums, numsSize, ans, returnSize, 0, perm);*returnColumnSizes = malloc(sizeof(int) * (*returnSize));for (int i = 0; i < *returnSize; i++) {(*returnColumnSizes)[i] = numsSize;}return ans;
}
思路详解
整体思路
- 排序:首先对数组进行排序,以便在后续过程中可以轻松跳过重复的数字。
- 回溯:使用回溯算法生成所有可能的全排列。
- 去重:在生成全排列的过程中,通过一个布尔数组
vis
来标记某个数字是否已经被使用,以及通过比较相邻数字是否相等来跳过重复的排列。
详细步骤
1. 排序函数cmp
cmp
函数是一个比较函数,用于qsort
函数进行排序。它比较两个整数的值,并返回它们之间的差值。
2. 主函数permuteUnique
- 初始化:分配内存给结果数组
ans
、排列数组perm
和访问标记数组vis
。 - 排序:调用
qsort
对输入数组nums
进行排序。 - 调用回溯函数:调用
backtrack
函数开始生成全排列。 - 设置返回列大小:为每个排列分配列大小,即
numsSize
。
3. 回溯函数backtrack
- 终止条件:当
idx
等于numSize
时,表示一个排列已经生成完毕,将其复制到结果数组ans
中。 - 遍历数组:遍历输入数组
nums
,尝试将每个数字放入当前排列的idx
位置。 - 剪枝:如果当前数字已经被访问过,或者当前数字与前一个数字相同且前一个数字未被访问过,则跳过当前循环,以避免生成重复的排列。
- 递归:将当前数字标记为已访问,并将其放入排列数组
perm
中,然后递归调用backtrack
函数处理下一个位置。 - 撤销选择:在递归返回后,将当前数字标记为未访问,以便在后续的迭代中可以重新使用。
代码亮点
- 去重:通过排序和比较相邻数字,巧妙地避免了生成重复的排列。
- 回溯算法:使用回溯算法高效地生成所有可能的全排列。
- 内存管理:动态分配和释放内存,以存储生成的排列和访问标记。
知识点精炼
-
排序:
- 使用
qsort
函数对数组进行排序,以便于后续去重操作。
- 使用
-
回溯算法:
- 利用回溯算法遍历所有可能的组合,并在满足条件时生成全排列。
-
去重策略:
- 通过排序和相邻元素比较,避免在回溯过程中生成重复的排列。
-
布尔数组:
- 使用布尔数组
vis
记录每个元素是否已被使用,确保每个元素在每个排列中只出现一次。
- 使用布尔数组
-
动态内存分配:
- 动态分配内存以存储全排列结果
ans
、排列数组perm
和访问标记数组vis
。
- 动态分配内存以存储全排列结果
-
剪枝优化:
- 在回溯过程中,通过剪枝操作跳过不可能生成有效排列的分支,提高算法效率。
-
递归与迭代:
- 结合递归和迭代,实现深度优先搜索(DFS)来生成全排列。
-
内存释放:
- 在递归返回时,恢复现场,释放不必要的资源,避免内存泄漏。
-
比较函数:
- 实现
cmp
比较函数,作为qsort
的参数,用于数组元素的排序。
- 实现
-
返回值与参数:
- 通过指针参数返回全排列结果及其大小,以及每个排列的列大小。
优化方案
去重优化
- 排序后去重:在回溯之前先对数组进行排序,这样可以在生成排列时更容易地跳过重复的元素。
- 位运算去重:对于小范围的整数,可以使用位运算来标记已访问的元素,减少内存使用和提高访问速度。
2. 回溯剪枝
- 提前终止:在递归过程中,如果发现某个分支不可能生成有效排列,应立即终止该分支的递归。
- 按字典序排列:在回溯时,保持元素按字典序排列,可以更早地发现重复并剪枝。
3. 数据结构优化
- 原地交换:使用原地交换而非额外的数组来存储排列,可以减少内存使用和复制操作。
- 栈代替递归:将递归改为使用栈,可以减少函数调用的开销。
4. 算法优化
- 迭代而非递归:在某些情况下,迭代可能比递归更高效,因为它避免了函数调用的开销。
- 分支限界:使用分支限界技术,只生成那些有希望成为有效排列的分支。
5. 并行计算
- 多线程:对于大型问题,可以使用多线程并行生成排列的不同分支。
- 分布式计算:将问题分割成多个子问题,在分布式系统中并行解决。
6. 编译器优化
- 编译器优化选项:使用编译器的优化选项,如-O2或-O3,可以自动进行某些优化。
相关文章:
力扣第四十七题——全排列II
内容介绍 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 1: 输入:nums [1,1,2] 输出: [[1,1,2],[1,2,1],[2,1,1]]示例 2: 输入:nums [1,2,3] 输出:[[1,2,3],…...

Springer旗下中科院2区TOP,国人优势大!
关注GZH【欧亚科睿学术】,第一时间了解期刊最新动态! 1 通信网络类 【期刊简介】IF:4.0-5.0,JCR1区,中科院3区 【出版社】ELSEVIER出版社 【检索情况】SCIE&EI双检,CCF-C类 【征稿领域】通信网络的…...

【C++】C++入门知识详解(下)
大家好~我们接着【C】C入门知识详解(上)-CSDN博客来介绍另一些C入门基础知识。 1.缺省值和缺省参数 缺省参数就是声明或定义函数时为函数的参数指定一个缺省参数。在调用该函数时,如果没有指定实参,则采用该形参的缺省值…...
分压电阻方式的ADC电压校准
无人机有个流程是电池电压校准。具体做法是:让你用万用表测量一下电池两端的电压,然后输入到文本框中,电机计算能重新计算出电压分压器的值,从而获得电池电压值。 这种方法实现的原理是这样的: 电阻分压检测电压原理,以上图为例: 当电路确定时,R2/(R1+R2)是一个定值R,…...
使用Postman测试API短轮询机制:深入指南
短轮询是一种Web开发中常用的技术,用于在客户端和服务器之间定期检查更新。与长轮询或WebSockets等技术相比,短轮询简单易实现,但可能带来较多的HTTP请求,从而增加服务器负担。Postman作为一个强大的API测试工具,可以用…...

明清进士人数数据
明清进士人数数据 指标:省份名称、城市名称、区县名称、明清各省进士人数、明清各城市进士人数、明清各县区进士人数 指标说明: Province[省份名称]-统计数据所属省份 City[城市名称]-统计数据所属地级市 Region[区县名称]-统计数据所属区县 MQpro…...

C# 串口通信(通过serialPort控件发送及接收数据)
连接串口 界面设计打开串口发送数据通过文件发送发送数据 接收数据 首先可以在 工具箱中搜索serialport,将控件拖到你的Winfrom窗口。 界面设计 打开串口 private void Connect_Click(object sender, EventArgs e){serialPort1.PortName comboBox2.Text;//端口名s…...
数据安全的新盾牌:SQL Server数据库镜像技术详解
数据安全的新盾牌:SQL Server数据库镜像技术详解 在数据驱动的商业世界中,数据库的安全性是维护企业运营的关键。SQL Server提供了多种数据保护机制,其中数据库镜像技术是一个强大的高可用性解决方案,它可以显著提高数据的安全性…...

【C语言版】数据结构教程(一)绪论(上)
【内容简介】本文整理数据结构(C语言版)相关内容的复习笔记,供各位朋友借鉴学习。本章内容更偏于记忆和理解,请读者们耐心阅读。 数据结构教程 绪论(上) 本节学习目标 1.1 基本概念 1.2 抽象数据类型的表示…...
酒后为什么总感觉渴?
喝酒后感到口渴,这种感觉其实很常见。这主要是因为酒精对我们的身体有几种影响。首先,酒精能够扩张血管,这会加快血液循环,让肾脏更加活跃,产生更多的尿液。这样一来,我们体内的水分就会通过排尿流失&#…...

Docker安装OwnCloud私有云盘对接ceph
一、安装OwnCloud 我的安装包链接:https://pan.baidu.com/s/1cJO8WEonsw4gGQWgQaYzpw?pwd6bak 提取码:6bak 启动OwnCloud容器,没有镜像会自动下载 docker run -d -p 80:80 -v /home/owncloud:/var/www/html --name owncloud --restartalway…...

创建了Vue项目,需要导入什么插件以及怎么导入
如果你不知道怎么创建Vue项目,建议可以看一看这篇文章 怎么安装Vue的环境和搭建Vue的项目-CSDN博客 1.在idea中打开目标文件 2.系在一个插件Vue.js 3.下载ELement UI 在Terminal中输入 # 切换到项目根目录 cd vueadmin-vue # 或者直接在idea中执行下面命令 # 安装element-u…...
abstract 关键字
在C#中,abstract 关键字是一个非常重要的特性,它用于定义抽象类和抽象成员(如方法、属性、索引器、事件或操作符)。使用 abstract 关键字的目的主要是为了提供一种机制,让基类能够指定一个或多个必须由派生类实现的方法…...

用Python编写你的网络监控系统详解
概要 在现代网络管理中,实时监控网络流量和状态是保证网络正常运行的关键。使用Python编写网络监控工具可以帮助管理员及时发现和解决网络问题。本文将详细介绍如何使用Python编写网络监控工具,包括基本概念、常用库及其应用场景,并提供相应的示例代码。 网络监控的基本概念…...

操作系统——虚拟内存
一、虚拟内存是什么? 虚拟内存类似一个桥梁,原来程序直接访问物理内存读取数据,现在程序直接访问虚拟内存,由虚拟内存再访问物理内存。 使用虚拟内存的好处: 隔离进程、提高内存使用安全性:每个进程直接…...
Zoom视频会议软件使用
Zoom 是一款广泛使用的视频会议软件,可以用于在线会议、网络研讨会、课堂教学、团队协作等。以下是使用 Zoom 的基本步骤和一些有用的技巧: 安装 Zoom 下载并安装: 访问 Zoom 下载页面。下载适用于你的操作系统(Windows, macOS, Linux, iOS, Android)的客户端。安装完成后…...

MVC软件设计模式及QT的MVC架构
目录 引言 一、MVC思想介绍 1.1 MCV模型概述 1.2 Excel的处理数据 1.3 MVC模式的优势 二、QT中的MVC 1.1 模型(Model) 1. QAbstractItemModel 2. QStringListModel 3. QStandardItemModel 4. QSqlTableModel 和 QSqlQueryModel 5. QAbstract…...
使用WSL通过SSH连接并运行图形界面程序
使用WSL通过SSH连接并运行图形界面程序 1. 在Windows上安装X服务器2. 配置并启动VcXsrv3. 在WSL Ubuntu中设置DISPLAY变量4. 从WSL Ubuntu连接到远程服务器5. 在远程服务器上设置DISPLAY变量6. 测试X11转发7. 运行您的安装程序注意事项 在Windows Subsystem for Linux (WSL) 上…...

柳湛宇-简历
...

6-1 从全连接层到卷积
我们之前讨论的多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。 对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。 此时,多层感知机可能是最好的…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...