【玩转回溯算法专题】491. 非递减子序列 【中等】
1、力扣链接
https://leetcode.cn/problems/non-decreasing-subsequences/
2、题目描述
给你一个整数数组 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
3、题目分析
回溯三部曲
1、递归函数参数
int[] nums, int startIndex
2、递归终止条件
可以不加终止条件,startIndex每次都会加1,并不会无限递归。
收集:
if(path.size() >= 2){
result.add(new ArrayList<>(path));
}
3、单层搜索逻辑
见注释
4、代码实现
1、Java
class Solution {
//定义返回值
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
//递归函数调用
backTracking(nums, 0);
return result;
}
private void backTracking(int[] nums, int startIndex){
//如果path的大小 >= 2符合条件 加入
if(path.size() >= 2){
result.add(new ArrayList<>(path));
}
//用于去重 如 【4,6,7,7】 【4,6,7】 下一个节点还为7 则 这个分支下所有的结果都是重复的
HashSet<Integer> hs = new HashSet<>();
for(int i = startIndex; i < nums.length; i++){
//跳过本轮 当 不是 递增子序列 或者 是 重复的分支节点 都要跳过
if(!path.isEmpty() && path.get(path.size() -1 ) > nums[i] || hs.contains(nums[i])){
continue;
}
//该节点存入
hs.add(nums[i]);
path.add(nums[i]);
backTracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
2、C++
// 版本一
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;
}
};