7

【LeetCode回溯算法#08】递增子序列,巩固回溯算法中的去重问题 - dayceng

 1 year ago
source link: https://www.cnblogs.com/DAYceng/p/17207384.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

递增子序列

力扣题目链接(opens new window)

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

  • 输入: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]]

  • 输入:nums = [4,4,3,2,1]
    输出:[[4,4]]

  • 给定数组的长度不会超过15。
  • 数组中的整数范围是 [-100,100]。
  • 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况

题目要找出数组的所有递增子序列,所以需要将整个树结构遍历一遍

根据说明中的第三点,本题也需要去重

根据示例2可以看出,我们要找的是数组中当前顺序下的递增序列,因此不能对给定数组进行排序

那就恶心了,要知道我们之前在此类问题中去重考的就是 排序后的相邻重复值 + used标记数组

不管没关系,不能排序也行,那就在单层处理时做手脚

这里还是需要使用used数组来记录单层中使用过的元素

在记录遍历值前,需要做以下判断:

  • 使用当前遍历值与之前保存在结果数组path中的值进行大小比较(判定是否有递增趋势)
  • 判断当前遍历值是否被记录在used数组中(即之前被使用过)

为了实现上述思路,在回溯时不能删除之前used数组记录的值,且used数组改为在单层递归循环前定义,这样每到一层新的递归层时,used数组都会被清空

1、确定回溯函数的参数和返回值

参数时题目给的数组nums、beingIndex,这里used数组在回溯函数里面定义

class Solution {
private:
    //定义结果数组
    vector<vector<int>> res;
    vector<int> path;
    //确定回溯函数的参数和返回值
    void backtracking(vector<int>& nums, int beginIndex){
        
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {

    }
};

2、确定终止条件

参考 子集问题 ,如需要收集树结构的所有节点,实际上是不需要返回值的(依靠for循环结束即可)

但是,本题有一个要求是:递增子序列大小至少为2

所以在这里需要单独处理一下这个逻辑(严格来说也不是终止条件)

class Solution {
private:
    //定义结果数组
    vector<vector<int>> res;
    vector<int> path;
    //确定回溯函数的参数和返回值
    void backtracking(vector<int>& nums, int beginIndex){
        //(确定终止条件)
        //确保递增子序列大小至少为2
        if(path.size() > 2){//2个以上才保存到结果数组
            res.push_back(path);
        }                
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {

    }
};

3、确定单层处理逻辑

在单层处理时,还是去循环遍历当前层的元素

不过需要初始化used数组,并且加入当前遍历值与path数组中元素比较大小以及判断当前元素是否被used记录的逻辑

class Solution {
private:
    //定义结果数组
    vector<vector<int>> res;
    vector<int> path;
    //确定回溯函数的参数和返回值
    void backtracking(vector<int>& nums, int beginIndex){
        //(确定终止条件)
        //确保递增子序列大小至少为2
        if(path.size() > 1){//2个以上才保存到结果数组
            res.push_back(path);
        } 
        //确定单层处理逻辑
        //初始化used数组,每次进入递归都会被刷新
        int used[201] = {0};//题目中说了,数值范围[-100,100],因此直接用数组就行
        for(int i = beginIndex; i < nums.size(); ++i){
            //判断逻辑
            //path是否为空;当前值是否小于path中最新加入的值;或者当前值是否被记录于used
            if(!path.empty() && nums[i] < path.back() || used[100 + nums[i]] == 1){
                continue;//满足上述条件就跳过
            }
            used[100 + nums[i]] = 1;//在nums[i]位置记录出现过当前遍历值
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
        
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {

    }
};

1、vector::back()用于取数组最新加入的一个值,这里用来取上次添加的值并与当前遍历值进行比较

2、使用数组来记录数的话,需要创建能够容纳所有元素个数的大小。然后只需在该元素大小位置处标记元素是否出现过即可

例如:如果遍历值是2,那么应该在used数组的100 + 2,也就是下标为102处记录1,表示2出现过1次

加100是因为题目所给范围是[-100,100],前100是负数值

class Solution {
private:
    //定义结果数组
    vector<vector<int>> res;
    vector<int> path;
    //确定回溯函数的参数和返回值
    void backtracking(vector<int>& nums, int beginIndex){
        //(确定终止条件)
        //确保递增子序列大小至少为2
        if(path.size() > 1){//2个以上才保存到结果数组
            res.push_back(path);
        } 
        //确定单层处理逻辑
        //初始化used数组,每次进入递归都会被刷新
        int used[201] = {0};//题目中说了,数值范围[-100,100],因此直接用数组就行
        for(int i = beginIndex; i < nums.size(); ++i){
            //判断逻辑
            //path是否为空;当前值是否小于path中最新加入的值;或者当前值是否被记录于used
            if(!path.empty() && nums[i] < path.back() || used[100 + nums[i]] == 1){
                continue;//满足上述条件就跳过
            }
            used[100 + nums[i]] = 1;//在nums[i]位置记录出现过当前遍历值
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
        
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
		backtracking(nums, 0);
        return res;
    }
};

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK