算法之回溯篇【面试题08.04幂集】
一、回溯算法
1、如何理解递归回溯
回溯一般都是伴随着递归而产生的,递归就是不断调用自身处理结果,类似深度遍历树,并记录下每一步的路径,所谓路径,就是在选择列表中做出的选择。而回溯是在递归语句后面的语句,每当递归结束后,便执行回溯撤销操作,对临时结果撤销上一步的选择。
2、基本的框架
变量类型 result=[]; //结果集合
void backtrack(结果集合 result,选择列表 nums,开始下标 start) {
if (终止条件:满足结果的条件) {
result.add(路径)存放结果;
return;
}
for (以选择列表里面的元素作为循环)) {
【1】做出选择,为当前路径添加元素
【2】进行递归, backtrack(结果集合 result,选择列表 nums,开始下标 start进行调整);
【3】回溯,撤销上一步添加的路径添加的元素
}
}
二、题目
1、题目介绍
幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
示例:
下面展示一些 例子
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
2、题目分析
【1】选择列表: 就是nums[]集合,以nums={1,2,3}为例,可选择的元素有1,2,3
【2】路径: 1,2,3任意个组成的集合就是路径,例如: [2,3],
[1,2]…
【3】注意点: 在不断地递归的过程中,如果函数里有循环,在每次的递归中,for(i;条件;i++)中的i并没有执行i++的操作,一般是递归结束后再执行本次的循环。
3、树状图分析
4、本题源代码
这里是引用
class Solution {
public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
backtrack(list, new ArrayList<>(), nums, 0);
return list;
}
private static void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] nums, int start) {
//走过的所有路径都是子集的一部分,所以都要加入到集合中
list.add(new ArrayList<>(tempList));
for (int i = start; i < nums.length; i++) {
//做出选择,添加元素
tempList.add(nums[i]);
//递归
backtrack(list, tempList, nums, i + 1);
//撤销选择,返回到上一步选择的状态
tempList.remove(tempList.size() - 1);
}
}
}
5、以nums[]={1,2,3}为例,详细的执行树型图解
三、总结(致🐱)
只要看懂了以上的过程图,就能比较好地理解递归和回溯,理解了递归回溯的过程,对问题抽象化成三步,抽象化这一步是最关键,也是最难想的,在满足条件下执行以下
【0】路径满足条件就添加到输出的集合
【1】做出选择,在选择列表中选择出元素添加到当前路径
【2】进行递归
【3】回溯,撤销上一步添加的路径添加的元素