【回溯算法part05】递增子序列,全排列

1.递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

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

注意:本题不能对原数组进行排序操作,
去重,同一树层不能重复使用相同的元素,所取元素小于子序列最后一个元素;
递归可以不加终止条件,startIndex 每次都会+1,不会无限递归;取树上所有符合条件的节点;
请添加图片描述

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var findSubsequences = function(nums) {
    let result = [];
    let path = [];
    let startIndex = 0;
  
    var backTracking = function (startIndex) {
        if (path.length > 1) {
            result.push(Array.from(path))
        }
        let used = new Array(200).fill(0);
        for (let i = startIndex; i < nums.length; i++) {
           
            if(path.length > 0 && nums[i] < path[path.length-1] || (used[nums[i]+100] === 1)){
                continue;
            }
            path.push(nums[i]);
            //记录这个元素在本层使用过了,本层后面不能再用
            used[nums[i]+100] = 1; 
            backTracking(i+1);
            path.pop();   
        }
    }
    backTracking(startIndex);
    return result;

};
findSubsequences([1,2,1,1]);

2.全排列1

给定一个没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

排列是有序的,[1,2],[2,1]是两个集合,需要标记已经选择的元素;
请添加图片描述
叶子节点就是所有符合条件的子集;当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点;

var permute = function(nums) {
    let result = [];
    let path = [];
    let used = [];
    var backTracking = function () {
        if(path.length == nums.length){
            result.push([...path]);
            return;
        }
        for(let i = 0;i<nums.length;i++){
            if (used[i]== true) {
                continue;
            }
            used[i] = true;
            path.push(nums[i]);
            backTracking();
            path.pop();
            used[i] = false;
        }
    }
    backTracking();
    return result;
};
permute([1,2,3])

3.全排列2

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]

去重一定要对元素进行排序,方便通过相邻的节点来判断是否重复使用;
同一树层,前一位如果使用过,那么就要去重;

组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果;
请添加图片描述

var permuteUnique = function(nums) {
    let result = [];
    let path = [];
    let used = [];
    nums = nums.sort((a,b)=>{return a-b});
    var backTracking = function () {
        if(path.length == nums.length){
            result.push([...path]);
            return;
        }
        for(let i = 0;i<nums.length;i++){
        	// used[i - 1] == true,说明同一  树枝   nums[i - 1]使用过
            // used[i - 1] == false,说明同一  树层  nums[i - 1]使用过
            // 如果同一树层nums[i - 1]使用过则直接跳过
            if (i > 0 && nums[i] === nums[i-1] && !used[i-1]) {
                continue;
            }
            if (!used[i]) {
            
            	// 因为每层都是从下标0开始遍历的,树枝上相同的不符合
                used[i] = true;
                path.push(nums[i]);
                backTracking();
                path.pop();
                used[i] = false;
            }
        }
    }
    backTracking();
    return result;
};
permuteUnique([1,1,3])

https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html#%E6%80%9D%E8%B7%A

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值