算法练习第25天|491. 非递减子序列

 491. 非递减子序列

491. 非递减子序列icon-default.png?t=N7T8https://leetcode.cn/problems/non-decreasing-subsequences/

题目描述:

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]
  • -100 <= nums[i] <= 100

思路分析:

注意,本题不能像算法练习第24天|78.子集、 90.子集II-CSDN博客中的90.子集II那样对元素组进行排序已达到元素子序列去重的目的,可以看上面的示例2,如果我们按照90题那样的做法对原数组进行排列的话【1,2,3,4,4】,就会得出不止一个非递减子序列,这显然与题目输出的【4,4】不符合。所以我们不能对原序列进行排序。

本题给出的示例,还是一个有序数组 [4, 6, 7, 7],这更容易误导大家按照排序的思路去做了。

为了有鲜明的对比,我用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:

按照正常的前后顺序进行搜索,会发现两种情况下元素是不能记录的:

(1)如果当前元素比刚刚记录的元素小,那么当前元素就不能往path中添加,因为此时不符合非递减的性质。

(2)同一父节点下的那一层遍历,如果元素之前用过,那么也不能向path中添加。

上面两种情况任意一种发生,path就不能记录当前元素。所以这两种情况对应代码的逻辑关系是或||

下面开始日常的回溯三部曲:

第一步:确认回溯函数的参数与返回值。由于需要在一个集合里面取序列,所以要用到startIndex.

 vector<int> path;vector<vector<int>> result;void backTracking(vector<int> & nums, int startIndex){}

第二步:确认回溯终止条件。当startIndex达到nums.size()之后就遍历完了,return。

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

第三步:确认单层遍历逻辑。此时就要考虑到我们当前的元素nums[i]是否是上面所述的两种不能记录的情况了。条件(1)如果当前元素比刚刚记录的元素小,用(!path.empty() && nums[i] < path.back())表示;条件(2)同一父节点下该元素(数值)之前用过,用used_numbers[nums[i]+100] == 1表示。

因为题目提示了nums所有元素-100 <= nums[i] <= 100,所以我们使用一个used_numbers数组来记录元素是否用过。由于数组的下标是从0开始算的,所以我们将nums[i]+100,将元素的范围【-100,100】线性拉伸到【0,200】,总共201个数。例如,当前元素为-100时,它存在数组的开始处,当元素为-99时,它存在数组的下标1处,依次类推。使用了该元素,则对应元素置1。另外也可以用set来记录用过的数据。

        int used_numbers[201] = {0};  //记录统一父节点下哪些数字是用过的for(int i = startIndex; i < nums.size(); i++){if((!path.empty() && nums[i] < path.back())|| used_numbers[nums[i]+100] == 1)continue;//不满足if条件则表示该节点可以记录,那么记录当前节点path.push_back(nums[i]);//判断path长度是否大于等于2,如果是,则reslut记录if(path.size() > 1){result.push_back(path);}//-100到100映射到0-201used_numbers[nums[i]+100] = 1;  //用过该数字,标志为置1//因为子序列最少要有两个元素,所以我们平常的result.push_back(path)就不能直接写了//result.push_back(path);backTracking(nums, i+1);path.pop_back();}

整体代码如下:

