代码随想录训练营Day30 | 491.递增子序列 | 46.全排列 | 47.全排列 II

news2024/11/24 22:33:17

学习文档:代码随想录 (programmercarl.com)

学习视频:代码随想录算法公开课 | 最强算法公开课 | 代码随想录 (programmercarl.com)

Leetcode  491. 非递减子序列

题目描述

给你一个整数数组 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]]

提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

解题思路 

这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。与90.子集II类似也是取子集,也需要去重

在90.子集II (opens new window)中我们是通过排序,再加一个标记数组来达到去重的目的。而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。

回溯算法三部曲

1.确定递归函数参数

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

vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex)

2.确定终止条件

 本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和回溯算法:求子集问题! (opens new window)一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。

// 子集里面元素大于1 就开始收集
if (path.size() > 1) {
    result.push_back(path);
    // 注意这里不要加return,因为要取树上的所有节点
}

3.确定单层搜索逻辑

 同一父节点下的同层上使用过的元素就不能再使用了

// 这里uset是局部变量 递归后会重新创建uset,这个uset是记录一层 与之前的used数组不同
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();
}

完整代码

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;
    }
};

注意:掌握一种去重逻辑即可!

Leetcode  

题目描述

给定一个不含重复数字的数组 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]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

解题思路

排列问题是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方

回溯三部曲

1.确定递归函数参数

排列问题需要一个used数组,标记已经选择的元素

vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used)

2. 确定递归终止条件

可以看出叶子节点,就是收割结果的地方。当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点。

// 此时说明找到了一组
if (path.size() == nums.size()) {
    result.push_back(path);
    return;
}

3.确定单层搜索的逻辑

这里和77.组合问题 (opens new window)、131.切割问题 (opens new window)和78.子集问题 (opens new window)最大的不同就是for循环里不用startIndex了。

因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。

而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次

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;
}

完整代码

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;
    }
};

Leetcode 47. 全排列 II

题目描述

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

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

解题思路

 这道题目和46.全排列 (opens new window)的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列还要强调的是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了

一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果

完整代码

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;
    }
};

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

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

相关文章

AI赋能!0基础小白自媒体创业,成功率提升90%的秘诀?

本文背景 好多小伙伴也想写公众号文章&#xff0c;但是自己又没有写过&#xff0c;不知道如何开始。 今天分享个小方法&#xff0c;就算是写作新手&#xff0c;也能靠 AI 快速上手&#xff0c;写出好内容&#xff01; 一起来看看怎么用 AI 工具 助力写作&#xff0c;提高效率&a…

STM32(十八):实时时钟

时间戳 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒。 时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整型变量。 世界上所有时区的秒计数器相同&#xff0c;不同时…

一次性使用病毒采样管-保存运输呼吸道 肠道等多种病毒样本的有力工具!

远离了新冠病毒却又来了流感病毒、手足口、猴痘病毒、诺如病毒等多种病毒&#xff0c;对于众多病毒的检测诊断&#xff0c;确保病毒样本的高效采集、安全保存和准确运输是至关重要的。为了满足这一需求&#xff0c;一次性使用病毒采样管应运而生。 在研究、医学诊断和疫情监测…

10.12 标准IO

练习&#xff1a;针对fscanf和fprintf练习 有如下结构体&#xff1a; typedef struct Student { char name[20]; int id; float chinese;//语文成绩 float math; float english; float physical; float chemical; float biological; }stu_t; *Pstu_t//声明学生结构体类型 在栈区…

逆向思维的力量:Prolog在游戏编程中的应用与代码实践

在主流游戏开发语言如C++、Python和Unity统治的今天,Prolog作为一种基于逻辑编程的语言,似乎与游戏开发不太沾边。然而,Prolog的逻辑推理机制和简洁的语法在解决复杂逻辑问题上有着独特的优势,尤其是在人工智能(AI)决策和路径规划等领域。通过Prolog,我们可以以极简的代…

【C++】C++的引用

一.引用 1.引用的概念和定义 引用不是新定义⼀个变量&#xff0c;而是给已存在变量取了⼀个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同⼀块内存空间。 类型& 引用别名 引用对象; 2.引用的特征 a.引用在定义时必须初始化 …

从数据到结论:ChatGPT如何帮助你完成复杂的数据分析?

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 在撰写学术论文时&#xff0c;数据分析往往是最具挑战性的部分之一。无论是定量研究还是定性研究&#xff0c;如何有效分析数据、得出合理结论都是关键所在。 ChatGPT的优势 1. 快速处理大量数据 在面对大…

Qt-窗口对话框QColorDialog的使用(52)

目录 描述 常用方法 使用 描述 颜⾊对话框的功能是允许⽤⼾选择颜⾊。继承⾃ QDialog 类。颜⾊对话框如下图⽰&#xff1a; 常用方法 1、QColorDialog (QWidget *parent nullptr) //创建对象的同时设置⽗对象 2、QColorDialog(const QColor &initial, QWidget *paren…

