代码随想录算法训练营DAY28(记录)|C++回溯算法Part.5|491.递增子序列、46.全排列、47.全排列II

文章目录

  • 491.递增子序列
    • 思路
    • 伪代码
    • CPP代码
    • 优化代码
  • 46.全排列
    • 思路
    • 伪代码
    • CPP代码
  • 47.全排列II
    • CPP代码

491.递增子序列

力扣题目链接

文章链接:491.递增子序列

视频连接:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列

状态:今天没时间了,随便记录一下!

思路

本题与90.子集II非常类似。

也就是本题的递增子序列有要求子集,然后还要去重,但是:本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用之前的去重逻辑!

这里用[4, 7, 6, 7]进行对比。

伪代码

  • 递归函数参数:求子序列,很明显一个元素不能重复使用,所以需要startIndex,调整下一层递归的起始位置。

    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex)
    
  • 终止条件:本题类似求子集问题,也要遍历树形结构找每一个结点,所以和78.子集类似,要遍历树形结构找每一个结点。其实可以不加终止条件,startIndex每次都会加1,并不会无限递归。本题收集结果有所不同,题目要求递增子序列大小至少为2,我们不要加return这样就可以取数上的所有结点啦:

if (path.size() > 1) {
    result.push_back(path);
    // 注意这里不要加return,因为要取树上的所有节点
}
  • 单层搜索逻辑

同一父结点下的同层上使用过的元素就不能再使用了!这里我们使用set来进行去重。

使用set只记录本层元素是否重复使用,新的一层uset都会被重新定义(清空)。

unordered_set<int> uset; // 使用set来对本层元素进行去重
for (int i = startIndex; i < nums.size(); i++) {
    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();
}

CPP代码

// 版本一
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        if (path.size() > 1) {
            result.push_back(path);
            // 注意这里不要加return,要取树上的节点
        }
        unordered_set<int> uset; // 使用set对本层元素进行去重
        for (int i = startIndex; i < nums.size(); i++) {
            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) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

优化代码

这里数值范围较小,我们使用数组来做哈希可以对代码进行优化:

程序运行的时候对unordered_set 频繁的insertunordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且每次重新定义set,insert的时候其底层的符号表也要做相应的扩充,也是费事的。

// 版本二
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        if (path.size() > 1) {
            result.push_back(path);
        }
        int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]
        for (int i = startIndex; i < nums.size(); i++) {
            if ((!path.empty() && nums[i] < path.back())
                    || used[nums[i] + 100] == 1) {
                    continue;
            }
            used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

46.全排列

46.全排列
文章链接:46.全排列

视频连接:组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列

状态:今天没时间了,随便记录一下!

思路

排列与组合(包括分割和子集都近似于组合问题,所以我们总是处理前对其进行排序)相比,排列是有顺序的。总而言之排列是强调元素顺序的

全排列问题的树形结构如图:

从树形结构可以看出,我们每次都是从头开始,即使元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。

这里我们会使用used数组,记录此时path里都有哪些元素使用啦,一个排列里一个元素只能使用一次。

伪代码

  • 递归函数参数:正如上文中所讲的,我们需要一个used数组
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used)
  • 递归终止条件:

    • 上图中可以看出,叶子结点收获结果,那么如何判断到了叶子结点呢,本题的判断很简单,path数组的大小达到和nums数组一样大的时候,就说明找到了全排列
    // 此时说明找到了一组
    if (path.size() == nums.size()) {
        result.push_back(path);
        return;
    }
    
  • 单层搜索的逻辑:

    • 排列问题和之前写的组合、切割、子集问题最大的不同就是不用startindex来指明遍历的位置了,因为我们每次都要从头开始搜索,正如上文描述的那样。这里我们使用used。
    for (int i = 0; i < nums.size(); i++) {
        if (used[i] == true) continue; // path里已经收录的元素,直接跳过
        used[i] = true;
        path.push_back(nums[i]);
        backtracking(nums, used);
        path.pop_back();
        used[i] = false;
    }
    

CPP代码

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

47.全排列II

力扣题目链接

