24暑假算法刷题 | Day25 | 回溯算法 III | LeetCode 491. 非递减子序列,46. 全排列,47. 全排列 II

news2024/9/21 23:16:33

目录

  • 491. 非递减子序列
    • 题目描述
    • 题解
  • 46. 全排列
    • 题目描述
    • 题解
  • 47. 全排列 II
    • 题目描述
    • 题解


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

题解

回溯算法解决。首先搭建基本框架:

class Solution
{
private:
    vector<int> path;
    vector<vector<int>> res;

public:
    void backTracking(const vector<int> &nums, int start) {
        // 每次将当前非递减子序列加入结果集
        if (path.size() > 1)
            res.push_back(path); 
        // 递归出口(纵向遍历)
        if (start >= nums.size())
            return;
        // 横向遍历
        for (int i = start; i < nums.size(); ++i) {
            // 处理
            if (path.empty() || nums[i] >= path.back())
                path.push_back(nums[i]);
            else
                continue;
            backTracking(nums, i + 1); // 递归
            path.pop_back();           // 回溯
        }
    }

    vector<vector<int>> findSubsequences(vector<int> &nums)
    {
        backTracking(nums, 0);
        return res;
    }
};

但是这样会导致重复子序列的问题,例如:

img

于是思考去重方案。本题和 40. 组合总和 II 一样,都需要对回溯树型结构中 “同一父节点下的同一层” (下面简称 “同一层” )节点值进行去重。那道题使用了 used 数组(详解参见 笔记-40(github) / 笔记-40(csdn) ),但本题不能直接用那个套路,因为那种算法需要先将原数组 排序 从而将重复值放在一起,而本题就是要求原数组中的有序列,自然不能先排序。

考虑用一个 集合 存储当前同一层使用过的节点值,从而避免同一层的节点值重复。由于同一层的节点选取是横向遍历的过程,即某次回溯函数中的 for 循环部分,故可以在此之前新建一个局部的 unordered_set ,就可以方便的记录当前这层用过的节点值了。

扒一张 代码随想录 上的树形图,便于进一步理解:

在这里插入图片描述

⚠️ 如果放在回溯函数外,该集合成为全局变量,处理起来会不方便

代码(C++)

class Solution
{
private:
    vector<int> path;
    vector<vector<int>> res;

public:
    void backTracking(const vector<int> &nums, int start)
    {
        // 每次将当前非递减子序列加入结果集
        if (path.size() > 1)
            res.push_back(path);
        // 递归出口(纵向遍历)
        if (start >= nums.size())
            return;
        // 横向遍历
        unordered_set<int> used; // 记录本层节点用过的值
        for (int i = start; i < nums.size(); ++i)
        {
            // 去重
            if (used.find(nums[i]) != used.end())
                continue;
            used.insert(nums[i]);
            // 处理
            if (path.empty() || nums[i] >= path.back())
                path.push_back(nums[i]);
            else
                continue;
            backTracking(nums, i + 1); // 递归
            path.pop_back();           // 回溯
        }
    }

    vector<vector<int>> findSubsequences(vector<int> &nums)
    {
        backTracking(nums, 0);
        return res;
    }
};

46. 全排列

点此跳转题目链接

题目描述

给定一个不含重复数字的数组 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 中的所有整数 互不相同

题解

回溯算法解决。

基本框架采用经典的 回溯三部曲:处理、递归、回溯

去重用 used 数组解决,本题对应的是回溯树形结构中对 同一个树枝上的节点值 进行去重。

回溯三部曲可参考:笔记-回溯算法 I

used 数组去重方法可参考:笔记-回溯算法 II 中的 40. 组合总和 II

代码(C++)

class Solution
{
private:
    vector<int> path;
    vector<vector<int>> res;
    vector<int> used; // 记录数字的使用情况,用于去重

public:
    void backTracking(const vector<int> &nums)
    {
        // 递归出口(纵向遍历)
        if (path.size() == nums.size())
        {
            res.push_back(path);
            return;
        }
        // 横向遍历
        for (int i = 0; i < nums.size(); ++i)
        {
            // 去重
            if (used[i])
                continue;
            used[i] = 1;
            path.push_back(nums[i]); // 处理
            backTracking(nums);      // 递归
            path.pop_back();         // 回溯
            used[i] = 0;
        }
    }

    vector<vector<int>> permute(vector<int> &nums)
    {
        used.resize(nums.size());
        backTracking(nums);
        return res;
    }
};

另外这题还有种不用刻意去重的解法:

