持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
回溯算法将集合问题一网打尽
1. 子集
1.1 问题描述
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
1.2 要求
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
1.3 思路
每一个元素都面临着两种选择,选或者不选。index 作为添加递归层数的判断依据,如果 index === length 则说明子集的一种情况已经产生,将其加入 paths即可。求取子集问题,不需要任何剪枝!因为子集就是要遍历整棵树。
1.4 代码
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
const length = nums.length;
const paths = [];
const backtracking = (index, path = []) => {
if (index === length) {
paths.push([...path]);
return;
}
path.push(nums[index]);
backtracking(index + 1, path);
path.pop();
backtracking(index + 1, path);
}
backtracking(0);
return paths;
};
2. 子集 II
2.1 问题描述
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
2.2 要求
示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
2.3 思路
如果你能理解上面的回溯法,那么包含重复元素的数组的子集,只不过一个小的改进。
比如说求 nums = [1,2,2] 的子集,那么对于子集 [1,2] 是选择了第一个 2,那么就不能再选第二个 2 来构成 [1,2] 了。所以,此时的改动点,就是先排序,每个元素 nums[i] 添加到 path 之前,判断一下 nums[i] 是否等于 nums[i - 1] ,如果相等就不添加到 path 中。
下面以[2,1,2,4,3]为例看看具体推导过程:
-
将数组排序,如
[2,1,2,4,3]得排成[1,2,2,3,4] -
第零层(特殊):
[],剩余的子集为[1,2,2,3,4],故开启新的一层 -
第一层(特殊元素为1):
- 先向下深搜,有
[1],[1,2],[1,2,2],[1,2,2,3],[1,2,2,3,4] - 现在开始回溯并去除元素
4([1,2,2,3]),但4后面没有元素了,故继续回溯 - 现在开始回溯并去除元素
3([1,2,2]),然后3后面有子集合[4] - 再对
[4]进行分层,有一层且该层只有4,故有[1,2,2,4] - 现在开始回溯并去除元素
2([1,2]),然后2后面有子集合[3,4] - 现在开始回溯并去除元素
2([1,2]),然后2后面有子集合[3,4] - 现在开始回溯并去除元素
2([1]),然后2后面有集合[2,3,4],但因为求子集合不能有重复元素 - 故子集合为
[3,4],故有[1,3],[1,3,4],[1,4] - 现在开始回溯并去除元素
1([]),然后有子集合[2,2,3,4],开启新的一层
- 先向下深搜,有
-
第二层(特殊元素为2):
- 先向下深搜,有
[2],[2,2],[2,2,3],[2,2,3,4] - 现在开始回溯并去除元素
4([2,2,3]),但4后面没有元素了,故继续回溯 - 现在开始回溯并去除元素
3([2,2]),然后3后面有子集合[4] - 再对
[4]进行分层,有一层且该层只有4,故有[2,2,4] - 现在开始回溯并去除元素
2([2]),然后2后面有子集合[3,4] - 再对
[3,4]进行分层,第一层为[3],[3,4],第二层为[4],故有[2,3],[2,3,4],[2,4] - 现在开始回溯并去除元素
2([]),然后2后面有子集合[2,3,4] - 但是
2重复了,故子集合为[3,4],开启新的一层
- 先向下深搜,有
-
后面也是以此类推
2.4 代码
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function(nums) {
const length = nums.length;
const paths = [];
nums.sort((a, b) => a - b);
const backtracking = (index, path = []) => {
paths.push([...path]);
for(let i = index; i < length; i++) {
if (i > index && nums[i] === nums[i - 1]) continue;
path.push(nums[i]);
backtracking(i + 1, path);
path.pop();
}
}
backtracking(0);
return paths;
};