自学内容网 自学内容网

【491. 非递减子序列 中等】

题目:

给你一个整数数组 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

思路:

本题有两个难点:

  1. 首先要保证是递增的子序列
  2. 要找出数组中不同的递增子序列,所以需要去重。并且由于求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用90. 子集 II 中等
    中的排序后去重的方法。

回溯三部曲

  • 递归函数参数

本题求子序列,很明显一个元素不能重复使用,所以需要startIndex,调整下一层递归的起始位置。

代码如下:

vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex)
  • 终止条件

本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和回溯算法:求子集问题!一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。

但本题收集结果有所不同,题目要求递增子序列大小至少为2,所以代码如下:

if (path.size() >= 2) {
result.push_back(path);
// 注意这里不要加return,因为要取树上的所有节点
}

  • 单层搜索逻辑
    491. 递增子序列1

在图中可以看出,同一父节点下的同层上使用过的元素就不能再使用了

那么单层搜索代码如下:

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

对于已经习惯写回溯的同学,看到递归函数上面的uset.insert(nums[i]);,下面却没有对应的pop之类的操作,应该很不习惯吧

这也是需要注意的点,unordered_set uset; 是记录本层元素是否重复使用,新的一层uset都会重新定义(清空),所以要知道uset只负责本层!


代码:

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex){
        if(path.size() >= 2){
            result.push_back(path);
            //  return;
        }
        
        unordered_set<int> uset;
        for(int i = startIndex; i < nums.size(); i++){
            if((!path.empty() && nums[i] < path.back()) ||   //  首先要保证是递增的子序列
            uset.find(nums[i]) != uset.end()){   //  要找出数组中不同的递增子序列,所以需要去重
                                                //  例如[4,6,7](第三个7)和[4,6,7](第四个7)是相同的
                continue;
            }
            uset.insert(nums[i]);   //  记录这个元素已经在本树层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backtracking(nums, 0);
        return result;
    }
};

总结:

时间复杂度: O(n * 2^n)
空间复杂度: O(n)

本题有两个难点:

  1. 首先要保证是递增的子序列
  2. 要找出数组中不同的递增子序列,所以需要去重。并且由于求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用90. 子集 II 中等
    中的排序后去重的方法。

参考:

代码随想录


原文地址:https://blog.csdn.net/yuan_2001_/article/details/145154820

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!