算法学习 | day25/60 递增子序列/全排列/全排列II

一、题目打卡

        1.1 递增子序列

        题目链接:. - 力扣(LeetCode)        

class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    int tmp = INT_MIN;
    void recur(vector<int>& nums, int startInd){
        if(path.size() >= 2){
            res.push_back(path);
        }
        if(startInd == nums.size()) return;
        unordered_set<int> used;
        for(int i = startInd; i < nums.size(); i++){
            // if(i > startInd && nums[i] <= nums[i-1] && (!path.empty() && path.back() > nums[i])) continue;
            // if(path.empty() || path.back() <= nums[i]) path.push_back(nums[i]);

            // if(i > startInd && nums[i] == nums[i-1]) continue;
            // if(i > startInd && nums[i] == tmp) continue;
            // if(path.empty() || path.back() <= nums[i]) path.push_back(nums[i]);
            // else continue;
            if(used.find(nums[i]) != used.end() || 
                (!path.empty() && path.back() > nums[i])) continue;
            used.insert(nums[i]);
            path.push_back(nums[i]);
            recur(nums,i+1);
            tmp = path.back();
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        // sort(nums.begin(),nums.end());
        recur(nums,0);
        return res;
    }
};

        这个题目写的过程中表明的坑我好像都踩了(擦汗.jpg),这个题目类似与子集,但是根据题目的要求,这个题目并不能排序,这个题目的去重过程也和之前的不一样,因为没有办法排序,那么在同一层中,就不能使用之前我写的那个条件进行判断了,然后我看了答案,确实解锁了一种新的思路,通过set来存储在同一层(横向)中重复的元素并进行判断,相当于是用空间换时间的一种方法吧,之前的算法,因为可以排序,所以其实节省了这里的一部分空间。

        1.2 全排列

        题目链接:. - 力扣(LeetCode)

class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    unordered_set<int> used;
    // vector<int> used;
    void recur(vector<int> &nums){
        if(path.size() == nums.size()){
            res.push_back(path);
            // used.clear();
            return;
        }
        for(int i = 0;i < nums.size();i++){
            // path.push_back(nums[i]);
            // if(path.empty() || path.back() != nums[i])path.push_back(nums[i]);
            // else continue;

            if(used.find(nums[i]) != used.end()) continue;
            used.insert(nums[i]);
            path.push_back(nums[i]);
            recur(nums);
            used.erase(nums[i]);
            path.pop_back();
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        recur(nums);
        return res;
    }
};

        一上来其实思路挺清晰的,很明确的是,求组合的问题,肯定是不需要传递 startInd 去更新每一层的参数的,只是做的过程里面有一点坎坷,不过做出来还是很有收获的,之前我还一直在思考 carl 的那句话,去重包含两个层面的内容,一个是横向的去重,另一个是纵向的去重,这个题目就是纵向的去重,也就是在某一层选择了的元素,在别的层就不能再选择了,和之前在同一层不能选择同一个元素,是两个不同的思路,我解决的办法是维护了一个全局的 set,这里必须是全局的,按照上个题目的声明方法,其实去重的是横向的一个去重。这种方法是可以解决纵向去重的问题的,不过我认为答案的思路其实是更清晰的,也就是每一层传递一个哈希映射:

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

        这个题在做的过程中,其实也复习了:vector没有find方法;erase 是set 的删除方法。

        1.3 全排列II(借助答案思路,两种去重同时使用的情况)

          leetcode链接:. - 力扣(LeetCode)

// class Solution {
// private:
//     vector<int> path;
//     vector<vector<int>> res;
//     void backtrack(vector<int>& nums, vector<bool>& used, int startInd){
//         if(path.size() == nums.size()){
//             res.push_back(path);
//             return;
//         }
//         for(int i = 0; i < nums.size();i++){
//             // if(used[nums[i]]) continue; // 纵向去重
//             if(used[i]) continue; // 纵向去重
//             if(i > startInd && nums[i - 1] == nums[i]) continue; // 横向去重
//             path.push_back(nums[i]);
//             used[i] = true;
//             backtrack(nums, used, i + 1);
//             used[i] = false;
//             path.pop_back();
//         }
        
//     }
// public:
//     vector<vector<int>> permuteUnique(vector<int>& nums) {
//         vector<bool> used(nums.size(),false);
//         backtrack(nums, used, 0);
//         return res;
//     }
// };