class Solution
{
private:
    vector<int> path;
    vector<vector<int>> res;

public:
    void backTracking(const vector<int> &nums, int start)
    {
        // 递归出口(纵向遍历)
        if (start >= nums.size())
        {
            res.push_back(path);
            return;
        }
        // 横向遍历
        for (int i = start; i < nums.size(); ++i)
        {
            swap(path[start], path[i]);    // 处理
            backTracking(nums, start + 1); // 递归
            swap(path[start], path[i]);    // 回溯
        }
    }
    vector<vector<int>> permute(vector<int> &nums)
    {
        path = nums;
        backTracking(nums, 0);
        return res;
    }
};

不过该算法和经典框架有些出入,例如递归时传递的是 start + 1 而不是大多数情况下的 i + 1i


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. 全排列 的基础上,增加了一个条件: nums可包含重复数字 。于是为了得到 不重复 的全排列结果,在对应的回溯树形结构上, 横向纵向都要去重

  • 同一树枝上要去重(不能重复使用 nums 相同下标的元素,但是下标不同、值相同是可以的)
  • 同一父节点下的同一层要去重(不能重复使用相同的节点值,无论该值在 nums 中是否本来就不止一个)

可以说是回溯算法两种常见去重的缝合怪了 ( 🐶 )

本题回溯树形结构见 代码随想录 中这张图:

在这里插入图片描述

回溯算法基础可参考:笔记-回溯算法 I

代码(C++)

class Solution
{
private:
    vector<int> path;
    vector<vector<int>> res;
    vector<int> branchUsed; // 记录当前树枝中使用过的元素

public:
    void backTracking(const vector<int> &nums)
    {
        // 递归出口(纵向遍历)
        if (path.size() == nums.size())
        {
            res.push_back(path);
            return;
        }
        // 横向遍历
        unordered_set<int> layerUsed; // 记录当前层中使用过的元素
        for (int i = 0; i < nums.size(); ++i)
        {
            // 去重
            if (layerUsed.find(nums[i]) != layerUsed.end())
                continue; // 同一层中去重
            if (branchUsed[i])
                continue; // 同一树枝中去重
            layerUsed.insert(nums[i]);
            branchUsed[i] = 1;
            path.push_back(nums[i]); // 处理
            backTracking(nums);      // 递归
            path.pop_back();         // 回溯
            branchUsed[i] = 0;
        }
    }

    vector<vector<int>> permuteUnique(vector<int> &nums)
    {
        branchUsed.resize(nums.size());
        backTracking(nums);
        return res;
    }
};

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

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

相关文章

MELLE: Autoregressive Speech Synthesis without Vector Quantization

2024.7MICROSOFT 文章目录 MELLE: Autoregressive Speech Synthesis without Vector Quantizationabstractmethod损失函数 Autoregressive Image Generation without Vector Quantizationabstractmethods更好的AR modelresults MELLE: Autoregressive Speech Synthesis without…

UI框架与MVC模式详解(3)——MVC\MVP\MVVM

【PDI模式】 前文中&#xff0c;我们详细讲解了为实现一个涉及UI的功能所必须得三者&#xff0c;简称PDI&#xff1a; Panel类&#xff1a;主要实现交互逻辑、显示逻辑的地方以及保存界面相关的数据的地方Data类&#xff1a;数据管理类&#xff0c;主要是业务相关的数据Inter…

小白也能轻松学的计算机网络零基础入门(附学习路线 + 计算机网络教程)

本文作者&#xff1a;程序员鱼皮 免费编程学习 - 编程导航网&#xff1a;https://www.code-nav.cn 介绍 计算机网络是指将地理位置不同的多台计算机或设备通过通信线路进行连接&#xff0c;从而实现信息传递和资源共享&#xff0c;或者组成一个更完整的计算机系统。 如今&…

【MySQL】用户管理连接池原理{数据库权限/连接池/mysql访问逻辑}

文章目录 1.普通用户的引入用户创建用户删除用户修改用户密码 2.数据库的权限给用户授权回收权限实操过程 3.简略介绍mysql连接池3.一个用户注册/登录/使用网页版mysql逻辑 1.普通用户的引入 用户 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中 mysql> use…

Internet Download Manager(IDM)2024最新免费版电脑下载管理器

1. Internet Download Manager&#xff08;IDM&#xff09;是一款流行的下载管理软件&#xff0c;以其高速、稳定的下载性能著称。 2. IDM支持多线程下载&#xff0c;可以显著提高下载速度。 3. 该软件还具备恢复中断下载的功能&#xff0c;对于网络不稳定的用户非常实用。 …

蓝鹏测径仪非标定制订制流程

测径仪通常属于非标定制设备&#xff0c;非标定制意味着这些设备不是按照标准规格批量生产的&#xff0c;而是根据特定的客户需求和应用场景设计和制造的。例如&#xff0c;某些测径仪可能需要特殊的测量范围、精度、传感器或软件来满足特定的工业检测要求。 测径仪非标定制的…

Axure Web端元件库:构建高效互动网页的基石

