Loading

回溯算法

回溯法有“通用解题法”之称。用它可以系统地搜索问题的所有解。

回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。

回溯法求问题的所有解时,要回溯到根,且根结点的所有子树都被搜索遍才结束。回溯法求问题的一个解时,只要搜索到问题的一个解就可结束。

这种以深度优先方式系统搜索问题解的算法称为回溯法,它适用于求解组合数较大的问题。也经常适用于求解二叉树的相关问题,例如以下两个二叉树路径总和的问题:

二叉树是否存在某一路径总和

示例:
给定如下二叉树,以及目标和 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]
]

提示:

  1. 节点总数 <= 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();
    }
}
posted @ 2020-09-01 17:03  codeduck  阅读(246)  评论(0编辑  收藏  举报