class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtrack(vector<int>& nums, vector<bool>& used){
        if(path.size() == nums.size()){
            res.push_back(path);
            return;
        }
        for(int i = 0; i < nums.size();i++){
            // if(used[nums[i]]) continue; // 纵向去重
            // if(used[i]) continue; // 纵向去重
            // if(i > 0 && nums[i - 1] == nums[i] && used[i]) continue; // 横向去重
            if(used[i]) continue; // 纵向去重
            if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; // 横向去重
            path.push_back(nums[i]);
            used[i] = true;
            backtrack(nums, used);
            used[i] = false;
            path.pop_back();
        }
        
    }
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        sort(nums.begin(),nums.end());
        backtrack(nums, used);
        return res;
    }
};

        这个题目做的过程有一点波折,做出来以后在理解横向去重和纵向去重有了更深的理解,进行了一个小的总结:

        不过还有一个小的细节,就是这里:

if(used[i]) continue; // 纵向去重
if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; // 横向去重

        这里横向的去重,之所以还多了一条检测的条件 used[i - 1],因为在不同层之间,传递的一个起始的 startInd 都是0,或者说对于排列问题,因为其全部都是从 0 开始的,所以不能和之前的那种一样,只判断 i > 0 && nums[i] == nums[i - 1],在不同层的相同元素就会取不到,比如说 [1, 1, 2],第一层取 1 ,第二层取第二个 1 的时候,就会直接跳过,这样就会导致结论不正确,因为 112 也是排列的一种,所以才需要再添加一个条件进行横向的去重,这里比较有趣的是,对于used[ i - 1 ] 这个条件,其实判断其为 true 和 false 都是可以的,而区别我觉得这里代码随想录已经讲的很细致了:

        树层上去重(used[i - 1] == false),的树形结构如下:

        这里实际上就是只处理了回溯过程结束以后前一位重置和递归过程中前一位未被重置的区别来进行剪枝的。

        树枝上去重(used[i - 1] == true)的树型结构如下:

        这样相对会有个更多无用的搜索,而且我感觉这样更不好理解一点,所以最好直接采用上一个方法比较好。

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

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

相关文章

Java项目:74 ssm基于Java的超市管理系统+jsp

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 功能包括:商品分类&#xff0c;供货商管理&#xff0c;库存管理&#xff0c;销售统计&#xff0c;用户及角色管理&#xff0c;等等功能。项目采…

数据库性能压测之TPC-C基准测试

原文链接&#xff1a;https://blog.csdn.net/TIME_1981/article/details/126114797 本文作为学习笔记记录。 TPC Transaction Processing Performance Council (TPC) 事务处理性能委员会&#xff0c;是一家非盈利IT组织&#xff0c;他们的目的是定义数据库基准并且向产业界推…

力扣438. 找到字符串中所有字母异位词

Problem: 438. 找到字符串中所有字母异位词 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.编写辅助函数bool same(vector& need, vector& matched)&#xff1a; 1.1 以need为标准&#xff0c;循环对比need和matched的每一个位置的元素值是否相等 2.获…

NAT---网络地址转换技术

Network Address Translation 1、起源&#xff1a;ip地址不够用 2、作用&#xff1a;让私网地址映射成公网地址&#xff0c;进而访问网络。 3、私网Ip地址的范围&#xff1a; A类&#xff1a;10.0.0.0-10.255.255.255 B类&#xff1a;172.16.0.0-172.31.255.255 C类&…

多线程合并练习题,线程安全(售票任务引入)--学习JavaEE的day30

day30 练习&#xff08;day29&#xff09; 注意代码注释&#xff0c;里面涉及代码实现遇到问题及解决方案&#xff0c;由于理解方便没有单独出来 1.计算任务 1.计算任务&#xff0c;一个包含了2万个整数的数组&#xff0c;分拆了多个线程来进行并行计算&#xff0c;最后汇总出…

Open CASCADE 用户指南:可视化

文章目录 介绍基本概念呈现(Presentation)Presentation 的结构Presentation 包(package)基本示例&#xff1a;如何显示 3D 对象 选择(Selection)术语和概念算法包和类(Packages and classes)使用示例 应用交互服务&#xff08;AIS&#xff09;介绍交互对象呈现(Presentations)隐…

【C语言】【Leetcode】88. 合并两个有序数组

文章目录 一、题目二、思路再思考 一、题目 链接: link 二、思路 这题属于简单题&#xff0c;比较粗暴的做法就是直接比较两个数组&#xff0c;先把第二个数组加到第一个的后面&#xff0c;如何冒泡排序&#xff0c;这种方法简单粗暴但有效&#xff0c;可是不适用于这题&…

Vue3使用v-if指令进行条件渲染

