代码随想录算法训练营22天|491.非递减子序列、46.全排列、47.全排列 II

LeetCode491

2025-02-22 18:15:40 星期六

题目描述:力扣491
文档讲解:代码随想录(programmercarl)491. 非递减子序列
视频讲解:《代码随想录》算法视频公开课:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列

代码随想录视频内容简记

这个题和之前的组合总和Ⅱ还有子集Ⅱ去重的方式不一样,因为本题不能对数组进行排序,可以看力扣的示例或者k哥的讲解

另外就是要确保输出的子序列的位数是大于等于2的

本题需要对两种情况做去除:

  1. [4, 7]和6,就是新添加的元素小于path.back()

  2. 对树层中出现重复元素的,比如,第一层递归的[4, 7, 6]和最后的7

梳理

  1. 确定函数的参数和返回值

  2. 确定递归的终止条件

  3. 确定单层递归的逻辑。这里是新定义了一个uset用来添加存放过的元素,使其在同层回溯的时候,可以用来检查有没有添加过,如果没有,那么就可以添加,如果有就不能添加了。uset的使用逻辑和used数组是差不多的,所以,uset也可以用来解决组合总和Ⅱ和子集Ⅱ。

大致代码内容

  1. if (path.size() >= 2) result.push_back(path)

  2. 确定单层递归的逻辑,unordered_set<int> uset,之后需要做判断,if (uset.find(nums[i]) != uset.end() || nums[i] < path.back() && !path.empty()) continueuset.insert(nums[i])关于uset,他就是在每次进入递归后,都会新建一个新的uset来管下一层的递归中是否有重复元素,所以在回溯的时候并不用erase

LeetCode测试

点击查看代码
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int index) {
        if (path.size() >= 2) {
            result.push_back(path);
        }
        if (index == nums.size()) return;
        unordered_set<int> uset;
        for (int i = index; 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) {
        backtracking(nums, 0);
        return result;
    }
};

LeetCode46

题目描述:力扣46
文档讲解:代码随想录(programmercarl)46.全排列
视频讲解:《代码随想录》算法视频公开课:组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列

代码随想录视频内容简记

首先是看到这道题的思路,既然是排列,那么我首先联想到的就是之前在39组合总和那道题中记录的错写了for循环的初始条件遇到的排列情况——要点1,那么也就是即时取到后面的数,前面的数也可以重复取。如果是这样的话,index就会失效,那么递归中的index参数也就不复存在了。那只有一个nums参数行不行?


这里注意一下,如果写成这样的话,忘记了写return,他会一直遍历[1, 1, 1],一定会栈溢出,

void backtracking(vector<int>& nums) {
	if (path.size() == nums.size()) {
		result.push_back(path);
	}
	for (int i = 0; i < nums.size(); i++) {
		path.push_back(nums[i]);
		backtracking(nums);
		path.pop_back();
	}
}

如果是

void backtracking(vector<int>& nums) {
	if (path.size() == nums.size()) {
		result.push_back(path);
		return;
	}
	for (int i = 0; i < nums.size(); i++) {
		path.push_back(nums[i]);
		backtracking(nums);
		path.pop_back();
	}
}

就会取重复元素,也不符合条件。

所以,要做的只是去重

梳理

  1. 确定递归函数的参数和返回值。

  2. 确定递归的终止条件。

  3. 确定单层递归的逻辑。这里仍旧是用到了used数组,在之前的40组合总和Ⅱ和子集Ⅱ使用used数组是为了做树层去重。那么在本题中也是树层去重吗?非也,是树枝去重,也就是我们要用used来完成之前的index的任务,完成他的遗愿。这还是第一次用used数组完成树枝去重。

大致代码内容

  1. 终止条件if (path.size() == nums.size())

  2. 在单层递归中,如何用used数组来做树枝去重?其实就是每进入一层递归的时候,判断used[i]是否用过,如果用过,就直接跳过。if (used[i] == true) continue,这个不如树层去重繁琐

LeetCode测试

整体代码不难

点击查看代码
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++) {
            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) {
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

LeetCode47

题目描述:力扣47
文档讲解:代码随想录(programmercarl)47.全排列 II
视频讲解:《代码随想录》算法视频公开课:回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II

要点

这道题和之前的唯一的区别就在于多了重复的元素,那么很简单,直接加上树层去重就好了

对了,记得对数组进行排列哈😉

LeetCode测试

点击查看代码
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++) {
        if (used[i] == true) continue;
        if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) continue; 
        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) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

posted on   bnbncch  阅读(764)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示