回溯算法:递增子序列 全排列 全排列II

491.递增子序列

  • 思路:
    • 分析题目:
      • 输入一个序列,输出至少有两个元素递增子序列。所谓序列,就是按照次序排好的行列,因此本题不可以把输入数组重新排序,否则就会改变序列的顺序。因此,不能使用前面90.子集2中的去重逻辑。
      • 本题要取的递增子序列,也就是取有序的子集,且不能有相同的递增子序列。
    • 回溯三部曲
      • 函数参数:需要startIndex,调整下一层递归的起始位置,防止取到重复的元素。
      • 终止条件:相比组合问题收集叶子节点,子集问题收集所有节点,本题更类似子集问题,只是对子集加了限定条件,也就是递增子序列大小至少为2,因此取的不是所有节点。
      • 单层逻辑
        • 假设给定一个数组[1,3,3,4,2],横向遍历到第二个3时,以它开头的递增子序列会和前面遍历的3一样,因此要去重,使用一个set来存储遍历过的元素,如果当前遍历到的元素已经存在set中了,就continue遍历下一个元素。
        • 当遍历到2时,如果此时path中末尾是元素3或4,那么加入2就无法构成递增子序列,这种情况也直接continue遍历下一个元素。
    • 时间复杂度: O(n * 2^n)
    • 空间复杂度: O(n)
    • 优化:用数组来做哈希
class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums, int startIndex) {
        //当path中至少有两个元素时才收集结果
        if(path.size() > 1) res.push_back(path);

        unordered_set<int> uset;//使用set在横向遍历时去重,由于每次递归都会重新定义一个新的set,因此不需要对set进行回溯
        for(int i = startIndex; i < nums.size(); i++) {
            //如果path中有元素,且当前遍历到的元素小于path中最后一个(最大)的元素
            //或 当前遍历元素,已有相同的元素被用过了
            //直接continue继续遍历下一个元素
            if(!path.empty() && nums[i] < path.back()
                || uset.find(nums[i]) != uset.end()) {
                    continue;
            }
            uset.insert(nums[i]);//记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        path.clear();
        backtracking(nums, 0);
        return res;
    }
};

46.全排列

  • 思路:排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这是和之前分析的子集以及组合所不同的地方
    • 回溯三部曲
      • 函数参数:排列问题就不需要使用startIndex来去重了,但是需要标记已经使用过的元素,防止排列中出现重复的元素。使用used数组,标记已经选择的元素。
      • 终止条件:叶子节点就是要收集的结果,当path的大小等于输入数组的大小时,就说明是叶子节点,收集结果。
      • 单层逻辑:排列问题,每次都要从头开始搜索,但是一个排列里一个元素只能使用一次,所以要用used数组标记使用过的元素
    • 时间复杂度: O(n!)
    • 空间复杂度: O(n)
class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums) {
        if(path.size() == nums.size()) {
            res.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); i++) {
            //如果path中已经有当前元素,直接continue遍历下一元素,防止元素重复
            if(count(path.begin(), path.end(), nums[i])) {
                continue;
            }
            path.push_back(nums[i]);
            backtracking(nums);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> permute(vector<int>& nums) {
        res.clear();
        path.clear();
        backtracking(nums);
        return res;
    }
};
class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums, vector<bool>& used) {
        if(path.size() == nums.size()) {
            res.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); i++) {
            //如果path中已经有当前元素,直接continue遍历下一元素,防止元素重复
            if(used[i]) {
                continue;
            }
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }

public:
    vector<vector<int>> permute(vector<int>& nums) {
        res.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return res;
    }
};

47.全排列II

  • 思路:
    • 与上一题的区别在于,输入数组可包含重复数字,要返回所有不重复的全排列。因此需要进行去重。去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了
    • 横向遍历时(for循环),如果前一个元素的值与当前遍历元素的值相同,那么就进行去重。
    • 使用used数组进行标记,以[1,1,2]为例:纵向遍历到第二个1时,used数组中第一位应该为1,因为纵向上元素可以重复;横向遍历到第二个1时,used数组中第一位应该为0,这是上一层回溯后的结果,说明横向遍历过程中已经使用过1了,直接跳过。
    • 时间复杂度: 最差情况所有元素都是唯一的。复杂度和全排列1都是 O(n! * n) 对于 n 个元素一共有 n! 中排列方案。而对于每一个答案,我们需要 O(n) 去复制最终放到 result 数组。
    • 空间复杂度: O(n) 回溯树的深度取决于我们有多少个元素