class Solution {
public:vector<int> path;vector<vector<int>> result;void backTracking(vector<int> & nums, int startIndex){if(startIndex == nums.size()){return ;}int used_numbers[201] = {0};  //记录统一父节点下哪些数字是用过的for(int i = startIndex; i < nums.size(); i++){if((!path.empty() && nums[i] < path.back())|| used_numbers[nums[i]+100] == 1)continue;//记录当前节点path.push_back(nums[i]);if(path.size() > 1){result.push_back(path);}//-100到100映射到0-201used_numbers[nums[i]+100] = 1;  //用过该数字,标志为置1//因为子序列最少要有两个元素,所以我们平常的result.push_back(path)就不能直接写了//result.push_back(path);backTracking(nums, i+1);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {backTracking(nums, 0);return result;}
};

下面是使用unordered_set<int>来记录重复元素的写法:

class Solution {
public:vector<int> path;vector<vector<int>> result;void backTracking(vector<int> & nums, int startIndex){if(startIndex == nums.size()){return ;}unordered_set<int> used_numbers;  //记录统一父节点下哪些数字是用过的for(int i = startIndex; i < nums.size(); i++){if((!path.empty() && nums[i] < path.back())|| used_numbers.find(nums[i]) != used_numbers.end())continue;//记录当前节点path.push_back(nums[i]);if(path.size() > 1){result.push_back(path);}//-100到100映射到0-201used_numbers.insert(nums[i]);  //用过该数字,标志为置1//因为子序列最少要有两个元素,所以我们平常的result.push_back(path)就不能直接写了//result.push_back(path);backTracking(nums, i+1);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {backTracking(nums, 0);return result;}
};

注意:不管是使用数组还是set来存放使用过的数字,它们都只存在与当前递归层,即下一层的递归中数组和set都会重新创建并初始化,然后for循环在同一层中遍历,这就保证了同一父节点下可以查找元素使用已经用过。

另外,在使用set时,程序运行的时候对unordered_set 频繁的insert,unordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且每次重新定义set,insert的时候其底层的符号表也要做相应的扩充,也是费事的。使用数组程序还快一些。算法训练第5天|哈希表理论基础 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和-CSDN博客

在上面这篇博文349题中,提到了数组和set作为哈西表时各自的应用场景:

而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费,优先使用set和map。数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如果数值范围小的话能用数组尽量用数组

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

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

相关文章

Flutter 中的 ButtonTheme 小部件:全面指南

Flutter 中的 ButtonTheme 小部件&#xff1a;全面指南 Flutter 是一个由 Google 开发的跨平台 UI 框架&#xff0c;它提供了一系列的组件来帮助开发者构建美观且功能丰富的应用。在 Flutter 的组件库中&#xff0c;ButtonTheme 是一个重要的小部件&#xff0c;它允许开发者统…

Linux、Windows安装python环境(最新版及历史版本指定版本)-python

目录 一、Linux环境二、windows环境最新版本下载指定版本下载 python 官网地址&#xff1a; https://www.python.org/ 一、Linux环境 以openEuler/CentOS为例 查看可安装python源版本 dnf provides python*默认安装新版本 dnf install -y python3. 进入python python退出p…

电源小白入门学习8——电荷泵电路原理及使用注意事项

电源小白入门学习8——电荷泵电路原理及使用注意事项 电荷泵简介电荷泵原理电荷泵设计过程中需要注意的点fly电容的安秒平衡DC/DC功率转换技术对比 电荷泵简介 电荷泵&#xff08;Charge Pump&#xff09;是一种电路拓扑结构&#xff0c;用于实现电压升压或降压的功能。它通过…

Python自动化测试断言详细实战代码(建议收藏)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在测试用例中&#xff0c;执行完测试用例后&#xff0c;最后一步是判断测试结果是 pass 还是 fa…

sh发送邮件如何通过配置SMTP服务器来实现?

sh发送邮件的操作方法&#xff1f;如何使用Shell脚本自动发信&#xff1f; 在Shell脚本中实现邮件发送功能是一项常见需求&#xff0c;特别是在自动化任务执行或系统监控中。AokSend将介绍如何通过配置SMTP服务器来实现sh发送邮件的方法和注意事项。 sh发送邮件&#xff1a;安…

Redash、Superset、DataEase、Metabase、FineBI 和 Power BI 报表系统的优缺点

最近在做报表系统的选型与调研&#xff0c;其中尝试了Redash、Superset、DataEase、Metabase、FineBI 和 Power BI几个报表系统&#xff0c;主要想使用开源免费的&#xff0c;如果大家有好用的报表系统推荐欢迎留言。 Redash 优点&#xff1a; 开源且免费&#xff1a;Redash…

【已解决】Error in the HTTP2 framing layer

1.问题描述 在使用git将代码上传github的时候在最后一部push的时候遇到这个fatal 2.解决方案 由于我原先设置的origin是http协议下的&#xff0c;如下 git remote add origin https://github.com/Charlesbibi/Simple_Cloud.githttp协议下行不通不妨试一试ssh协议下&#xff…

跟风报考PMP,我真的后悔了

真的太香吧&#xff01; 我一开始没打算报考PMP证书的&#xff0c;但是我看身边很多朋友都因为PMP证书得到了升职加薪&#xff0c;这让我实在是一整个羡慕住了&#xff0c;所以我也去报考了PMP。 报考PMP前期我做了什么&#xff1f; 由于我是零基础&#xff0c;没有什么项目…

探索网格生成技术在AI去衣应用中的作用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;其在图像处理和计算机视觉领域的应用日益广泛。其中&#xff0c;AI去衣技术作为一种新兴的应用&#xff0c;引起了广泛的关注和讨论。然而&#xff0c;要实现这一功能并非易事&#xff0c;需要借助于先进的算法和技术。…

Mybatis第一讲——你会Mybatis吗?

文章目录 什么是MybatisMybatis的作用是什么 Mybatis 怎么使用注解的方式注解的多种使用Options注解ResultType注解 XML的方式update标签 #{} 和 ${}符号的区别#{}占位${}占位 ${}占位的危险性(SQL注入)数据库连接池 什么是Mybatis 首先什么是Mybatis呢&#xff1f;Mybatis是一…

latex bib引参考文献

1.bib内容 2.sn-mathphys-num是官方的参考文献格式 3.不用导cite包&#xff0c;文中这么写 4.end document前ckwx是自己命名的bib的名字

Ollama教程,本地部署大模型Ollama,docker安装方法,仅供学习使用

不可商用&#xff01;&#xff01;仅仅提供学习使用&#xff01; 先上视频教学&#xff1a; Ollama教程&#xff0c;本地部署大模型Ollama&#xff0c;docker安装方法&#xff0c;仅供学习使用&#xff01; 资料获取 &#xff1a; Ollama下载包和安装文档在这里&#xff1…

Web自动化测试-掌握selenium工具用法,使用WebDriver测试Chrome/FireFox网页(Java

目录 一、在Eclipse中构建Maven项目 1.全局配置Maven 2.配置JDK路径 3.创建Maven项目 4.引入selenium-java依赖 二、Chrome自动化脚本编写 1.创建一个ChromeTest类 2.测试ChromeDriver 3.下载chromedriver驱动 4.在脚本中通过System.setProperty方法指定chromedriver的…

vi和vim有什么不同?

vi 和 vim 都是流行的文本编辑器&#xff0c;它们之间有以下主要区别&#xff1a; 历史&#xff1a; vi 是一个非常古老的文本编辑器&#xff0c;最初由 Bill Joy 在 1976 年为 Unix 系统编写。vim&#xff08;Vi IMproved&#xff09;是 vi 的一个增强版&#xff0c;由 Bram M…

Ubuntu 20.04安装CMake 3.22.6版本

Ubuntu 20.04通过apt安装的cmake版本是3.16.3&#xff0c;默认安装到/usr/bin/cmake路径。 $ cmake Command cmake not found, but can be installed with:sudo snap install cmake # version 3.29.3, or sudo apt install cmake # version 3.16.3-1ubuntu1.20.04.1See sna…

Multer 文件上传中间件 和 Busboy表单解析

Multer 是一个node.js中间件&#xff0c;用于处理 multipart/form-data类型的表单数据&#xff0c;主要用于上传文件。只处理 multipart/form-data 类型的表单数据。 Multer是基于Busboy解析的文件参数信息&#xff0c;获取fileStream&#xff0c;并通过storage转存的file.str…

Unity + 雷达 粒子互动(待更新)

效果预览: 花海(带移动方向) VFX 实例 脚本示例 使用TouchScript,计算玩家是否移动,且计算移动方向 using System.Collections; using System.Collections.Generic; using TouchScript; using TouchScript.Pointers; using UnityEngine; using UnityEngine.VFX;public …

AI预测福彩3D采取888=3策略+和值012路一缩定乾坤测试6月1日预测第8弹

今天继续基于8883的大底&#xff0c;使用尽可能少的条件进行缩号。好了&#xff0c;直接上结果吧~ 首先&#xff0c;888定位如下&#xff1a; 百位&#xff1a;6,5,4,7,8,9,1,0 十位&#xff1a;7,8,6,5,9,3,1,0 个位&#xff1a;5,7,6,4,2,…

看广告赚金币提现小游戏app开发源码

开发一个看广告赚金币并可以提现的小游戏APP&#xff0c;源码的搭建涉及到多个方面&#xff0c;包括前端界面设计、后端逻辑处理、数据库管理以及广告平台的对接等。以下是一些建议的步骤和考虑因素&#xff1a; 前端界面设计&#xff1a; 使用HTML5、CSS3和JavaScript等技术…

第十三届蓝桥杯B组c++国赛

A - 2022&#xff1a; 题目&#xff1a; 笔记&#xff1a; 一道经典的dp题&#xff1a; &#xff08;1&#xff09;明确dp数组含义&#xff1a; dp[i][j][k]: 表示前i个数字中选择j个凑成k的方法数。 &#xff08;2&#xff09;确定状态转移方程&#xff1a; dp[i][j][k…