1、题目:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
}
如图中二叉树和整数22,则打印出两条路径,一是包含结点10、12,二是包含结点10、5和7。
10
5 12
4 7
解法:
以上图为例分析,由于路径是从根结点出发到叶结点,也就是说路径总是以根结点为起始点,因此需要首先遍历根结点,即前序遍历。
1)每访问到一个结点时,把当前的结点添加到路径当中去。比如到达5时,路径中包含两个结点,值分别是10和5,接下来遍历到结点4,把它也加入到路径中去,此时已到达了叶结点,但总和为19,不等于输入的22,因此不是符合的路径。
2)每一次当从子结点回到父结点时,需要在路径上删除子结点。比如上述中从结点4回到结点5,再去遍历结点5的右子结点7。由于结点4已经不在前往结点7的路径上了,故把结点4从路径中删除。
2、规律:
当用前序遍历的方式访问到某一结点时,把该结点添加到路径上,并累加该结点的值。如果该结点为叶结点并且路径上结点值得和刚好等于输入的整数,则打印出来。若当前结点不是叶结点,则继续访问它的子结点。当前结点访问结束后,递归函数将自动返回它的父结点。在函数退出之前要在路径上删除当前结点并减去当前结点的值。
可以看出保存路径的数据结构是一个栈,因为路径要与递归调用状态一致,而递归调用的本质就是一个压栈和出栈的过程。
void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
if(pRoot == NULL)
return;
std::vector<int> path;
int currentSum = 0;
FindPath(pRoot, expectedSum, path, currentSum);
}
void FindPath(BinaryTreeNode* pRoot,
int expectedSum,
std::vector<int>& path,
int currentSum)
{
currentSum += pRoot->m_nValue;
path.push_back(pRoot->m_nValue);
//如果是叶节点,并且路径上结点的和等于输入的值
//打印出这条路径
bool isLeaf = pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL;
if(currentSum == expectedSum && isLeaf)
{
printf("A path is found: ");
std::vector<int>::iterator iter = path.begin();
for(; iter != path.end(); ++iter)
printf("%d\t", *iter);
printf("\n");
}
//如果不是叶结点,则遍历它的子结点
if(pRoot->m_pLeft != NULL)
FindPath(pRoot->m_pLeft, expectedSum, path, currentSum);
if(pRoot->m_pRight != NULL)
FindPath(pRoot->m_pRight, expectedSum, path, currentSum);
//在返回到父节点之前,在路径上删除当前结点
path.pop_back();
}
3、我们用vector实现了一个栈来保存路径,每一次都用push_back在路径的末尾添加结点,用pop_back在路径的末尾删除结点,保证了栈的先入后出。这里没有用STl中的stack是因为在stack中只能得到栈顶元素,而我们打印路径时需要得到路径上的所有结点。