代码随想录训练营第二十九天打卡|491.递增子序列 46.全排列 47.全排列 II

491.递增子序列

上来模仿着之前子集问题的去重逻辑,结果没能通过。原因是因为之前子集II问题去重是先对数组进行排序,然后进行树层去重。而本题要求收集递增子序列,就不能先排序,之前的去重逻辑就不适用了。那针对这个问题,我们应该如何选择去重逻辑呢?

1.一个最朴素也最容易想到的思路是set哈希表,针对同一树层的元素,之前用过的元素之后就不能再用。所以set哈希表每次回溯时需要进行重置,只能放在回溯函数内部,否则就不只是同一树层元素去重了。

class Solution {
private:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums, int startIndex) {if (path.size() >= 2 && isNondecreasing(path)) {result.push_back(path);}unordered_set<int> uset;for (int i = startIndex; i < nums.size(); i++) {if (uset.find(nums[i]) != uset.end()) //去重continue;uset.insert(nums[i]);path.push_back(nums[i]);   //加入路径数组backtracking(nums, i + 1); //递归之后的元素path.pop_back();           //回溯}}bool isNondecreasing(vector<int> nums) { //判断是否非递减for (int i = 0; i < nums.size() - 1; i++) {if (nums[i] > nums[i + 1])return false;}return true;}public:vector<vector<int>> findSubsequences(vector<int>& nums) {backtracking(nums, 0);return result;}
};

2.但是set去重还是太慢了,有没有更快一点的去重办法呢?注意到数组元素范围在[-100,100]内,元素个数不多,可以考虑用数组哈希表,相比于set减少了映射等时间。

class Solution {
private:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums, int startIndex) {if (path.size() >= 2 && isNondecreasing(path)) {result.push_back(path);}int used[201] = {0}; //数组哈希表for (int i = startIndex; i < nums.size(); i++) {if (used[nums[i] + 100] == 1) //去重continue;used[nums[i] + 100] = 1; //用过的元素记录起来,下次不能用了path.push_back(nums[i]);   //加入路径数组backtracking(nums, i + 1); //递归之后的元素path.pop_back();           //回溯}}bool isNondecreasing(vector<int> nums) { //判断是否非递减for (int i = 0; i < nums.size() - 1; i++) {if (nums[i] > nums[i + 1])return false;}return true;}public:vector<vector<int>> findSubsequences(vector<int>& nums) {backtracking(nums, 0);return result;}
};

3.数组哈希表用时相对于set减少了一半,但是仍然排在后面。相信数组哈希表去重已经是很优秀的去重逻辑了,前面那些花费时间更少的题解是怎么做到的呢?回想起之前的经历,剪枝很多时候能帮助我们减少开销!之前的做法是我们找到一个符合大小的序列然后判断它是否非递减,如果非递减才加入返回数组。这样判断增加了许多时间开销,那能不能在收集路径顺序的时候就直接收集非递减序列呢?可以的,于是有了如下代码,时间开销大大减小,算法性能排在前列。

class Solution {
private:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums, int startIndex) {if (path.size() >= 2) {result.push_back(path);}int used[201] = {0}; //数组哈希表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) {backtracking(nums, 0);return result;}
};

46.全排列

1.这是首次遇到排列问题,那排列与组合有什么不同呢?举个例子,对于数组[1,2]的全组合只有一个[1,2]或[2,1],而它的全排列则有两个[1,2]和[2,1]。在组合问题中我们使用startIndex,每次递归时i+1,因为我们只需要找到一个[1,2]就行,2不用回头找1。但是在排列问题中,2需要回头找1,所以每次递归都需要从头开始,所以没必要再使用startIndex。数组不含重复元素,所以只需要考虑树枝去重。采用used数组,树枝递归过程用过的元素就标记一下。递归是从头开始的,遇见标记过的元素就跳过不再使用。结束当层递归就把元素弹出,并重新标记为未使用,那么树层循环就能找到这个元素了,相当于2能找到1了。

class Solution {
private: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;}for (int i = 0; i < nums.size(); i++) {//同一树枝递归过程中遇到用过的元素就跳过if (used[i] == true)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) {// bool型used数组,大小和nums数组一样大,初始值全设为falsevector<bool> used(nums.size(), false);backtracking(nums, used);return result;}
};

47.全排列 II

1.本题是全排列的进阶,数组元素可以重复。按理说在上一题的基础上,给初始数组排个序,然后多个树层去重不就行了。根据之前的经验,我们写出树层去重代码if (i > 0 && nums[i] == nums[i - 1]) continue;。但我们发现测试用例[1,1,2]直接没过,问题出在哪呢?模拟代码的执行过程,我们发现第一层递归我们选择了1,第二层递归for循环的第一轮我们选择了第一个1,由于树枝去重的关系第一个1被跳过。这时候我们来到for循环的第二轮选择了第二个1,因为和前一个树是相等的这时候树层去重代码直接把第二个1也跳过了。这不对呀,这个1我们是要的啊。究其原因是因为第一个以1开头的排序数组还没有收集完成我们就把它跳过了,那什么时候它才算收集完成呢?根据代码可知一轮收集完成就是for循环完成一轮,在末尾处弹出并重新标记为false。所以我们的树层去重代码应该改为if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false continue;,表示在上一轮收集结束的情况下,如果开头元素还和之前相同,那就跳过。(其实used[i - 1] == true也能达到去重效果,本质上是所有树枝去重达到对前一位的去重,效率不如直接对树层去重。既然used[i - 1]设为true或false都能去重为什么不加不行呢?因为去重逻辑要保持used[i - 1]始终处于一种状态,不能一会true一会false)

class Solution {
private: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;}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) {// bool型used数组,大小和nums数组一样大,初始值全设为falsevector<bool> used(nums.size(), false);sort(nums.begin(), nums.end()); //树层去重要排序backtracking(nums, used);return result;}
};

