Leetcode 491.递增子序列
题目链接:491 递增子序列
题干:给你一个整数数组
nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
1 <= nums.length <= 15
-100 <= nums[i] <= 100
思考:回溯法。先定义结果集result和节点集node,再考虑回溯函数:
参数 | 含义 |
nums | 题干条件给定数组 |
startIndex | 下一层循环搜索的起始位置 |
终止条件:如果节点集node中数据个数大于1则将此节点集写入结果集。(此处不可返回,要继续执行下续操作,因为要处理符合条件的各个节点)
单层搜索逻辑:定义set容器记录本层数字使用情况。循环从startIndex开始,如果当前处理数字在容器set中能查询到则说明此数字本层搜索已经使用过。若未使用过则从添加进节点集node的第二个元素开始与节点集尾部元素进行大小比较,若当前数字大则递归处理并回溯,反之跳过此次处理。
代码:
class Solution {
public:vector<vector<int>> result;vector<int> node;void backtracking(vector<int>& nums, int startIndex) {if (node.size() > 1) //含两个以上元素加入结果集result.push_back(node);unordered_set<int> uset; //记录本层元素是否重复使用for (int i = startIndex; i < nums.size(); i++) {if (uset.find(nums[i]) != uset.end()) //去重continue;if (node.size() == 0 || nums[i] >= node.back()) { //除空情况外判断是否升序node.push_back(nums[i]);uset.insert(nums[i]); //记录当前元素本层已经使用过backtracking(nums, i + 1);node.pop_back(); //回溯}}}vector<vector<int>> findSubsequences(vector<int>& nums) {result.clear();node.clear();backtracking(nums, 0);return result;}
};
优化:以上代码用了unordered_set<int>
来记录本层元素是否重复使用。其实用数组来做哈希,效率就高了很多。注意题目中说了,数值范围[-100,100],所以完全可以用数组来做哈希。
代码:
class Solution {
private:vector<vector<int>> result;vector<int> node;void backtracking(vector<int>& nums, int startIndex) {if (node.size() > 1) {result.push_back(node);}int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]for (int i = startIndex; i < nums.size(); i++) {if ((!node.empty() && nums[i] < node.back())|| used[nums[i] + 100] == 1) {continue;}used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了node.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}
public:vector<vector<int>> findSubsequences(vector<int>& nums) {result.clear();node.clear();backtracking(nums, 0);return result;}
};
Leetcode 46.全排列
题目链接:46 全排列
题干:给定一个不含重复数字的数组
nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
思考:回溯法。先定义结果集result、路径集path以及记录元素使用情况的数组used,再考虑回溯函数:
参数 | 含义 |
nums | 题干条件给定数组 |
used | 记录元素使用情况 |
终止条件:如果路径path长度等于数组nums长度则将当前路径path加入结果集。
单层搜索逻辑:从下标0开始循环处理,若当前数字未使用过则递归处理并更新数组used,之后再回溯。
代码:
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]) { //判断当前元素是否使用过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
题目链接:47 全排列 II
题干:给定一个可包含重复数字的序列
nums
,按任意顺序 返回所有不重复的全排列。
思考一:与上题的区别:给定数组内存在重复元素。故想到单层搜索逻辑中同层元素去重。(当然去重一定先要对元素进行排序)
代码:
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 (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) //同层去重continue;if (!used[i]) { //判断当前元素是否使用过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();sort(nums.begin(), nums.end()); //先将数组排序vector<bool> used(nums.size(), false); //记录元素使用情况backtracking(nums, used);return result;}
};
自我总结:
- 同层搜索去重的两种方式
- 用set来记录本层元素是否重复使用
- 先对元素进行排序再通过相邻的节点来判断是否重复使用