回溯算法
回溯法有“通用解题法”之称。用它可以系统地搜索问题的所有解。
回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
回溯法求问题的所有解时,要回溯到根,且根结点的所有子树都被搜索遍才结束。回溯法求问题的一个解时,只要搜索到问题的一个解就可结束。
这种以深度优先方式系统搜索问题解的算法称为回溯法,它适用于求解组合数较大的问题。也经常适用于求解二叉树的相关问题,例如以下两个二叉树路径总和的问题:
二叉树是否存在某一路径总和
示例:
给定如下二叉树,以及目标和 sum = 22
,
5
/ \
4 8 (左)sum = sum - 5 = 17
/ / \
11 13 4 (左)sum = sum - 4 = 13
/ \ \
7 2 1 (左)sum = sum - 11 = 2
返回 true
, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2
。
使用递归,分别遍历左右子树,当左右子树中有一个路径满足条件,则返回true
递归结束条件:root.left == null && root.right == null && sum == root.val
sum的值自顶向下 逐层递减 sum = sum - root.val。
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
// 当前节点为空,sum 大于等于 0;例如:root = [],sum = 0、sum = 1
if (root == null && sum >= 0) {
return false;
}
// 当前节点为空,sum 小于 0;例如:root = [],sum = -1
if (root == null && sum < 0) {
return false;
}
// 当且仅当 左右孩子节点为空 且 sum = root.val 时,则找到目标
if (root.left == null && root.right == null && sum == root.val) {
return true;
}
// 遍历目标节点的左子树和右子树 ,当左右子树中有一个返回true(即找到目标) 时,则返回true;
// sum = sum - root.val,当到达叶子节点且sum= root.val则 找到目标
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
}
二叉树路径总和为某值的所有路径
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
提示:
节点总数 <= 10000
解题思路:从根节点开始递归遍历,每遍历一个节点,则把当前节点入栈。
同时判断 sum == 当前节点.val(每遍历一个节点时,其sum = sum - 父节点.val)。
如果到达叶子节点且sum = 当前节点.val,则说明找到了正确的路径,就将栈内的所有元素保存到链表中。
每次返回父节点前都要将栈顶的节点删除再返回父节点。
class Solution {
List<List<Integer>> resList = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if (root == null) {
return new ArrayList<>();
}
findPath(root, sum);
return resList;
}
public void findPath(TreeNode root, int currentSum) {
// 将当前根节点入栈
stack.push(root);
// 判断节点状态及sum数值,找到合适的路径就将栈内元素保存到链表中
if ((root.left == null && root.right == null) && currentSum == root.val) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = stack.size() - 1; i >= 0; i--) {
TreeNode node = stack.get(i);
list.add(node.val);
}
resList.add(list);
}
// 如果不是叶子节点,则继续遍历其子节点
if (root.left != null) {
findPath(root.left, currentSum - root.val);
}
if (root.right != null) {
findPath(root.right, currentSum - root.val);
}
// 递归返回其父节点前,将栈内路径上的当前节点删除
stack.pop();
}
}