2024开放原子开源生态大会 | 麒麟信安携手openEuler共建开源生态,共塑产业未来

9月25日-27日&#xff0c;由开放原子开源基金会主办的2024开放原子开源生态大会在北京开幕&#xff0c;大会以“开源赋能产业&#xff0c;生态共筑未来”为主题。工业和信息化部党组书记、部长金壮龙&#xff0c;北京市委副书记、市长殷勇&#xff0c;工业和信息化部总经济师、…

Chromium HTML Tags与c++接口对应关系分析

一、HTML 标签(HTML Tags) <a> <head> <img>等等这些标签在c中的接口是如何定义和查找的呢&#xff1f; 更多标签参考&#xff1a; HTML <a> target 属性 (w3school.com.cn) 二、html_tag_names.json5 (third_party\blink\renderer\core\html\htm…

QD1-P10 HTML 超链接标签(a)下篇

本节学习&#xff1a;HTML a 标签实现大纲跳转&#xff08;锚点&#xff09;。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p10 本节实现大纲&#xff08;锚点&#xff09;跳转 ​​ 完整 HTML 代码 <!DOCTYPE html> <html><head><meta charset&…

python爬虫--某动漫信息采集

python爬虫--tx动漫 一、采集主页信息二、采集详情页信息三、代码供参考一、采集主页信息 略。 二、采集详情页信息 如上图所示,使用xpath提取详情页的标题、作者、评分、人气、评论人数等数据。 三、代码供参考 import csv import time import random import requests fr…

弧光保护装置的应用

长期以来&#xff0c;人们对中低压母线的保护一直不够重视&#xff0c;没有保护来快速切除配电站所内部发生的故障&#xff0c;往往使故障发展扩大造成开关设备被严重烧毁&#xff0c;有的甚至发展成“火烧连营”的事故。同时&#xff0c;变电站的变压器由于遭受外部短路电流冲…

【harmonyOS开发笔记3】ArkTS中数组的使用

数组的定义 数组&#xff1a;是一个容器&#xff0c;可以存储多个数据 定义数组的格式&#xff1a; let 数组名: 类型[] [数据1&#xff0c; 数据2&#xff0c; ] 示例&#xff1a;let names: string[] [小明, 小红] // 数组 let 数组名: 类型[] [数据1, 数据2, ] let …

【大模型实战教程】Qwen2.5 PyTorch模型微调入门实战

1 引言 Qwen2.5是Qwen大型语言模型系列的最新成果。对于Qwen2.5&#xff0c;通义千问团队发布了从0.5到720亿参数不等的基础语言模型及指令调优语言模型。Qwen2.5相比Qwen2带来了以下改进&#xff1a; 显著增加知识量&#xff0c;在编程与数学领域的能力得到极大提升&#xf…

四种Agent落地方案:Coze、AutoGen、Langchain、llama-index

前言 1. Coze 优势&#xff1a; 模块化设计&#xff1a; 允许开发者灵活地添加或替换组件&#xff0c;适应不同的应用需求。 易于集成&#xff1a; 支持与多种外部系统和服务的集成&#xff0c;方便与现有业务系统对接。 用户友好&#xff1a; 提供图形化界面和命令行工具&…

讯飞与腾讯云:Android 语音识别服务对比选择

目录 一、讯飞语音识别 1.1 讯飞语音识别介绍 1.1.1 功能特点 1.1.2 优势 1.2 接入流程 1.2.1 注册账号并创建应用 1.2.2 下载SDK等相关资料 1.2.3 导入SDK 1.2.4 添加用户权限 1.2.5 初始化讯飞SDK 1.2.6 初始化语音识别对象 1.2.7 显示结果 二、腾讯云语音识别 …

数字人直播带货操作详解,必看!

作为人工智能和直播带货两大热门行业的结合体&#xff0c;数字人直播带货一经出现就引发了众多商家的关注&#xff0c;许多创业者更是因此对数字人直播这一创业项目产生了浓厚的兴趣&#xff0c;连带着对数字人直播带货怎么操作等问题的讨论也日渐热烈了起来。 本期&#xff0…

给图片加文字的软件有哪些?这几种图片编辑文字软件简单好用

一张优秀的图片往往能瞬间抓住人们的眼球&#xff0c;无论是自媒体运营、广告设计还是日常生活分享&#xff0c;给图片添加文字都是提升视觉效果的重要手段。很多小伙伴不清楚该怎样给图片添加上精美的文字&#xff0c;下面给大家分享几种好用编辑文字的方法&#xff0c;一起来…

双语大脑的神经可塑性能力:来自健康和病理个体的见解

摘要 双语经验的神经印记对于理解大脑如何处理优势语言和非优势语言至关重要&#xff0c;但关于它的研究仍然没有定论。不同的研究表明神经处理存在相似性或差异性&#xff0c;这对患有脑肿瘤的双语患者具有重要意义。保留术后的双语功能需要考虑到术前的神经可塑性变化。在这…