最近在这里看到一道面试题,看了题目和作者的解答后,感觉真是强大。
题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。
例如输入整数22和如下二元树
则打印出两条路径: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
};
作者的解答为:
///
// Find paths whose sum equal to expected sum
///
void FindPath
(
BinaryTreeNode* pTreeNode, // a node of binary tree
int expectedSum, // the expected sum
std::vector<int>& path, // a path from root to current node
int& currentSum // the sum of path
)
{
if(!pTreeNode)
return;
currentSum += pTreeNode->m_nValue;
path.push_back(pTreeNode->m_nValue);
// if the node is a leaf, and the sum is same as pre-defined,
// the path is what we want. print the path
bool isLeaf = (!pTreeNode->m_pLeft && !pTreeNode->m_pRight);
if(currentSum == expectedSum && isLeaf)
{
std::vector<int>::iterator iter = path.begin();
for(; iter != path.end(); ++ iter)
std::cout << *iter << '\t';
std::cout << std::endl;
}
// if the node is not a leaf, goto its children
if(pTreeNode->m_pLeft)
FindPath(pTreeNode->m_pLeft, expectedSum, path, currentSum);
if(pTreeNode->m_pRight)
FindPath(pTreeNode->m_pRight, expectedSum, path, currentSum);
// when we finish visiting a node and return to its parent node,
// we should delete this node from the path and
// minus the node's value from the current sum
currentSum -= pTreeNode->m_nValue;
path.pop_back();
}
仔细看代码,你会发现,这种方法遍历了解空间树,所有的叶子节点都会访问到,
如果二叉树是这样的呢:
按照这种方法,20的两个孩子都会访问到,但是,这在做无用功,因为,题目要求的是从根节点到叶子节点的路径和为22,当访问到20的时候,路径和已是30了(大于22),再访问20的孩子,路径和也会大于22,这样就没有必要再访问20的孩子了。
所以,应该避免这种无效搜索,在遍历每个节点的时候,先判断路径和是否大于目标值,如果大于的话,则不要遍历该节点的孩子,并且回溯。
优化后的代码为:
void FindPath(
BinaryTreeNode* pTreeNode, // a node of binary tree
int expectedSum, // the expected sum
std::vector<int>& path, // a path from root to current node
int& currentSum // the sum of path
)
{
if(!pTreeNode)
return;
currentSum += pTreeNode->m_nValue;
if(currentSum > expectedSum){ //判断路径和是否会超出目标值,超出则回溯
currentSum -= pTreeNode->m_nValue;
return;
}
path.push_back(pTreeNode->m_nValue);
// if the node is a leaf, and the sum is same as pre-defined,
// the path is what we want. print the path
bool isLeaf = (!pTreeNode->m_pLeft && !pTreeNode->m_pRight);
if(currentSum == expectedSum && isLeaf)
{
std::vector<int>::iterator iter = path.begin();
for(; iter != path.end(); ++ iter)
std::cout << *iter << '\t';
std::cout << std::endl;
}
// if the node is not a leaf, goto its children
if(pTreeNode->m_pLeft)
FindPath(pTreeNode->m_pLeft, expectedSum, path, currentSum);
if(pTreeNode->m_pRight)
FindPath(pTreeNode->m_pRight, expectedSum, path, currentSum);
// when we finish visiting a node and return to its parent node,
// we should delete this node from the path and
// minus the node's value from the current sum
currentSum -= pTreeNode->m_nValue;
path.pop_back();
}
在原来的代码里增加了如下代码:
if(currentSum > expectedSum){ //判断路径和是否会超出目标值,超出则回溯
currentSum -= pTreeNode->m_nValue;
return;
}
剪去无效的子树,避免无效搜素,提高效率。
参考:
http://zhedahht.blog.163.com/blog/static/254111742007228357325/