算法练习第26天|46.全排列、47全排列II

46.全排列

46. 全排列 - 力扣(LeetCode)icon-default.png?t=N7T8https://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]

思路分析:

今天的全排列题目就不在和之前的求组合的问题一样了,之前从一个集合中取子集,需要使用startIndex来控制每次搜索开始的位置。但是全排列则不能用startIndex来控制搜索的开始位置,因为一旦用了startIndex,则startIndex前面的元素就遍历不到了。所以,全排列的思路就是每次遍历都是将nums数组从头到尾遍历一遍(注意是每一次遍历,就是常写的for循环那里),已经在path中的数字不能再用。

那么如何使得用过的数字不再用呢?我这里的做法是使用一个used数组来做记录。对应的树形结构如下:

 下面开始回溯三部曲:

第一步:确认回溯函数的参数和返回值。返回值老规矩void,参数除了题目的nums原数组,还需要used数组。通过used数组每次传进来上一层递归所使用元素的情况。used数组和nums数组长度一样。used某个位置的元素为0,则表明nums对应位置的元素没被用过;为1则表示被用过。

    vector<int> path;
    vector<vector<int>> result;

    void backTracking(vector<int> & nums, vector<int>& used){}

第二步,确定回溯中之条件。当path搜集到的元素个数和nums一样时,说明遍历完了,可以用result记录了。

void backTracking(vector<int> & nums, vector<int>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
}

第三部:单层搜索逻辑。这个时候就要用到前面所说的 每次遍历都是将nums数组从头到尾遍历一遍,

void backTracking(vector<int> & nums, vector<int>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
        //每次都是从头开始遍历nums中的元素
        for(int i = 0; i<nums.size(); i++){
            if(used[i] == 1){  //如果这个元素对应的标志位为1,表明已经用过,则continue进入下一轮循环,即开始检索下一个元素
                continue;
            }

            //走到这一步说明当前元素还没被用过,马上要用
            used[i] = 1;  //标志为置0
            path.push_back(nums[i]);  //path记录
            backTracking(nums, used);  //递归
            //两步回溯
            used[i] = 0;
            path.pop_back();

        }
    }

整体代码如下:

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;

    void backTracking(vector<int> & nums, vector<int>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
        
        for(int i = 0; i<nums.size(); i++){
            if(used[i] == 1){
                continue;
            }
            used[i] = 1;
            path.push_back(nums[i]);
            backTracking(nums, used);
            used[i] = 0;
            path.pop_back();

        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        int N = nums.size();
        vector<int> used(N,0);
        backTracking(nums, used);
        return result;
    }
};

 由于used数组不是局部变量,它需要在每次递归时都保留上一层元素是否使用的具体情况,所以我们将其初始化在permute函数中,并使用引用类型的形参来保持数据的更新。如果想节省空间,vector<int> used可以换成bool类型,即vector<bool> used。

总结

大家此时可以感受出排列问题的不同:

  • 每层都是从0开始搜索而不是startIndex
  • 需要used数组记录path里都放了哪些元素了

47.全排列 II

47. 全排列 II - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/permutations-ii/description/

题目描述:

给定一个可包含重复数字的序列 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]]

 思路分析:

本题与46题不同的是,46题所有元素都不重复,所以不用担心因为元素重复所带来的排列重复的问题,所以46题可以好不担心的每次都从0位置开始遍历nums数组。

但是本题存在了重复元素,需要当心得到重复的排列,所以可以先画出对应的树形结构,看看重复出现在什么地方:

 在40组合问题II、和90子集II中我们分别详细讲解了组合问题和子集问题如何去重。去重的前提是对nums原数组进行排序。我们尝试一下排序看行不行。发现当对数组进行排序后进行去重依然试用这道排列题。因为之前的i>0 && nums[i-1] == nums[i]可以帮助我们判断当前元素与其前一个元素是否相等。如果相等,那么去重问题的关键就成了怎么限定我访问的是第二个重复的元素呢?