Vue3使用v-if指令进行条件渲染 条件渲染是根据条件的真假来有条件地渲染元素。在Vue.js 3.x中&#xff0c;常见的条件渲染包括使用v-if指令和v-show指令。本文讲解使用v-if指令进行条件渲染。 2.3.1 v-if/v-else-if/v-else 在Vue中使用v-if、v-else-if和v-else指令实现条件…

数据结构·二叉树(1)

目录 1 树的概念及结构 1.1 树的结构 1.2 树的概念 1.3树的表示 2 二叉树的概念及结构 2.1二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的存储结构 1 树的概念及结构 1.1 树的结构 前面所学到的顺序表链表等&#xff0c;都是线性的数据结构&#xff0c;今天介绍的树&am…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-第几个幸运数字

幸运数字是可以被3,5,7任一整除的数字&#xff0c;列举小明号码内的所有可能组合并计数。注意别忘了把1占的一位减去。 #include<stdio.h> typedef long long ll; int main(){long long ans 0, n 59084709587505LL;for(ll i 1; i < n; i * 3){//计算小于等于n的数…

代码随想录训练营Day32:● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 题目链接 https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/ 题目描述 思路 看完视频讲解之后豁然开朗啊简直了&#xff01;&#xff01;&#xff01; 统计后一天减去前一天&#xff0c;差值为正数的&#xff0c;再…

一篇文章带你搞定接单的多种渠道,赶紧码住!!!

相信大家也看到了不少人通过网络接单实现年入30W&#xff0c;彻底财富自由&#xff01;咱看了&#xff0c;真的很难不心痒痒&#xff0c;想加入其中&#xff0c;大干一场&#xff01;毕竟搞钱嘛&#xff01;才是王道。但是呢&#xff0c;也有很多人心向往之&#xff0c;奈何不知…

C++剑指offer与高频面试题源码解答与分析

这是博主在当初秋招刷题时候记录的剑指offer第二版以及一些高频题的C源码和解法分析&#xff0c;可以说把这上面的题练好了面试不虚&#xff0c;最后也顺利帮助我拿下baidu ali meituan等多家大厂offer。整篇文章写了大概5W个字&#xff0c;也是积累了很长一段时间的作品&#…

MYSQL通过substr函数与instr函数截取字符串

mysql通过substr函数与instr函数截取字符串 1.从字段左边开始第三位&#xff0c;截取到结束2.截取字段前三位3.截取字段后三位4.从字段右边开始第三位&#xff0c;往后截取一位5.从字段小数点的位置开始&#xff0c;向后截取两位&#xff08;使用了instr&#xff08;&#xff0…

leetcode刷题日记-外观数组

题目描述 解题思路 初始化字符串 init 为 “1”&#xff0c;作为外观数列的第一项。 通过循环迭代生成外观数列的下一项&#xff0c;循环次数为 n-1&#xff0c;因为已经初始化了第一项。 在每次迭代中&#xff0c;通过两个指针 pos 和 start 来遍历当前项 init&#xff0c;po…

22.保护性暂停扩展(一对一)

如果需要多个类之间使用GuardedObject对象&#xff0c;作为参数传递不是很方便&#xff0c;因此设计一个解耦的中间类&#xff0c;这样不仅能够解耦结果的等待者和结果生产者&#xff0c;还能够支持多个任务的管理。 Futures就好比居民楼一层的信箱&#xff0c;每个信箱有房间的…

大数据面试题 —— Flume

目录 介绍 FlumeFlume 架构请说一下你提到的几种 source 的不同点Flume 传输数据时如何保证数据一致性TailDir 为什么可以断点重传说下Flume事务机制Sink 消费能力弱&#xff0c;Channel 会不会丢失数据数千个Flume要怎么统一配置&#xff0c;修改就分发吗Flume一个节点宕机了怎…

【科研基础】分布式信源编码与中继通信

[1] Bian, Chenghong, et al. “Deep joint source-channel coding over cooperative relay networks.” arXiv preprint arXiv:2211.06705 (2022). [2] Bian, Chenghong, et al. “Process-and-Forward: Deep Joint Source-Channel Coding Over Cooperative Relay Networks.”…

还在用传统知识库?AI知识库才是企业的最优选择

在数字化和信息化日趋严重的时代&#xff0c;企业不仅要处理海量的数据&#xff0c;同时还要有效地管理和利用它们。这就使得知识库&#xff0c;作为一种集中存储、管理和共享知识资源的工具&#xff0c;被越来越多的企业所重视。然而&#xff0c;随着技术的快速迭代&#xff0…