递增子序列

107 阅读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. 画出抽象树 image.png
  2. 乍一看,本题感觉与子集II十分相似,但使用子集II的思路却不行,这是因为 子集II的去重操作的前提是先对数组进行排序本题却不能先进行排序,因为我们要得到的是所有递增的子序列
  3. 需要进行处理的情况:
  • 长度必须大于2
  • 序列必须是递增
  • 去重逻辑:树层去重每次递归单独设置一个Map结构存放本层元素的使用情况。

代码

function findSubsequences(nums: number[]): number[][] {
    let result=[],path=[];

    //回溯方法
    function backtracking(nums: number[],startIndex:number):void{
        // 终止条件
        if(startIndex==nums.length){
            return;
        }
        // 存放同一层元素元素是否使用过
        let used=new Map();

        for(let i=startIndex;i<nums.length;i++){
            // 如果元素没使用过
                if(!used.get(nums[i])){
                    // push进used数组
                    path.push(nums[i]);
                    // 判断是否是递增数组吗,若是
                    if(isIncreasing(path)){
                        //记录该数使用过
                        used.set(nums[i],true)
                        // 收割结果
                        if(path.length>1){
                            result.push([...path]);
                        }
                        // 递归调用
                        backtracking(nums,i+1);
                        // 回溯操作
                        path.pop();
                    }else{
                        // 如果path 不是一个递归数组,则直接将push进去的数弹出,进行下一次循环。
                        path.pop();
                    }   
                }else{
                    continue;
                }
        }
    }

    // 判断是否是递增函数
    function isIncreasing (nums: number[]):Boolean{
        for(let i=1;i<nums.length;i++){
            if(nums[i]<nums[i-1]) return false;
        }
        return true;
    }


    backtracking(nums,0)
    return result

};

image.png

总结

子序列子集问题不同点在于:子序列内部是有一定的顺序的,而子集不在意内部顺序。所以这也是子集问题可以先排序再去重,而子序列问题不能这样做的原因。