首先,我们提前对数组进行了排序,所以能保证元素的增长顺序,其次,当i>0 && nums[i-1] == nums[i]这个条件满足时,说明有两个相邻元素时相等的,那么此时used数组就可以派上用场了。访问两个相同元素的前一个时,used置1,然后递归回溯,回溯后used对应位置会置0。走到这里表明两个相同元素中的前一个已经访问完毕,然后for循环i++,会访问到两个相同元素中的后一个,也就是 i>0 && nums[i-1] == nums[i]满足了,但是还不够,因为此时该元素不能被记录,所以还需要加的限制条件就是used[i-1] = 0, i>0 && nums[i-1] == nums[i]&&used[i-1] = 0 表明这是两个相同元素中的后一个且used[i-1] = 0表明前面的元素已经遍历过了,该记录的也记录了,所以着第二个元素就直接continue跳过。

整体代码如下:

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;

    void backtracking(vector<int> &nums, vector<bool>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return;
        }
        //由于时排列问题,不用startIndex,每次都从头遍历nums
        for(int i = 0; i< nums.size(); i++){
            //path已经记录过的,标志为1,直接continue到下一个元素
            if(used[i] == true){
                continue;
            }
            //如果当前元素没有用过,那么就该判断是不是相同元素中的后一个,如果是,直接continue
            if(i>0 && nums[i-1] == nums[i] && used[i-1]==false){
                continue; 
            }
            //正常元素的记录、递归、回溯
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            used[i] = false;
            path.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<bool> used(nums.size(), false);
        backtracking(nums,used);
        return result;
    }
};

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

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

相关文章

C++STL---vector模拟实现

通过上篇文章&#xff0c;我们知道vector的接口实际上和string是差不多的&#xff0c;但是他俩的内部结构却大不一样&#xff0c;vector内有三个成员变量&#xff1a;_start、_finish、_endofstorage: _start指向容器的头元素&#xff0c;_finish指向有效元素末尾的元素&#x…

【计算机毕业设计】359微信小程序校园失物招领系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Java基础知识点(反射、注解、JDBC、TCP/UDP/URL)

文章目录 反射反射的定义class对象反射的操作 注解注解的定义注解的应用注解的分类基准注解元注解 自定义注解自定义规则自定义demo JDBCTCP/UDP/URLTCPUDPURL 反射 反射的定义 Java Reflection是Java被视为动态语言的基础啊&#xff0c; 反射机制允许程序在执行期间接入Refl…

Vue进阶之Vue无代码可视化项目(一)

Vue无代码可视化项目 项目搭建初始步骤拓展:工程项目从0-1项目规范化package.jsoncpell.jsoncustom-words.txtts-eslint规则.eslintrc.cjsgit钩子检查有没有问题type-checkspellchecklint:stylehusky操作安装pre-commitpnpm的commit规范package.json:commitlint.config.cjs安装…

Java数组操作

数组拓展 1.1 数组拷贝 需求&#xff1a;定义一个方法arraycopy, 从指定源数组中从指定的位置开始复制指定数量的元素到目标数组的指定位置。 1.2. 排序操作 需求&#xff1a;完成对int[] arr new int[]{2,9,6,7,4,1}数组元素的升序排序操作. 1.2.1.冒泡排序 对未排序的各元素…

【计算机毕设】基于SpringBoot的教师工作量管理系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 随着高校规模的扩大和教学任务的增加&#xff0c;教师的工作量管理变得越来越复杂和重要。传统的教师工作量管理方式效率低下&#xff0c;容易出错&…

【typescript/flatbuffer】在websocket中使用flatbuffer

目录 说在前面场景fbs服务器代码前端typescript代码问题 说在前面 操作系统&#xff1a;Windows11node版本&#xff1a;v18.19.0typescript flatbuffer版本&#xff1a;24.3.25 场景 服务器(本文为golanggin)与前端通信时使用flatbuffer进行序列化与反序列化通信协议为websock…

CSDN UI 2024.06.01

当我们的栏目很多的时候&#xff0c;通过【置顶】来排列顺序是很麻烦的&#xff0c;应该加一列&#xff0c;设置优先级别。太难用了 或者加两个按钮【上移】 【下移】

正邦科技(day4)