class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums, vector<bool>& used) {
        if(path.size() == nums.size()) {
            res.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); i++) {
            // used[i - 1] == true,说明同一树枝nums[i - 1]使用过
            // used[i - 1] == false,说明同一树层nums[i - 1]使用过
            // 如果同一树层nums[i - 1]使用过则直接跳过
            if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            if(used[i] == false) {
                used[i] = true;
                path.push_back(nums[i]);
                backtracking(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
    }

public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        res.clear();
        path.clear();
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return res;
    }
};

总结

前面跳过了used做法,现在还是遇到了,还要再多理解练习

参考链接

代码随想录:递增子序列  全排列  全排列II

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

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

相关文章

C# WPF上位机开发(抽奖软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 每到年末或者是尾牙的时候&#xff0c;很多公司都会办一些年终的清楚活动&#xff0c;感谢员工过去一年辛苦的付出。这个时候&#xff0c;作为年会…

深度学习实战66-基于计算机视觉的自动驾驶技术,利用YOLOP模型实现车辆区域检测框、可行驶区域和车道线分割图

大家好,我是微学AI,今天给大家介绍一下深度学习实战66-基于计算机视觉的自动驾驶技术,利用YOLOP模型实现车辆区域检测框、可行驶区域和车道线分割图。本文我将介绍自动驾驶技术及其应用场景,并重点阐述了基于计算机视觉技术下的自动驾驶。自动驾驶技术是一种利用人工智能和…

Dockerfile 指令的最佳实践

这些建议旨在帮助您创建一个高效且可维护的Dockerfile。 一、FROM 尽可能使用当前的官方镜像作为镜像的基础。Docker推荐Alpine镜像&#xff0c;因为它受到严格控制&#xff0c;体积小&#xff08;目前不到6 MB&#xff09;&#xff0c;同时仍然是一个完整的Linux发行版。 FR…

【技术分享】利用双网口透传网关实现三菱FX3U PLC远程程序上下载监控

准备工作 一台可联网操作的电脑一台双网口的远程透传网关及博达远程透传配置工具网线两条&#xff0c;用于实现网络连接及连接PLC一台三菱 FX3U PLC及其编程软件一张4G卡或WIFI天线实现通讯(使用4G联网则插入4G SIM卡&#xff0c;WIFI联网则将WIFI天线插入USB口&#xff09; …

如何选择靠谱的软件测试外包公司?CMA、CNAS软件测试报告获取

作为信息科技产业的代表之一&#xff0c;软件公司受到了越来越多的关注&#xff0c;它们的发展为我国的科技创新提供了强大的战略支撑。软件测试作为提升软件产品质量的后盾&#xff0c;日益成为一个专业化、标准化和规范化的行业&#xff0c;软件测试外包公司就是这种背景下成…

安装Centos7

作者&#xff1a;余小小 下载VMware15 参考&#xff1a;http://t.csdnimg.cn/saS9S 下载镜像 这里使用网易镜像库下载 网易开源镜像站http://mirrors.163.com/ 网易Centos下载http://mirrors.163.com/centos/7.7.1908/isos/x86_64/ 安装Centos系统&#xff08;基础设施&…

云安全技术包括哪些?

云安全技术是随着云计算技术的发展而衍生出来的一种安全技术&#xff0c;它利用云计算的分布式处理和数据存储能力&#xff0c;实现对海量数据的快速处理和存储&#xff0c;同时采用机器学习和人工智能技术对数据进行分析和挖掘&#xff0c;以便更好地发现和防御安全威胁。云安…

Java常见算法和lambda

