其他分享
首页 > 其他分享> > 排列组合子集

排列组合子集

作者:互联网

子集(元素无重不可复选)

[a,b,c] 的全部子集为 [] [a] [b] [c] [a,b] [a,c] [b,c] [a,b,c]

let res = [];
// 记录回溯算法的递归路径
let track = [];

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack( nums, start) {
    // 前序位置,每个节点的值都是一个子集
    res.push([...track]);
    // 回溯算法标准框架
    for (let i = start; i < nums.length; i++) {
        // 做选择
        track.push(nums[i]);
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i + 1);
        // 撤销选择
        track.pop();
    }
}

backtrack(['a', 'b', 'c'], 0);
console.log(res);

  

 

组合(元素无重不可复选)

[a,b,c] 的2元素的所有组合为  [a,b] [a,c] [bc]

let res = []; // 所有组合的结果
let track = []; // 记录回溯算法的递归路径
let max = 2; // 组合的个数

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack( nums, start) {
    // 前序位置,每个节点的值都是一个子集
    if(max === track.length){
        res.push([...track]);
        return;
    }
    
    // 回溯算法标准框架
    for (let i = start; i < nums.length; i++) {
        // 做选择
        track.push(nums[i]);
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i + 1);
        // 撤销选择
        track.pop();
    }
}

backtrack(['a', 'b', 'c'], 0);
console.log(res);

  

排列(元素无重不可复选)

标准全排列可以抽象成如下这棵二完全叉树:

 

 

[a,b,c] 的2元素的所有排列为  [a,b] [a,c] [b,a] [b,c] [c,a] [c,b]

let res = []; // 所有排列的结果
let track = []; // 记录回溯算法的递归路径
let max = 2; // 排列的个数
let used = []; // 记录已经使用了的序号,防止重复

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack(nums) {
    // 前序位置,每个节点的值都是一个子集
    if (max === track.length) {
        res.push([...track]);
        return;
    }

    // 回溯算法标准框架
    for (let i = 0; i < nums.length; i++) {
        if (used[i]) { // 已经存在 track 中的元素,不能重复选择
            continue;
        }
        // 做选择
        track.push(nums[i]);
        used[i] = true;
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums);
        // 撤销选择
        track.pop();
        used[i] = false;
    }
}

backtrack(['a', 'b', 'c']); 
console.log(res);

  

子集/组合(元素可重不可复选)

[a,b,b] 的全部子集为 [] [a] [b] [a,b] [b,b] [a,b,b]

 

 [a,b,b]需要先进行排序,让相同的元素靠在一起,如果发现 nums[i] == nums[i-1],则跳过

let res = []; // 所有组合的结果
let track = []; // 记录回溯算法的递归路径

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack( nums, start) {
    // 前序位置,每个节点的值都是一个子集
    res.push([...track]);
    
    // 回溯算法标准框架
    for (let i = start; i < nums.length; i++) {
        // 剪枝逻辑,值相同的相邻树枝,只遍历第一条
        if (i > start && nums[i] == nums[i - 1]) {
            continue;
        }
        // 做选择
        track.push(nums[i]);
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i + 1);
        // 撤销选择
        track.pop();
    }
}

let arr = ['a', 'b', 'b'];
arr.sort();  // 排序,将相同元素放在一起

backtrack(arr, 0);
console.log(res);

  

排列(元素可重不可复选)

[a,b,b] 的3元素的所有排列为 [a,b,b] [b,a,b] [b,b,a] 

nums[i] === prevNum 这一句,就是去重,因为如果两个节点是一样的,则其对应的树也是一样的,所以会产生重复。 如  [a,b,b]的排列  [a,b,b'] [a,b',b]
let res = []; // 所有排列的结果
let track = []; // 记录回溯算法的递归路径
let max = 3; // 排列的个数
let used = []; // 记录已经使用了的序号,防止重复

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack(nums) {
    // 前序位置,每个节点的值都是一个子集
    if (max === track.length) {
        res.push([...track]);
        return;
    }

    // 回溯算法标准框架
    let prevNum = -666666; // 一个不可能出现在nums中的值
    for (let i = 0; i < nums.length; i++) {
        if (used[i]) { // 已经存在 track 中的元素,不能重复选择
            continue;
        }
        // 新添加的剪枝逻辑,固定相同的元素在排列中的相对位置
        if ( nums[i] === prevNum) {
            continue;
        }
        // 做选择
        track.push(nums[i]);
        used[i] = true;
        prevNum = nums[i];
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums);
        // 撤销选择
        track.pop();
        used[i] = false;
    }
}

let arr = ['a', 'b', 'b'];
arr.sort();  // 排序,将相同元素放在一起
backtrack(arr); 
console.log(res);

  

 

子集/组合(元素无重可复选)

[a,b]的所有小于等于3的可复选子集为: [] [a] [a, a] [a,b] [a,c] [b] [b,b] [b,c] [c] [c,c]

 

 

let res = [];
// 记录回溯算法的递归路径
let track = [];
let max = 2; // 最大数量

// 回溯算法核心函数,遍历子集问题的回溯树
function backtrack( nums, start) {
    // 前序位置,每个节点的值都是一个子集
    res.push([...track]);
    if(track.length >= 2){
        return;
    }
    // 回溯算法标准框架
    for (let i = start; i < nums.length; i++) {
        // 做选择
        track.push(nums[i]);
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i); // 注意,这里的i不加1,表示下一层使用的序号>=i,但是同一层上从左到右还是递增
        // 撤销选择
        track.pop();
    }
}

backtrack(['a', 'b', 'c'], 0);
console.log(res);

  

 

标签:nums,track,回溯,let,子集,排列组合,backtrack
来源: https://www.cnblogs.com/xiafl/p/16298559.html