题目:输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数22 和如下二元树
10
/ \
5 12
/ \
4 7
则打印出两条路径:10, 12 和10, 5, 7。
二元树节点的数据结构定义为:
struct BinaryTreeNode // a node in the binary tree
{
int m_nValue; // value of node
BinaryTreeNode *m_pLeft; // left child of node
BinaryTreeNode *m_pRight; // right child of node
};
逻辑分析:
1、既然是树,那么第一想法肯定是递归,而这题用递归解也确实是一目了然,类似树的深度遍历,只需要每一次将传入的num-root.m_nValue作为子问题的参数一路向北即可。
2、因为是所有路径,并不是找出某一条路径就结束,所以,考虑回溯+剪枝,解决问题。也就是说,我们需要一个容器,用来存放路径上的每一个点,每当递归遍历,则将路径点存入容器,而当不满足要求的时候,则将该点删除,显然,我们需要一个栈。而当sum==0的时候,则将栈依次打印出来,到此,问题完美解决。
给出代码样本:
///@copyright 玉涵
///updated 2013/12/21
#include <stdio.h>
#define MAX_HEIGHT 10
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode *m_pLeft;
BinaryTreeNode *m_pRight;
};
void helper(BinaryTreeNode *root, int sum, int path[], int top);
void printPaths(BinaryTreeNode *root, int sum)
{
int path[MAX_HEIGHT];
helper(root,sum,path,0);
}
void helper(BinaryTreeNode *root, int sum, int path[], int top)
{
path[top++] = root->m_nValue;
sum -= root->m_nValue;
if(root->m_pLeft == NULL && root->m_pRight == NULL)
{
if(sum == 0)
{
for(int i=0; i<top;i++)
{
printf("%d\t",path[i]);
}
printf("\n");
}
}
else
{
if(root->m_pLeft != NULL)
helper(root->m_pLeft,sum,path,top);
if(root->m_pRight != NULL)
helper(root->m_pRight,sum,path,top);
}
sum += root->m_nValue;
top--;
}
老规矩,写一个很不规范,纯粹是为了测试的主函数:
int main()
{
BinaryTreeNode T10,T5,T12,T4,T7;
T10.m_nValue = 10;
T10.m_pLeft = &T5;
T10.m_pRight = &T12;
T5.m_nValue = 5;
T5.m_pLeft = &T4;
T5.m_pRight = &T7;
T12.m_nValue = 12;
T12.m_pLeft = NULL;
T12.m_pRight = NULL;
T4.m_nValue = 4;
T4.m_pLeft = T4.m_pRight = NULL;
T7.m_nValue = 7;
T7.m_pLeft = T7.m_pRight = NULL;
printPaths(&T10,22);
return 0;
}
输出结果:
3、下午有位道友提出了这样的一个问题,如果在处理到某一中间结点的时候,sum的值直接越变,比如本例子传入的22,如果根节点左孩子是13,那么遍历到13的时候,10+13=23>22,那么是否我们就可以停止向下遍历,显然,从时间复杂度的角度考虑,上述算法的时间复杂度为O(Σleaf[i]*height[i]),其中leaf为叶子个数,height对应个叶结点的深度,那么如果经上述优化,时间复杂度的最差情况则与上式相同。但是这里存在一个问题,结点的域值不一定都是正数,如果存在负数,那么就不得不完全遍历了。
4、由此想到一点,如果二叉树是二叉查找树,那么上述的优化方案显然是可行的,而且是势必要进行的。我们知道二叉查找树的特点是,左子树小于等于根,右子树大于根,这样在遍历的时候,就加了很多判断条件,通过判断条件剪掉部分路径,优化了时间。