查找算法 public class day11 {public static void main(String[] args) {//基本查找 / 顺序差宅//核心://从0索引开始挨个往后查找//需求:定义一个方法利用基本查找 查询某个元素是否存在//数据如下:{131,127,147,81,103,23,7,79}int[] arr{131,127,147,81,103,23,7,79};int…

如何高效管理多个微信?

看倒这个标题&#xff0c;你是否有以下烦恼&#xff1a; 1.微信账号太多&#xff0c;管理过于麻烦 2.微信号多&#xff0c;需要很多员工来管理&#xff0c;人工费用多 3.多个微信打开后会造成微信登陆界面过多&#xff0c;切换操作十分不方便 4.当微信多的时候&#xff0c;…

mfc140u.dll文件下载的方法指南,教你多种方法修复mfc140u.dll

在面对诸如"mfc140u.dll文件丢失"或者"mfc140u.dll错误"等问题时&#xff0c;许多用户可能会考虑直接从互联网上下载该DLL文件来快速解决问题。确实&#xff0c;此类错误信息经常在尝试运行某些软件&#xff0c;特别是依赖于 Microsoft Visual C Redistrib…

运行时更改Android应用程序图标

设想一下&#xff0c;当我们正在开发一款应用。随着某个节日的临近&#xff0c;我们可能希望通过更改应用图标来增强用户的节日氛围&#xff0c;例如在图标上添“新年特惠”或者“龙年大吉”等标签。 这种小小的改变看似不经意&#xff0c;却能够吸引用户的注意。 运行时更改应…

【Unity动画】Unity 2D动画创建流程

本文以2D为案例&#xff0c;讲解Unity 播放动画的流程 准备和导入2D动画资源 外部导入序列帧生成的 Unity内部制作的 外部导入的3D动画 2.创建动画过程 打开时间轴Ctrl6 选中场景中的一个未来需要播放动画的物体 回到时间轴点击Create一个新动画片段 拖动2D动画资源放入…

记录:Unity脚本的编写10.0

目录 前言实验1: 仿真系统的UI主界面设计1.实验目的2.实验内容3.实验步骤 实验2&#xff1a;仿真系统功能实现1.实验目的2.实验内容3.实验步骤 前言 之前内容的集大成者&#xff0c;一个游戏小demo&#xff0c;虽然很简陋但是还是有一些东西的 实验1: 仿真系统的UI主界面设计…

鸿蒙开发ServiceAbility基本概念

时间过长&#xff0c;开发者必须在Service里创建新的线程来处理&#xff08;详见线程间通信&#xff09;&#xff0c;防止造成主线程阻塞&#xff0c;应用程序无响应。 创建Service 介绍如何创建一个Service 创建Service的代码示例如下&#xff1a;查看获取鸿蒙开发 (qq.com)…

【运维面试100问】(八)如何手动释放内存

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

单电源、轨到轨输入输出、高精度运放MS8551/8552/8554

产品简述 MS8551/8552/8554 是输入输出轨到轨的高精度运算放大器&#xff0c;它 有极低的输入失调电压和偏置电流&#xff0c;单电源电压范围为 1.8V 到 5V 。 轨到轨的输入输出范围使 MS8551/8552/8554 可以轻松地放大高 电平和低电平的传感信号。所有特性使得 MS8…

【Hive】——安装部署

1 MetaData&#xff08;元数据&#xff09; 2 MetaStore &#xff08;元数据服务&#xff09; 3 MetaStore配置方式 3.1 内嵌模式 3.2 本地模式 3.3 远程模式 4 安装前准备 <!-- 整合hive --><property><name>hadoop.proxyuser.root.hosts</name><v…

版本依赖冲突问题排查过程记录

问题 开发平台在集成minio时&#xff0c;pom引入了sdk。 <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version> </dependency>在调用上传文件API时&#xff0c;控制台报错&…

K8S pod无损上下线

在最近的K8s服务上线过程中&#xff0c;我发现了一些问题&#xff0c;更具体的说&#xff0c;我在使用阿里云k8s的过程中注意到&#xff1a;会出现slb短时RT增加&#xff0c;Pod部署初期就达到了扩容上限&#xff0c;并且开始大量的扩容&#xff0c;这无疑占用了大量的k8s资源。…

高压放大器应用场景分析

高压放大器是一种重要的电子设备&#xff0c;其功能是将输入信号的电压幅度放大&#xff0c;以满足不同领域对于信号处理和放大的需求。下面安泰电子将对高压放大器在各个应用场景中的重要性进行深入分析&#xff0c;帮助大家更好地理解和使用高压放大器。 一、音频领域 音乐制…