剑指Offer——二叉树中和为某一值的路径

来源:互联网 发布:淘宝货到付款怎么关闭 编辑:程序博客网 时间:2024/06/16 15:58

题目:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的所有的节点形成一条路径。

输入如下图的二叉树和整数22,则打印出两条路径,第一条路径包含节点10,12,第二条路径包含的节点为10,5,7。

一般的数据结构和算法的教材都没有介绍树的路径,因此对很多人而言,这是一个新概念,也就很难一下子想出完整的解题思路。这个时候我们可以试着从一两个具体的例子入手,找到规律。

以图中的二叉树为例分析,由于路径是从根节点出发到叶节点,也就是说路径是以根节点为起始点,因此,我们首先需要遍历根节点。在树的前序、中序、后序三种遍历方式中,只有前序遍历是首先访问根节点的。

按照前序遍历的顺序遍历图中的二叉树,在访问节点10之后,就会访问节点5,。从二叉树节点的定义可以看出,在本题的二叉树节点中没有指向父节点的指针,访问到节点5的时候,我们是不知道前面经过了哪些节点的,除非我们把经过的路径上的节点都保存起来。每访问到一个节点的时候,我们都把当前的节点添加到路径中去。到达节点5时,路径中包含两个节点,它们的值分别是10和5。接下来遍历到节点4,我们把这个节点也添加到路径中,这个时候已经到达了叶节点,但路径上三个节点的值之和为19。这个和不等于输入的值22,因此不符合要求的路径。

我们接着遍历的其他的节点,在遍历下一个节点之前,先要从节点4回到节点5,再去遍历节点5的右子结点7。值得注意的是,回到节点5的时候,由于节点4已经不在前往节点7的路径上了,我们需要把节点4从路径上删除。接下来访问到节点7的时候,再把节点添加到路径中。此时路径中三个节点10,5,7之和刚好是22,是一条符合要求的路径。

我们最后要遍历的节点是12。在遍历这个节点之前,需要先经过节点5回到节点10。同样,每一次当从子节点回到父节点的时候,我们都需要在路径上删除子节点。最后从节点10到达节点12的时候,路径上的两个节点的值之和也是22,因此,这也是一条符合条件的路径。

总结过程如下:


分析完前面的例子,我们就找到了一点规律。当用前序遍历的方式访问到某一个节点时,我们把该节点添加到路径上,并累加该节点的值。如果该节点的为叶节点并且路径中节点值的和刚好为输入的整数,则当前的路径符合要求,我们把它打印出来。如果当前的节点不是叶节点,则继续访问它的子节点。当前节点访问结束后,递归函数将自动回到它的父节点。因此我们在函数退出之前要在路径上删除当前节点并减去当前节点的值,以确保返回父节点时路径刚好是从根节点到父节点的路径。我们不难看出保存路径的数据结构实际上是一个栈,因此路径要与递归调用状态一致,而递归调用的本质是一个压栈和出栈的过程。

代码实现:

import java.util.Stack;class BinaryTreeNode {int value;BinaryTreeNode left;BinaryTreeNode right;}public class PathValueSum {public void findPath(BinaryTreeNode root, int sum) {if(root == null)return;Stack<Integer> stack = new Stack<Integer>();int currentSum = 0;findPath(root, sum, stack, currentSum);}private void findPath(BinaryTreeNode root, int sum, Stack<Integer> stack,int currentSum) {currentSum += root.value;stack.push(root.value);if(root.left == null && root.right == null) {if(currentSum == sum) {//找到一条路径for(int item : stack) {//输出路径System.out.print(item + " ");}System.out.println();}}if(root.left != null)findPath(root.left, sum, stack, currentSum);if(root.right != null)findPath(root.right, sum, stack, currentSum);//函数返回时,出栈stack.pop();}}






阅读全文
0 0
原创粉丝点击