求递增子序列有点像是取有序的子集,而且本题也要求不能有相同的递增子序列,很容易就产生思维定式使用 “组合总和II” 的方法(先排序再用类似 i > startIndex && nums[i] == nums[i - 1] 的条件去重)。然而根据本题题意,nums 数组是不能排序的(不然排完序的数组就都是递增子序列了),使用之前的去重逻辑就有问题了。故本题在去重时需要设置一个 set 进行检测(由于数组的数据范围在 [-100, 100],也可换成长度为 201 的标记数组)。
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { n = nums.size(); dfs(nums, 0); return res; } vector<vector<int>> res; vector<int> path; int n; void dfs(vector<int>& nums, int j) { if(path.size() >= 2) { res.push_back(path); // 不能写return,要取整棵树的结点而不仅仅取叶子结点 } unordered_set<int> uset; // 使用set来对本层元素进行去重,注意不要放在类的成员变量里 for(int i = j; i < n; i ++) { if((!path.empty() && path.back() > nums[i]) || uset.find(nums[i]) != uset.end()) // 当遍历到的数非递增、或在本层出现过后剪枝 continue; uset.insert(nums[i]); path.push_back(nums[i]); dfs(nums, i + 1); path.pop_back(); } } };
需要注意,unordered_set<int> uset 仅记录本层元素是否重复使用,进入下一层 set 都会重新定义(清空)。在树形结构中,如果把 set 放在类成员的位置(相当于全局变量),就把树枝的情况都记录了,不是单纯的控制某一节点下的同一层了。
使用标记数组优化后的版本如下。
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { n = nums.size(); dfs(nums, 0); return res; } vector<vector<int>> res; vector<int> path; int n; void dfs(vector<int>& nums, int j) { if(path.size() >= 2) { res.push_back(path); // 不能写return,要取整棵树的结点 } int flag[201] = {0}; // 使用标记数组 for(int i = j; i < n; i ++) { if((!path.empty() && path.back() > nums[i]) || flag[nums[i] + 100] == 1) // 当遍历到的数非递增、或在本层出现过后剪枝 continue; flag[nums[i] + 100] = 1; path.push_back(nums[i]); dfs(nums, i + 1); path.pop_back(); } } };