今日总结:烧脑。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dubbo_入门

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 Dubbo_入门 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、什么是分布式系统二、什么…

Android开发--状态栏布局隐藏的方法

1.问题如下&#xff0c;安卓布局很不协调 2.先将ActionBar设置为NoActionBar 先打开styles.xml 3.使用工具类 package com.afison.newfault.utils;import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graph…

【Elasticsearch篇】详解使用RestClient操作索引库的相关操作

文章目录 &#x1f354;什么是Elasticsearch&#x1f33a;什么是RestClient&#x1f386;代码操作⭐初始化RestClient⭐使用RestClient操作索引库⭐使用RestClient删除索引库⭐使用RestClient判断索引库是否存在 &#x1f354;什么是Elasticsearch Elasticsearch是一个开源的分…

SpringBoot 统计更多Api接口SQL相关日志信息

统计(查询,更新,批量更新)SQL执行次数及用时并输出log import com.zhangziwa.practisesvr.utils.log.LogContext; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts…

什么是通配监听端口? 什么是通配监听IP?

什么是通配监听端口? 监听端口&#xff1a; 指的是服务器或服务开启的特定TCP或UDP端口号&#xff0c;等待客户端连接或发送数据。TCP/IP协议下每个端口只能由一个服务独占监听&#xff0c;一个服务或应用会指定监听特定的一个或多个端口来接收客户端的连接请求。 例如 Web…

RocketMQ-Windows版本安装

RocketMQ-Windows版本安装 1.环境准备 JDK和maven需要先安装好&#xff0c;我这里使用的JDK1.8版本 Maven 3.8.6版本。需要注意的是&#xff0c;这里配置java时需要指定JAVA_HOME环境变量 RokectMQ才能正常启动。 2.下载RocketMQ 官网下载: https://rocketmq.apache.org/z…

C++读取txt文件中的逐个字符

为了增加读取的灵活性&#xff0c;所以separator和filename都设置为在主函数中获取输入或者在函数中传参的视线方法 举个例子&#xff0c;txt文件如下&#xff1a; household;2;true; 首先声明一个读取数据的文件 void read_data_file(const string& filename,char se…

【计算机组成原理】P2 计算机系统的层次结构

计算机系统的层次结构 现代计算机的解题过程计算机语言发展历程早期20世纪50年代20世纪60年代微程序机器 M 0 M_0 M0​操作系统机器 M 2 M_2 M2​ 现代计算机的解题过程 首先将用户用高级语言编写的源程序与数据一起送入计算机内&#xff0c;再由计算机将其翻译成机器能识别…

Android:registerForActivityResult

在《Android:FragmentActivity》中我们提到过Fragment中的onActivityResult已经废弃了,推荐使用registerForActivityResult去注册一个ActivityResultContract契约,从而启动一个forResult的Activity来达到目的,所以这里就看看这个流程是如何进行的。 场景:MainActivity中嵌…

