面试题25:二叉树中和为某一个值的路径
题目:输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。二叉树的定义如下:
/*******二叉树结点定义*******/
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
预备知识:
vector中的push_back是添加一个元素
vector中的pop_back是删除一个元素
二叉树的操作:
#include "stdafx.h"
#include "BinaryTree.h"
//创建树结点
BinaryTreeNode* CreateBinaryTreeNode(int value)
{
BinaryTreeNode* pNode = new BinaryTreeNode(); //分配内存,返回头指针
pNode->m_nValue = value; //结点值
pNode->m_pLeft = NULL; //指向左子结点
pNode->m_pRight = NULL; //指向右子结点
return pNode;
}
//链接结点
void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
if(pParent != NULL)
{
pParent->m_pLeft = pLeft; //左子结点
pParent->m_pRight = pRight; //右子结点
}
}
//输出树的单个结点,同时也输出它的子结点
void PrintTreeNode(BinaryTreeNode* pNode)
{
if(pNode != NULL)
{
printf("value of this node is: %d\n", pNode->m_nValue);
if(pNode->m_pLeft != NULL)
printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
else
printf("Left child is null.\n");
if(pNode->m_pRight !=NULL)
printf("value is its right child is: %d.\n", pNode->m_pRight->m_nValue);
else
printf("right child is null.\n");
}
else
{
printf("this node is null.\n");
}
printf("\n");
}
//遍历树的所有结点,并输出(这里使用了前序遍历:先根结点-左子结点-右子结点)
void PrintTree(BinaryTreeNode* pRoot)
{
PrintTreeNode(pRoot); //先输出根结点
if(pRoot !=NULL)
{
if(pRoot->m_pLeft != NULL)
PrintTree(pRoot->m_pLeft);
if(pRoot->m_pRight != NULL)
PrintTree(pRoot->m_pRight);
}
}
//销毁树的所有结点(这里使用了前序遍历,销毁所有的结点)
void DestroyTree(BinaryTreeNode* pRoot)
{
if(pRoot != NULL)
{
BinaryTreeNode* pLeft = pRoot->m_pLeft;
BinaryTreeNode* pRight = pRoot->m_pRight;
delete pRoot; //根结点
pRoot = NULL;
DestroyTree(pLeft); //左子结点
DestroyTree(pRight); //右子结点
}
}
思路:这里我们遇到了一个新的概念“路径”,由此不妨可以通过几个具体的例子加以说明,如可以通过画图或列表的形式,从而找到其中的规律。事实上通过具体的例子我们不能发现:当用前序遍历的方式访问到某个结点的时候,我们把该结点添加到路径上,并累加该结点的值。如果该结点为叶结点,并且路径中结点值的和刚好等于输入的整数,则当期的路径符合要求,我们就打印出来。如果当前的结点不是叶结点,那么继续访问它的子结点。当前结点访问结束后,递归函数将自动回到它的父结点。因此,在退出函数的时候,要在路径上删除当前结点,并减去当前结点的值,以确保返回父节点时路径刚好是从根结点到父结点的路径。
算法实现:
// 面试题25.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "..\BinaryTree.h"
#include <vector> //vector
void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector<int>& path, int currentSum);
//二叉树中和为某一值的路径
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();
}
//***************测试代码********************
// 10
// / \
// 5 12
// /\
// 4 7
void Test1()
{
// 创建树的结点
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(10);
BinaryTreeNode* pNode2 = CreateBinaryTreeNode(5);
BinaryTreeNode* pNode3 = CreateBinaryTreeNode(12);
BinaryTreeNode* pNode4 = CreateBinaryTreeNode(4);
BinaryTreeNode* pNode5 = CreateBinaryTreeNode(7);
//连接结点构成二叉树
ConnectTreeNodes(pNode1, pNode2, pNode3);
ConnectTreeNodes(pNode2, pNode4, pNode5);
//测试函数
FindPath(pNode1, 22);
//销毁二叉树
DestroyTree(pNode1);
}
// 5
// /
// 4
// /
// 3
// /
// 2
// /
// 1
void Test2()
{
// 创建树的结点
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(5);
BinaryTreeNode* pNode2 = CreateBinaryTreeNode(4);
BinaryTreeNode* pNode3 = CreateBinaryTreeNode(3);
BinaryTreeNode* pNode4 = CreateBinaryTreeNode(2);
BinaryTreeNode* pNode5 = CreateBinaryTreeNode(1);
//连接树的结点构成二叉树
ConnectTreeNodes(pNode1,pNode2,NULL);
ConnectTreeNodes(pNode2,pNode3,NULL);
ConnectTreeNodes(pNode3,pNode4,NULL);
ConnectTreeNodes(pNode4,pNode5,NULL);
//测试函数
FindPath(pNode1, 15);
//销毁二叉树
DestroyTree(pNode1);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
return 0;
}