回溯法的注意点:
1,递归调用之后,即递归方法的下一行,一定要进行回溯一级!
2,递归触底之后,也是一定要回溯一级!
·
题目案例:
给你一个数字n,请你生成并返回所有 从 1 到 n 可能的全排列 。你可以 按任意顺序 返回答案。
比如:
输入:n = 3 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
·
思路:
把从1~n的每个数字,抽象成一个以该元素为根节点的多叉树,
以n为4举例,如图:
这是以1为根节点的多叉树:
还有以2、3、4为根节点的多叉树;
DFS该多叉树,记录路径;
如何遍历呢:
比如说,以1为起点:
- 遍历数组中的元素,将已遍历过的避开,还剩下——2、3、4,将其中一个元素加入路径中,
- 继续遍历数组中的元素,将已遍历过的避开,还剩下——x、x,将其中一个元素加入路径中,
- 继续遍历数组中的元素,将已遍历过的避开,还剩下——x,将其中一个元素加入路径中,
- 路径的长度等于n了,说明递归触底了,此时回溯一级;
注意:上述每一次递归调用之后,即递归方法的下一行,一定要进行回溯一级!
递归触底了,当然也是要回溯一级。
·
代码演示:
//全排列
public static List<List<Integer>> permute(int n) {
Set<Integer> alreadyAddedSet=new HashSet<>(); //利用HashSet,存储遍历过的元素,防止重复
Deque<Integer> deque = new LinkedList<>(); //辅助栈,记录递归的路径
//递归方法:
helper(alreadyAddedSet, n, deque);
System.out.println("nodeValList = " + nodeValList);
return nodeValList;
}
private static List<List<Integer>> nodeValList = new ArrayList<>(); //全排列结果,存储所有的路径;
/**
* 全排列:
* @param deque 辅助栈,记录递归的路径;
* @param hashSet 利用HashSet,存储存储过的元素,防止重复
*/
public static void helper(Set<Integer> alreadyAddedSet,int n, Deque<Integer> deque) {
for (int currEle = 1; currEle <= n; currEle++) {
//避开已经存储过的元素
if (alreadyAddedSet.contains(currEle)) {
continue;
}else {
//将当前的元素加入路径:
alreadyAddedSet.add(currEle);
deque.push(currEle);
if (deque.size() < n) {
//递归:
helper(alreadyAddedSet,n, deque);
//递归之后的下一行,切记要回溯一级:
//路径回退一级:
//因为使用了Set作为防止元素重复遍历,所以Set也要回退一下,退出刚才的元素:
alreadyAddedSet.remove(deque.pop());
}
//当栈的元素数量==n了, 说明触底了
else{
// 记录这一条路径:
nodeValList.add(new LinkedList<>(deque));
//触底了,要回溯一级:
//路径回退一级:
//因为使用了Set作为防止元素重复遍历,所以Set也要回退一下,退出刚才的元素:
alreadyAddedSet.remove(deque.pop());
return ;
}
}
}
return ;
}
·
测试结果:
输入:3
输入:4