java多叉树的深搜_子集、全排列、组合 问题模式

文章目录

78. 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

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

输出: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

子集: 对每个元素都有 取与不取 两个状态,所以共有2^n种子集,正好可以使用对应位的二进制掩码来判断每个元素的状态,1取0不取,共有1<

拿题目来举例子:

对应二进制数字为:000 001 010 011 100 101 110 111

class Solution {

public:

vector> subsets(vector& nums) {

int len = nums.size();

if(len<1) return vector> {{}};

int bit_map = 0;

int end = 1<

vector> res;

vector tmp;

// 2^n个掩码(状态),然后取当前掩码对应的子集,需要遍历数组 n*2^n

while(bit_map

for(int i=0;i

if((1<

res.push_back(tmp);

tmp.clear();

bit_map++;

}

return res;

}

};

复杂度分析

时间复杂度:O(N×2^N),生成所有的子集,并复制到输出列表中。

空间复杂度:O(2^N),存储所有子集,共N个元素,每个元素都有可能存在或者不存在。

递归回溯 求解

其实存在两种递归求解的方法,一种是把解空间当做二叉树进行深度优先或者广度优先的遍历,一种则是把解空间当做图(多叉树)进行深度优先的遍历。

当成一个满二叉树 求解, 对于每一个 元素 都有 选与不选 两个结果,对应左右节点,这样每个叶子节点都是一个结果,但是这样处理会 遍历所有节点,速度会稍慢。

297a9c6237eacac90b7a1af50cac840d.png

class Solution {

public:

vector> res;

void dfs(const vector& nums,int index,vector& ans){

if(index == nums.size()) res.push_back(ans);

if(index < nums.size()){

dfs(nums,index+1,ans); // 不选

ans.push_back(nums[index]);

dfs(nums,index+1,ans); // 选

ans.pop_back(); // 删除 回溯到上一个状态

}

}

vector> subsets(vector& nums) {

if(nums.size()<1) return vector> {{}};

vector ans;

dfs(nums,0,ans);

return res;

}

};

// [[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]

深度搜索,一直不选择到底,然后回溯慢慢添加元素,

回溯

它的每一个结点是满足题目要求的一个解,结点之间的相邻关系取决于该结点是否还可以塞入其他元素。

13d245979b6c10f1f5acdb1bfab46264.png

class Solution {

public:

vector> res;

void backtrack(const vector nums,int index,vector& tmp){

res.push_back(tmp);

for(int i=index;i

tmp.push_back(nums[i]);

backtrack(nums,i+1,tmp);

tmp.pop_back();

}

}

vector> subsets(vector& nums) {

if(!nums.size()) return res;

vector tmp;

backtrack(nums,0,tmp);

return res;

}

};

递归回溯可能模板

void dfs(待搜索集合,递归层数,状态变量1,2,结果集){

// 终止条件 return

// 或者 达到搜索结果 return

for(可执行分支){

// 剪枝

if 递归到第几层, 状态变量 1, 状态变量 2, 符合一定的剪枝条件:

continue

// do something 对状态变量的操作

dfs(待搜索集合,递归层数+1,状态变量1,2,结果集); // 继续递归

// do something 回溯 对状态变量的操作

// 如 push_back(i) pop_back() 删除这个元素,回溯到添加这个元素上一步

}

}

90 子集II

回溯

去重,首先对数组进行排序,使重复元素挨在一起

然后回溯时,如果同一层分支中存在重复元素,则跳过

cd98697de59e368e36c8088f7615c165.png

class Solution {

public:

vector> res;

void backtrack(const vector nums,int index,vector& tmp){

res.push_back(tmp);

for(int i=index;i

//同层递归中,第二次遇到重复元素跳出

if(i != index && nums[i-1]==nums[i])

continue;

tmp.push_back(nums[i]);

backtrack(nums,i+1,tmp);

tmp.pop_back();

}

}

vector> subsetsWithDup(vector& nums) {

if(!nums.size()) return res;

vector tmp;

sort(nums.begin(),nums.end());

backtrack(nums,0,tmp);

return res;

}

};

77 组合

回溯

使用多叉图的方法,对每个

1d2c8412c387bf9a7451f831e77e4fd0.png

class Solution {

public:

vector> res;

void backtrack(int n,int k,vector& tmp,int index){

if(tmp.size() == k) res.push_back(tmp);

for(int i=index;i<=n;i++){

tmp.push_back(i);

backtrack(n,k,tmp,i+1);

tmp.pop_back();

}

}

vector> combine(int n, int k) {

if(k<1) return res;

vector tmp;

backtrack(n,k,tmp,1);

return res;

}

};

55d28f6cc1b717fcfbec9c6c7500abf5.png

二叉树 深搜

对每个节点,都有选择与不选择 两个状态,分别对应两个分支, 但是因为取到k个元素就满足,所以递归到k深度 即可返回

class Solution {

public:

vector> res;

void dfs(int n,int k,int index,vector& tmp){

if(tmp.size() == k) {

res.push_back(tmp);

return ;

}

if(index<=n && tmp.size() < k){

dfs(n,k,index+1,tmp);

tmp.push_back(index);

dfs(n,k,index+1,tmp);

tmp.pop_back();

}

}

vector> combine(int n, int k) {

if(k<1) return vector> {{}};

vector tmp;

dfs(n,k,1,tmp);

return res;

}

};

46 全排列

class Solution {

public:

vector> res;

void dfs(const vector nums,vector& dp,vector& tmp,int len){

if(tmp.size() == len) res.push_back(tmp);

for(int i=0;i

if(!dp[i]){

tmp.push_back(nums[i]);

dp[i] = true;

dfs(nums,dp,tmp,len);

tmp.pop_back();

dp[i] = false;

}

}

}

vector> permute(vector& nums) {

int len = nums.size();

if(!len) return res;

vector dp(len,false);

vector tmp;

dfs(nums,dp,tmp,len);

return res;

}

};

47 全排列II

class Solution {

public:

vector> res;

void dfs(const vector nums,vector& dp,vector& tmp,int len){

if(tmp.size() == len) res.push_back(tmp);

for(int i=0;i

if(i!=0 && nums[i-1]==nums[i] && !dp[i-1]) continue;

if(!dp[i]){

tmp.push_back(nums[i]);

dp[i] = true;

dfs(nums,dp,tmp,len);

tmp.pop_back();

dp[i] = false;

}

}

}

vector> permuteUnique(vector& nums) {

int len = nums.size();

if(!len) return res;

sort(nums.begin(),nums.end());

vector dp(len,false);

vector tmp;

dfs(nums,dp,tmp,len);

return res;

}

};

39 组合总和

ff732770d6f24ae499dfa51d88d3f59f.png

回溯算法

这一题的难点在于数字可以无限制重复,但是又要避免解集中出现重复,由此想到累加的下一次要从自身的index开始。

递归条件 加上当前元素 不超过目标值 则可以进一步递归 超过则退出

可以先排序,然后超过元素之后的所有元素都可以跳过 不排序的话,就对所有元素进行遍历,判断是否进入下一层递归

去重

如果不去重,会产生 [223] [232] [322] 这些类似结果

class Solution {

public:

vector> res;

void dfs(const vector nums,int sum,int target,int len,vector& tmp,int index){

if(sum == target) {

res.push_back(tmp);

return ;

}

for(int i=index;i

if(sum+nums[i]>target) continue;

tmp.push_back(nums[i]);

dfs(nums,sum+nums[i],target,len,tmp,i);

tmp.pop_back();

}

}

vector> combinationSum(vector& candidates, int target) {

int len = candidates.size();

if(!len) return res;

vector tmp;

dfs(candidates,0,target,len,tmp,0);

return res;

}

};

// sort(nums)

// if(sum+nums[i]>target) break;

// 排序后 如果当前元素大于目标值 则后面所有元素都大于 直接跳出循环

40 组合总和II

5a0a833480f628cbc706a3198ae2855c.png

回溯

每个字符只能使用一次 所以将index当做状态变量递归

去重,首先对数组进行排序,重复的元素在一起,递归时 判断上次元素和这次元素相等则跳出,

剪枝 如果当前元素已经大于目标值,则跳出循环,此后所有元素不满足 目标状态

class Solution {

public:

vector> res;

void dfs(const vector& nums,int target,int sum,int index,vector& tmp){

if(sum == target) res.push_back(tmp);

for(int i=index;i

if(i!=index && nums[i-1]==nums[i]) continue;

tmp.push_back(nums[i]);

dfs(nums,target,sum+nums[i],i+1,tmp);

tmp.pop_back();

}

}

vector> combinationSum2(vector& candidates, int target) {

int len = candidates.size();

if(!len) return res;

sort(candidates.begin(),candidates.end());

vector tmp;

dfs(candidates,target,0,0,tmp);

return res;

}

};

关于去重一个比较清晰的解释

其实就是如何 去除同层的重复 并且保留不同层的相同值

if(i!=index && nums[i-1]==nums[i]) continue;主要实现就是 i!=index,重复元素在同层第二次取到时 一定满足 i!=index 所以将它去除

a7edce64c5807d9853605218878b4517.png

标签:tmp,index,排列,组合,nums,int,res,vector,子集

来源: https://blog.csdn.net/junqing_wu/article/details/104630843

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值