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

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...