46. 全排列 不含重复数字
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
输入: nums = [1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
[ 1 ]
[ 1, 2 ]
[ 1, 2, 3 ]
[ 1, 3 ]
[ 1, 3, 2 ]
[ 2 ]
[ 2, 1 ]
[ 2, 1, 3 ]
[ 2, 3 ]
[ 2, 3, 1 ]
[ 3 ]
[ 3, 1 ]
[ 3, 1, 2 ]
[ 3, 2 ]
[ 3, 2, 1 ]
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
const res = [];
const used = {};
function dfs(path) {
if(path.length === nums.length){
res.push(path.slice());
return res;
}
for(let i = 0; i < nums.length; i++) { // 横向循环
if(used[i]) continue; // 纵向递归时去掉使用过的数字
path.push(nums[i]);
used[i] = true;
console.info(path);
dfs(path); // 纵向递归
path.pop();
used[i] = false;
}
}
dfs([]);
return res;
};
47. 全排列 II 包含重复数字
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
输入: nums = [1,1,2]
输出: [ [1,1,2], [1,2,1], [2,1,1] ]
过滤掉首层未递归前的同层横向重复项(推荐这种,直接去掉了第二个分支)
这种属于从根上直接剪枝
!used[i-1]即used[i - 1] == false,说明同一树层nums[i - 1]使用过
[ 1 ]
[ 1, 1 ]
[ 1, 1, 2 ]
[ 1, 2 ]
[ 1, 2, 1 ]
[ 2 ]
[ 2, 1 ]
[ 2, 1, 1 ]
过滤掉纵向递归中横向同层的重复
这种属于从枝叶上逐个深入剪枝,效率不如从根上剪枝好。理解起来也较为困难
used[i-1]即used[i - 1] == true,说明同一树支nums[i - 1]使用过
[ 1 ]
[ 1, 2 ]
[ 1 ]
[ 1, 1 ]
[ 1, 1, 2 ]
[ 1, 2 ]
[ 1, 2, 1 ]
[ 2 ]
[ 2, 1 ]
[ 2, 1 ]
[ 2, 1, 1 ]
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
let res = [];
let used = {};
// 先排序,以便过滤重复数字
nums.sort((a,b)=>a-b);
const dfs = (path) => {
if(path.length === nums.length){
res.push(path.slice());
return res;
}
for(let i = 0; i < nums.length; i++) {
if(used[i]) continue;
// 过滤掉首层未递归前的同层横向重复项(推荐这种)
if(i>0 && nums[i] === nums[i-1] && !used[i-1]) continue;
// if(i-1>=0 && nums[i] === nums[i-1] && !used[i-1]) continue;
// 过滤掉纵向递归中横向同层的重复
// if(i-1>=0 && nums[i] === nums[i-1] && used[i-1]) continue;
path.push(nums[i]);
console.info(path);
used[i] = true;
dfs(path);
path.pop();
used[i] = false;
}
}
dfs([]);
return res;
};
39. 组合总和
不含重复数字的数组,该数组中的数字可以无限制重复使用
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
输入: candidates = [2,3,6,7], target = 7
输出: [ [7], [2,2,3] ]
[ 2 ]
[ 2, 2 ]
[ 2, 2, 2 ]
[ 2, 2, 2, 2 ]
[ 2, 2, 2, 3 ]
[ 2, 2, 2, 6 ]
[ 2, 2, 2, 7 ]
[ 2, 2, 3 ]
[ 2, 2, 6 ]
[ 2, 2, 7 ]
[ 2, 3 ]
[ 2, 3, 3 ]
[ 2, 3, 6 ]
[ 2, 3, 7 ]
[ 2, 6 ]
[ 2, 7 ]
[ 3 ]
[ 3, 3 ]
[ 3, 3, 3 ]
[ 3, 3, 6 ]
[ 3, 3, 7 ]
[ 3, 6 ]
[ 3, 7 ]
[ 6 ]
[ 6, 6 ]
[ 6, 7 ]
[ 7 ]
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
let res = [];
const dfs = (start, temp, sum) => {
if(sum >= target){
if(sum === target){
res.push(temp.slice());
}
return;
}
for(let i = start; i < candidates.length; i++){
temp.push(candidates[i]);
dfs(i, temp, sum + candidates[i]);
temp.pop();
}
}
dfs(0,[],0);
return res;
};
40. 组合总和 II
包含重复数字的数组,该数组中的数字在每个组合中只能使用一次
需要重点理解:我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
注意:解集不能包含重复的组合。
输入: candidates = [2,3,6,7], target = 7
输出: [ [7] ]
dfs(i + 1, temp, sum + candidates[i]);语句中
i+1保证candidates中的每个数字在每个组合中只能使用一次
[ 2 ]
[ 2, 3 ]
[ 2, 3, 6 ]
[ 2, 3, 7 ]
[ 2, 6 ]
[ 2, 7 ]
[ 3 ]
[ 3, 6 ]
[ 3, 7 ]
[ 6 ]
[ 6, 7 ]
[ 7 ]
输入: candidates = [2,5,2,1,2], target = 5,
输出: [ [1,2,2], [5] ]
需要先排序:[1,2,2,2,5]
不去除横向同层重复项
[ 1 ] [ 1, 2 ] [ 1, 2, 2 ] [ 1, 2, 2 ] [ 1, 2, 5 ]
[ 1, 2 ] [ 1, 2, 2 ] [ 1, 2, 5 ]
[ 1, 2 ] [ 1, 2, 5 ] [ 1, 5 ]
[ 2 ] [ 2, 2 ] [ 2, 2, 2 ] [ 2, 2, 5 ] [ 2, 2 ] [ 2, 2, 5 ] [ 2, 5 ]
[ 2 ] [ 2, 2 ] [ 2, 2, 5 ] [ 2, 5 ]
[ 2 ] [ 2, 5 ]
[ 5 ]
去除横向同层重复项
[ 1 ] [ 1, 2 ] [ 1, 2, 2 ] [ 1, 2, 5 ] [ 1, 5 ]
[ 2 ] [ 2, 2 ] [ 2, 2, 2 ] [ 2, 2, 5 ] [ 2, 5 ]
[ 5 ]
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum2 = function(candidates, target) {
let res = [];
candidates.sort((a, b) => a - b);
const dfs = (start,temp,sum) => {
// 和大于等于目标值时,后面的分支已经不可能满足结果条件,剪枝返回
if(sum >= target){
// 和等于目标值时,记录目标值
if(sum === target){
res.push(temp.slice());
}
return;
}
for(let i = start; i < candidates.length; i++){
// 去除横向同层重复项
if(i > start && candidates[i] === candidates[i-1]) continue;
temp.push(candidates[i]);
// i+1保证candidates中的每个数字在每个组合中只能使用一次
dfs(i + 1, temp, sum + candidates[i]);
temp.pop();
}
}
dfs(0,[],0);
return res;
};
216. 组合总和 III
范围为[1, 9],固定每个组合长度为k,target为n
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。 解集不能包含重复的组合。
输入: k = 2, n = 7
输出: [[1,6],[2,5],[3,4]]
目前的未作剪枝处理,所有的组合都打印出来了。剪枝待更新。。。
[ 1 ] [ 1, 2 ] [ 1, 3 ] [ 1, 4 ] [ 1, 5 ] [ 1, 6 ] [ 1, 7 ] [ 1, 8 ] [ 1, 9 ]
[ 2 ] [ 2, 3 ] [ 2, 4 ] [ 2, 5 ] [ 2, 6 ] [ 2, 7 ] [ 2, 8 ] [ 2, 9 ]
[ 3 ] [ 3, 4 ] [ 3, 5 ] [ 3, 6 ] [ 3, 7 ] [ 3, 8 ] [ 3, 9 ]
[ 4 ] [ 4, 5 ] [ 4, 6 ] [ 4, 7 ] [ 4, 8 ] [ 4, 9 ]
[ 5 ] [ 5, 6 ] [ 5, 7 ] [ 5, 8 ] [ 5, 9 ]
[ 6 ] [ 6, 7 ] [ 6, 8 ] [ 6, 9 ]
[ 7 ] [ 7, 8 ] [ 7, 9 ]
[ 8 ] [ 8, 9 ]
[ 9 ]
/**
* @param {number} k
* @param {number} n
* @return {number[][]}
*/
var combinationSum3 = function(k, n) {
let res = [];
const dfs = (start,temp,sum) => {
if(temp.length === k) {
if(sum === n) {
res.push(temp.slice());
}
return;
}
for(let i = start; i <= 9; i++){
temp.push(i);
console.info(temp);
// sum这里未直接改变sum本身的值,所以在dfs之后也不需要做-i操作
dfs(i + 1,temp,sum + i);
temp.pop();
}
}
// 1为值,非下标
dfs(1,[],0);
return res;
};
77. 组合
范围为[1, n],固定每个组合长度为k
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
输入: n = 4, k = 2
输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
[ 1 ]
[ 1, 2 ]
[ 1, 3 ]
[ 1, 4 ]
[ 2 ]
[ 2, 3 ]
[ 2, 4 ]
[ 3 ]
[ 3, 4 ]
[ 4 ]
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function(n, k) {
let res = [];
const dfs = (start, temp) => {
if(temp.length === k) {
res.push(temp.slice());
return;
}
for(let i = start; i <= n; i++) {
temp.push(i);
console.info(temp);
dfs(i + 1, temp);
temp.pop();
}
}
// 注意这里1是值本身,不是下标
dfs(1, []);
return res;
};
78. 子集 不含重复数字
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
输入: nums = [1,2,3]
输出: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
[]未打印,[]会在最开始加入res结果集中
[ 1 ]
[ 1, 2 ]
[ 1, 2, 3 ]
[ 1, 3 ]
[ 2 ]
[ 2, 3 ]
[ 3 ]
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function(nums) {
let res=[];
const dfs = (start, temp) => {
res.push(temp.slice());
for(let i = start; i < nums.length; i++){
temp.push(nums[i]);
console.info(temp);
dfs(i+1, temp);
temp.pop();
}
};
// 这里0是下标;[]也是子集,会在最开始加入res
dfs(0,[]);
return res;
};
90. 子集 II 包含重复数字
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
输入: nums = [1,2,2]
输出: [[],[1],[1,2],[1,2,2],[2],[2,2]]
不去除横向(同一层)重复的打印结果
[ 1 ] [ 1, 2 ] [ 1, 2, 2 ] [ 1, 2 ] [ 2 ] [ 2, 2 ] [ 2 ]
去除横向(同一层)重复的打印结果;纵向可以重复
[ 1 ] [ 1, 2 ] [ 1, 2, 2 ] [ 2 ] [ 2, 2 ]
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function(nums) {
let res=[];
nums.sort((a,b)=>a-b);
const dfs = (start,temp) => {
res.push(temp.slice());
for(let i = start; i < nums.length; i++){
// 去掉横向同层重复。i > start更好理解,for是横向的
if(i > start && nums[i-1]===nums[i]) continue;
// if(i-1 >= start && nums[i-1]===nums[i]) continue;
temp.push(nums[i]);
dfs(i+1,temp);
temp.pop();
}
}
dfs(0,[]);
return res;
};
131. 分割回文串
输入: s = "aab"
输出: [["a","a","b"],["aa","b"]]
/**
* @param {string} s
* @return {string[][]}
*/
var partition = function(s) {
const isTrue = (str) => {
return str === str.split('').reverse().join('');
}
let res = [];
const dfs = (start, temp) => {
if(start === s.length){
res.push(temp.slice());
return;
}
for(let i = start; i < s.length; i++) {
if(isTrue(s.substring(start, i + 1))) {
temp.push(s.substring(start, i + 1));
dfs(i+1, temp);
temp.pop();
}
}
}
dfs(0, []);
return res;
};