在快速迭代的互联网时代&#xff0c;Web设计与开发不仅追求视觉上的美感&#xff0c;更注重用户体验的流畅与功能的强大。Axure RP&#xff0c;作为一款专业的原型设计工具&#xff0c;凭借其强大的交互设计能力和丰富的元件库&#xff0c;成为了众多UI/UX设计师、产品经理及前…

箱型图大揭秘!Matplotlib带你玩转数据界的‘变形金刚’

想要一窥数据深海的秘密&#xff1f;Matplotlib箱型图来助阵&#xff01;不是魔术师&#xff0c;却能让数据‘箱’中显真章&#xff0c;异常值无所遁形。笑点低&#xff1f;不&#xff0c;是数据洞察力强&#xff01;跟着这波操作&#xff0c;让数据可视化变得既科学又有趣&…

【手撕数据结构】链表面试题进阶

目录 相交链表环形链表扩展问题 环形链表||原理 随机链表的复制 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 思…

Centos7.6升级Nginx到nginx-1.27.0

Centos7.6升级Nginx到nginx-1.27.0 简介 升级过程 常见问题 简介 Nginx 更新到了1.27.0&#xff0c;为了系统的安全性将进行服务升级 升级过程 一、Nginx的卸载 升级前需要先卸载以前安装的版本 1、yum的卸载 > yum remove nginx同时还需要手动删除配置文件和日志文…

sqli-labs靶场——第三关

1、判断注入类型 ?id1和?id2-1的页面不一样 2、判断闭合类型 http://127.0.0.1/sqli-labs/Less-3/?id1)and 11 -- 页面正常 http://127.0.0.1/sqli-labs/Less-3/?id1)and 12 -- 页面变化 3、order by查看有几列 http://127.0.0.1/sqli-labs/Less-3/?id1)order by 4 --页面…

vue3+ts+pinia+vant-项目搭建

1.pnpm介绍 npm和pnpm都是JavaScript的包管理工具&#xff0c;用于自动化安装、配置、更新和卸载npm包依赖。 pnpm节省了大量的磁盘空间并提高了安装速度&#xff1a;使用一个内容寻址的文件存储方式&#xff0c;如果多个项目使用相同的包版本&#xff0c;pnpm会存储单个副本…

LABVIEW2016安装教程完整版安装图文教程、注册下载方法

大家好&#xff0c;今天分享下LABVIEW2016安装步骤&#xff0c;同样的安装前请大家务必退出杀毒软件&#xff0c;安装路径千万不要出现中文或其他特殊符号&#xff0c;否则都会导致安装失败的&#xff0c;跟着教程来安装都会安装成功的&#xff0c;如果你遇到了安装问题&#x…

springboot整合mybatis-plus(数据层Dao/Mapper测试)

第一部分&#xff1a;创建springboot文件 第一步&#xff1a;打开软件&#xff0c;点击file&#xff0c;点击new 然后选择module&#xff0c;在右侧选择springboot 第二步&#xff1a;选择配置和JDK以及java版本 ①选择maven类型 ②选择JDK1.8版本 ③选择java8版本 ④选择j…

破解监控难题,局域网电脑监控软件哪家强?

现在的环境&#xff0c;企业要想茁壮成长&#xff0c;员工的高效工作那可是关键中的关键。但不少老板都发现了一个头疼的问题&#xff0c;员工上班老是偷懒&#xff0c;这可怎么行&#xff1f;今天&#xff0c;就来给大家详细说道说道几款出色的局域网电脑监控软件&#xff0c;…

使用Nginx Proxy Manager配置Halo的反向代理和申请 SSL 证书

本文介绍Nginx Proxy Manager配置Halo的反向代理和申请 SSL 证书&#xff0c;如需要了解Halo 2的安装&#xff0c;参考 如何在Linux云服务器上通过Docker Compose部署安装Halo&#xff0c;搭建个人博客网站&#xff1f;。 文章目录 安装Nginx Proxy ManagerNginx Proxy Manager…

人工智能会越来越闭源——对话东北大学副教授王言治 | Open AGI Forum

作者 | Annie Xu 责编、采访 | Echo Tang 出品丨GOSIM 开源创新汇 在读期间研究方向为并不“火”的模式识别与深度学习&#xff0c;毕业却刚好踩上人工智能计算研究的风口……来自美国东北大学的王言治副教授深耕深度学习与大模型&#xff0c;前瞻性地探索大模型的本地化部署…

服务器并发模型

服务器&#xff1a; 单循环服务器&#xff1a;服务器在同一时刻只能响应一个客户端的请求 并发服务器模型&#xff1a;服务器在同一时刻可以响应多个客户端的请求 UDP:无连接 TCP:有连接 1.多进程 资源空间消耗大 效率低 2.多线程 相…