烧录 一、烧录固件二、 通讯模块升级1&#xff1a;USB的方式升级固件2&#xff1a;通过mqtt的方式升级固件3&#xff1a;切换环境 三、 烧录WiFi1&#xff1a;短接2&#xff1a;烧录脚本 设备注意事项&#xff1a; 第一种方式&#xff1a;通信模组和MCU都可以统一烧录BoodLoade…

内网穿透-FRP流量改造

前言 在拿下一台机器作为入口时&#xff0c;内网代理就变得尤为重要。他是我们进行横向渗透一个中间人&#xff0c;没了代理在内网中就寸步难行。而内网穿透的工具有很多&#xff0c;比如frp&#xff0c;reGeorg等等非常优秀的代理工具。使用方法不在赘述&#xff0c;这篇文章…

ssm_mysql_高校自习室预约系统(源码)

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Ansible05-Ansible进阶(流程控制、Roles角色、加密优化调优等)

目录 写在前面7 Ansible 进阶7.1 流程控制7.1.1 handlers触发器与notify7.1.1.1 未使用handlers7.1.1.2 使用handlers 7.1.2 when判断7.1.2.1 when的语法7.1.2.2 when判断主机名选择模块输出7.1.2.3 when结合register变量 7.1.3 loop/with_items循环7.1.3.1 with_items案例7.1.…

微信小程序注册流程及APPID,APPSecret获取

1.注册微信小程序 注册链接&#xff1a;公众号 (qq.com) 1.1填写邮箱、密码、验证码 1.2邮箱登录点击邮件中链接激活&#xff0c;即可完成注册 1.3用户信息登记 接下来步骤&#xff0c;将用个人主题类型来进行演示 填写主体登记信息&#xff0c;使用管理员本人微信扫描二维码…

行政工作如何提高效率?桌面备忘录便签软件哪个好

在行政管理工作中&#xff0c;效率的提高无疑是每个行政人员都追求的目标。而随着科技的发展&#xff0c;各种便捷的工具也应运而生&#xff0c;其中桌面备忘录便签软件便是其中的佼佼者。那么&#xff0c;这类软件又如何帮助我们提高工作效率呢&#xff1f; 首先&#xff0c;…

(2024,LoRA,全量微调,低秩,强正则化,缓解遗忘,多样性)LoRA 学习更少,遗忘更少

LoRA Learns Less and Forgets Less 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 引言 2. 背景 3. 实验设置 3.2 使用编码和数学基准测试来衡量学习&#xff08;目标域…

C++:细谈Sleep和_sleep

ZINCFFO的提醒 还记得上上上上上上上上上上上上上上上上上上&#xff08;上的个数是真实的&#xff09;篇文章吗&#xff1f; 随机应变——Sleep()和_sleep() 但在ZINCFFO的C怪谈-02中&#xff1a; 我不喜欢Sleep...... 奤&#xff1f;媜煞鷥&#xff01; 整活&#xff01;…

容器项目之前后端分离

容器化部署ruoyi项目 #需要的镜像nginx、java、mysql、redis、 #导入maven镜像、Java镜像和node镜像 docker load -i java-8u111-jdk.tar docker load -i maven-3.8.8-sapmachine-11.tar docker load -i node-18.20.3-alpine3.20.tar #拉取MySQL和nginx镜像 docker pull mysql…

权限修饰符和代码块

一.权限修饰符 1.权限修饰符:是用来控制一个成员能够被访问的范围的。 2.可以修饰成员变量&#xff0c;方法&#xff0c;构造方法,内部类。 3.例子&#xff1a; public class Student {priviate String name;prviate int age;} 二.权限修饰符的分类 有四种作用范围大小…

牛客网刷题 | BC102 带空格直角三角形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

#1 深度优先搜索

深搜思想 DFS其实是针对图论的一种搜索算法&#xff0c;由一个节点出发&#xff0c;不撞南墙不回头式的遍历所有的节点。 如先遍历1&#xff0c;沿&#xff08;1,2&#xff09;遍历2&#xff0c;再沿&#xff08;2,4&#xff09;遍历4&#xff0c;撞南墙&#xff08;边界条件…