文章链接:47.全排列II

视频连接:回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II

状态:今天没时间了,随便记录一下!

这题很有意思,这里给定的是一个包含了重复数字的序列,要返回所有不重复的全排列。很明显,需要去重

那么我们这里要判断了,是树层去重呢,还是树枝去重?

![在这里插入图片描述]()
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
    continue;
}

我们在40.组合总和II、90.子集II中已经详细论述过去重逻辑的写法,这里直接给出代码

CPP代码

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.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) {
        result.clear();
        path.clear();
        sort(nums.begin(), nums.end()); // 排序
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

// 时间复杂度: 最差情况所有元素都是唯一的。复杂度和全排列1都是 O(n! * n) 对于 n 个元素一共有 n! 中排列方案。而对于每一个答案,我们需要 O(n) 去复制最终放到 result 数组
// 空间复杂度: O(n) 回溯树的深度取决于我们有多少个元素

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

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

相关文章

安装GPT 学术优化 (GPT Academic)@FreeBSD

GPT 学术优化 (GPT Academic)是一个非常棒的项目 可以帮助我们完成中科院的一些日常工作。 官网&#xff1a;GitHub - binary-husky/gpt_academic: 为GPT/GLM等LLM大语言模型提供实用化交互接口&#xff0c;特别优化论文阅读/润色/写作体验&#xff0c;模块化设计&#xff0c;…

win2022服务器apache配置https(ssl)真实环境实验(避坑之作)不依赖宝塔小皮等集成环境

本次实验背景&#xff1a; 完全参考官方 https://cloud.tencent.com/document/product/400/4143 文档流程&#xff0c;没有搞定&#xff0c;于是写下避坑之作。 服务器&#xff1a;腾讯云轻量应用服务器 操作系统&#xff1a; Windows Server 2022 DataCenter 64bit CN apache…

51-41 Stable Video Diffusion,高质量视频生成新时代

23年11月&#xff0c;Stability AI公司公开了稳定视频扩散模型Stable Video Diffusion(SVD)的代码和权重&#xff0c;视频生成迎来了新时代。SVD是一种潜在扩散模型&#xff0c;支持文本生成视频、图像生成视频以及物体多视角3D合成。从工程角度来看&#xff0c;本文主要提出了…

C++如何使用string类

文章目录 为什么要学习string?库中的string关于编码ASCII编码Unicode编码 迭代器Iteratorsstring常用构造接口接口声明与功能说明接口演示 string类对象的容量操作接口声明与功能说明接口演示reverse与resize在不同平台下的扩容与缩容机制 string类对象的访问及遍历操作接口声…

Java项目实现图形验证码(Hutool)

项目架构&#xff1a; 使用SpringCloudmysqlmybatis-plus需要将数据库中的数据导出到Excel文件中 前端为Vue2 业务场景&#xff1a; 登录时使用验证码登录 1.1 打开hutool, 搜索 图片验证码 1.2后端编写生产验证码方法 1.3前端 1.3.1展示验证码 1.3.2 前端方法 1.3.2.1UU…

Django中的数据库优化与ORM性能调优【第169篇—ORM性能调优】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Django中的数据库优化与ORM性能调优 在开发基于Django的Web应用程序时&#xff0c;数据库是…

ubuntu 查询mysql的用户名和密码 ubuntu查看username

ubuntu 查询mysql的用户名和密码 ubuntu查看username 文章标签mysqlUbuntu用户名文章分类MySQL数据库 一.基本命令 1.查看Ubuntu版本 $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.5 LTS Release: 16.04 Coden…

leetcode-分割链表

题目 面试题 02.04. 分割链表 提示 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1&#xff1a; 输入&#xff…

linux-centos虚拟机设置固定ip

环境准备 虚拟机版本&#xff1a;centos7 安装环境&#xff1a;vmware17 1、设置网络连接 虚拟机-设置-网络适配器-NAT模式 2、查看子网信息 编辑-虚拟网络编辑器-NAT模式-NAT设置 查看子网ip和网关ip 下一步要用 3、修改配置文件 vim /etc/sysconfig/network-scripts…

BGP边界网关路由实验(华为)

一&#xff0c;技术简介 BGP&#xff08;边界网关路由协议&#xff09;是一种自治系统&#xff08;AS&#xff09;间的协议&#xff0c;主要用于在不同的AS之间交换路由信息。AS是一个由一组网络设备和路由器组成的网络集合&#xff0c;这些设备可以在一个共同的管理域中协同工…

Netty-NioServerSocketChannel与NioSocketChannel

NioServerSocketChannel NioServerSocketChannel是netty服务端的channel。在ServerbootStrap的bind方法中&#xff0c;通过反射&#xff0c;实例化对象NioServerSocketChannel。   NioServerSocketChannel对象实例化的过程中。 AbstractChannel中实例化channel的id&#xff…

【QT进阶】Qt Web混合编程之QWebEngineView基本用法

往期回顾 【QT入门】Qt自定义控件与样式设计之自定义QTabWidget实现tab在左&#xff0c;文本水平的效果-CSDN博客【QT进阶】Qt Web混合编程之CEF、QCefView简单介绍-CSDN博客 【QT进阶】Qt Web混合编程之VS2019 CEF的编译与使用-CSDN博客 【QT进阶】Qt Web混合编程之QWebEngi…

通过Idea部署Tomcat服务器

1.在idea中创建项目 有maven构建工具就创建maven&#xff0c;没有就正常创建一个普通的java程序 创建普通java项目 2.添加框架 3.配置 Tomcat 注意&#xff1a;创建web项目后我们需要配置tomcat才能运行&#xff0c;下面我们来进行配置。 4.添加部署 回到服务器 5.完善配置 6…

EFK环境搭建(基于K8S环境部署)

目录 一.环境信息二.安装nfs供应商三.安装elasticsearch四.安装kibana组件五.安装fluentd 一.环境信息 1.服务器及k8s版本 IP地址主机名称角色版本192.168.40.180master1master节点1.27192.168.40.181node1node1节点1.27192.168.40.182node2node2节点1.27 2.部署组件版本 序…

Python 数据结构和算法实用指南(二)

原文&#xff1a;zh.annas-archive.org/md5/66ae3d5970b9b38c5ad770b42fec806d 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第四章&#xff1a;列表和指针结构 我们已经在 Python 中讨论了列表&#xff0c;它们方便而强大。通常情况下&#xff0c;我们使用 Python…

近端安全互联样例使用指导

样例介绍 本样例基于rk3568开发板&#xff0c;通过封装openharmony安全子系统deviceauth组件提供的能力&#xff0c;实现了一组可用于设备间快速建立可信认证和连接的接口&#xff0c;通过预先定义关系网&#xff0c;在设备初始化阶段完成端端设备间的认证&#xff0c;构建安全…

ES源码四:网络通信层流程

听说ES网络层很难&#xff1f;今天来卷它&#x1f604; 前言 ES网络层比较复杂&#xff0c;分为两个部分&#xff1a; 基于HTTP协议的REST服务端基于TCP实现的PRC框架 插件化设计的网络层模块&#xff08;NetworkModule&#xff09; 入口还是上一章的创建Node构造方法的地方…

目标检测应用场景—数据集【NO.31】布匹数据集目标检测数据集

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

uniapp picker 多列选择器用法

uniapp picker 多列选择器联动筛选器交互处理方法&#xff0c; uniapp 多列选择器 mode"multiSelector" 数据及筛选联动交互处理&#xff0c; 通过接口获取数据&#xff0c;根据用户选择当前列选项设置子列数据&#xff0c;实现三级联动效果&#xff0c; 本示例中处…

【honggfuzz学习笔记】honggfuzz的基本特性

本文架构 1.动机2.honggfuzz的基本概念官网描述解读 3. honggfuzz的反馈驱动(Feedback-Driven)软件驱动反馈&#xff08;software-based coverage-guided fuzzing&#xff09;代码覆盖率代码覆盖率的计量单位 代码覆盖率的统计方式 硬件驱动反馈&#xff08; hardware-based co…