Matplotlib Mastery: 从基础到高级的数据可视化指南【第30篇—python:数据可视化】

文章目录 Matplotlib: 强大的数据可视化工具1. 基础1.1 安装Matplotlib1.2 创建第一个简单的图表1.3 图表的基本组件&#xff1a;标题、轴标签、图例 2. 常见图表类型2.1 折线图2.2 散点图2.3 条形图2.4 直方图 3. 图表样式与定制3.1 颜色、线型、标记的定制3.2 背景样式与颜色…

CC工具箱使用指南:【属性映射】

一、简介 在规划工作中&#xff0c;经常会遇到这样一种情况&#xff0c;有一组一一对应的值。 比如用地编码和用地名称&#xff0c;用地编码【0101】和用地名称【水田】是对应的。 当你在用地编码字段输入【0101】时&#xff0c;用地名称值就必须为【水田】。 当我们确定用地…

gin路由篇

1. 基本路由 gin 框架中采用的路由库是基于httprouter做的 import ("net/http""github.com/gin-gonic/gin" )func main() {// 1.创建路由r : gin.Default()// 2.绑定路由规则&#xff0c;执行的函数// gin.Context&#xff0c;封装了request和responser.…

Nacos源码下载与运行

早先在linux环境下搭建过nacos环境 即Centos安装部署nacos实战&#xff0c;本次是从官网上下载源码&#xff0c;本地运行看看&#xff0c;记录过程&#xff0c;方便备查。 第一步、Nacos源码下载 推荐到nacos官网下载 Github地址&#xff0c;本次选择最新版&#xff0c;1.4.7…

查看Pytorch的GPU是否可用

查看Pytorch的GPU是否可用 import torch torch.cuda.is_available()返回为True表示 Pytorch 的 GPU 可用&#xff0c;返回为False表示 Pytorch 的 GPU 不可用。 其余命令&#xff1a; # 查看cuda是否可用 torch.cuda.is_available() # 返回当前设备索引 torch.cuda.current_d…

openssl3.2/test/certs - 019 - ca-nonca trust variants: +serverAuth, +anyEKU

文章目录 openssl3.2/test/certs - 019 - ca-nonca trust variants: serverAuth, anyEKU概述笔记 ca-nonca.pem from exp 016openssl3.2/test/certs - 019 - ca-nonca trust variants: serverAuth, anyEKUEND openssl3.2/test/certs - 019 - ca-nonca trust variants: serverAu…

Kubeadm安装单master多node节点K8S集群

kubeadm安装k8s1.25版本集群步骤 环境说明实验环境规划集群搭建规划 初始化安装k8s集群的实验环境安装虚拟机更新yum源和操作系统配置机器主机名配置主机hosts文件&#xff0c;相互之间通过主机名互相访问配置主机之间无密码登录关闭交换分区swap&#xff0c;提升性能修改机器内…

Excel导出警告:文件格式和拓展名不匹配

原因描述&#xff1a; Content-Type 原因&#xff1a;Content-Type&#xff0c;即内容类型&#xff0c;一般是指网页中存在的Content-Type&#xff0c;用于定义网络文件的类型和网页的编码&#xff0c;决定文件接收方将以什么形式、什么编码读取这个文件&#xff0c;这就是经常…

Qt单选按钮

前言 本篇文章介绍Qt的单选按钮&#xff0c;就是QRadioButton QRadioButton是一个选项按钮&#xff0c;可以打开&#xff08;选中&#xff09;或关闭&#xff08;取消选中&#xff09;。单选按钮通常向用户提供“众多之一”的选择。 在一组单选按钮中&#xff0c;一次只能选中…

【Linux命令】du 和 df 查看磁盘占用情况

du 和 df 算是一对同门师兄弟&#xff0c;du 侧重在文件夹和文件的磁盘占用方面&#xff0c;而 df 则侧重在文件系统级别的磁盘占用方面。这两个命令都非常的基础&#xff0c;也是每位 Linux 工程师都应该掌握的命令。 1、du 命令&#xff1a; du 是 “disk usage” 的缩写&a…

CommunityToolkit.Mvvm源生成器

引言 MVVM 工具包包含全新的 Roslyn 源生成器&#xff0c;有助于在使用 MVVM 体系结构编写代码时大幅减少样板。这意味着&#xff0c;在编写代码时&#xff0c;MVVM 工具包生成器现在将负责在后台为你生成其他代码。 以前&#xff1a; private string? name;public string?…