递增子序列(回溯)

题目描述

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

题解

如图所示,本题在使用回溯法时,需要解决以下两个问题:

(回溯法的使用详解见:组合(回溯+剪枝、图解)-CSDN博客

  • 如何判断序列递增
  • 回溯遍历时如何进行去重

在使用回溯法时,我们用path数组保存遍历时路径上的每一个节点,用res保存最终结果,如下:

    vector<int> path;//保存当前结果
    vector<vector<int>> res;//保存最终结果

去除非递增序列

使用回溯法遍历nums时,由于我们用path数组保存当前遍历结果,因此对于下一个遍历到的元素num[i],我们只需要判断其与path数组中最后一个元素的大小。也就是说,

扫描二维码关注公众号,回复: 17219582 查看本文章

如果nums[i]>=path.back(),就说明如果将当前元素nums[i]加入到path数组中,序列就可以继续保持递增,那么就可以将nums[i]添加到path数组中;

否则,说明将当前元素nums[i]加入到path数组中后序列无法保持递增,就不能将nums[i]添加到path数组中

去重

由上图我们可以看到,去重是指对同一层上出现过的元素进行去重,由于按照题意我们无法对数组进行排序,因此不能使用之前使用过的去重逻辑(组合总和II(回溯、去重)-CSDN博客),需要重新设计去重方法。

假如我们现在不考虑回溯法,那么又该如何对一个有重复元素的数组nums进行去重呢?

如图所示,我们用一个set集合保存已经遍历过的元素,在每次遍历新元素的时候都在set中查找当前正在遍历的元素是否在set中出现过,如果当前正在遍历的元素在set中找到了,说明该元素已经出现过,也就是与之前的元素出现了重复。

为什么用set保存已经遍历过的元素呢?因为set数据结构天然不能保存重复的元素。

可以用其他数据结构保存已经遍历过的元素么?

当然可以,本题要求的-100 <= nums[i] <= 100,因此我们完全可以开一个大小为201的数组用于对已经遍历过的元素打上标记,用于标志该元素已经被遍历过

故而将上述想法假如到对上图中回溯法的单层去重逻辑中,即可完成本题的去重。整体代码如下

代码

解1

class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
public:
    void backing(vector<int>& nums,int startIndex)
    {
        if(path.size()>1)
            res.push_back(path);
        
        unordered_set<int> uset;//记录本层元素是否重复
        for(int i=startIndex;i<nums.size();i++)
        {
            /*
            判断当前元素
                1.如果nums[i]<path.back(),说明若加入元素就会使得序列非递增
                2.如果uset.find(nums[i])!=uset.end(),说明当前元素之前在同一层已经出现过
            */
            if((!path.empty() && nums[i]<path.back())
             || uset.find(nums[i])!=uset.end())
                continue;
            
            uset.insert(nums[i]);//记录当前已经使用过的元素
            path.push_back(nums[i]);//遍历路径上的节点
            backing(nums,i+1);
            path.pop_back();//回溯
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backing(nums,0);
        return res;
    }
};

解2

class Solution {
private:
    vector<int> path;//保存当前结果
    vector<vector<int>> res;//保存最终结果
public:
    void backing(vector<int>& nums,int startIndex)
    {
        if(path.size()>1)
            res.push_back(path);
        
        // unordered_set<int> uset;//记录本层元素是否重复
        int used[201]={0};
        for(int i=startIndex;i<nums.size();i++)
        {
            if((!path.empty() && nums[i]<path.back())
             || used[nums[i]+100])
                continue;
            
            // uset.insert(nums[i]);//记录当前已经使用过的元素
            used[nums[i]+100]=1;
            path.push_back(nums[i]);//遍历路径上的节点
            backing(nums,i+1);
            path.pop_back();//回溯
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backing(nums,0);
        return res;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_58158950/article/details/134799894