题意:
由连续两行来表示一棵二叉树, 第一行为中序遍历, 第二行为后序遍历; 要求输出从根节点到叶子节点的各个路径中, 和最小的路径的叶子节点的值; 如果存在多个最小和, 则输出值最小的叶子节点.
思路:
1. 根据输入的中序遍历和后序遍历创建树:
(1). 中序:左根右;
(2). 后序:左右根;
(3). 由上可以看到,从后序遍历最后开始,找到“根”,然后在中序中找到“根”,其左边就是左子树,右边就是右子树.依此递归, 可建树.
2. 从左/右子树递归寻找最小路径和, 为了确保当路径和相等时, 取最小叶节点, 需要把叶子节点的值也和一起返回.
要点:
1. 使用 istringstream 读取 string 中的各个 int, 速度并不比自己解析字符串, 然后 atoi 慢太多, 如果提交时提示 Time limit exceeded, 应该是别的地方出了问题, 无需在这里优化太多.
2. vector back 函数返回最后一个值.
代码:
# include <iostream>
# include <string>
# include <cstdio>
# include <cstring>
# include <vector>
# include <algorithm>
# include <cctype>
# include <iterator>
# include <sstream>
# include <assert.h>
using namespace std;
// 读入两行,第一行中序,第二行后序遍历,
// 要求找到从根节点到叶节点路径和最小的叶节点,若有多个最小和,则输出最小的叶节点
class Node {
public:
Node(int n, Node* left, Node* right):
n_(n), left_(left), right_(right){}
int n_;
Node* left_;
Node* right_;
};
// 找到 n 在 vt[begin, end) 中的下标,若找不到,则返回 end
// 为了保证重复节点出现时,找到的下标是靠后的节点,这里从后往前找
int getIndex(const vector<int>& vt, int begin, int end, int n) {
for (int i=end-1; i>=begin; i--) {
if (vt[i] == n) return i;
}
return end;
}
// 根据中序与后序遍历创建树
// 中序:左根右
// 后序:左右根
// 算法:由上可以看到,从后序最后开始遍历,找到“根”,然后在中序中找到“根”,
// 其左边就是左子树,右边就是右子树
Node* buildTree(const vector<int>& inOrder, int inBegin, int inEnd,
const vector<int>& postOrder, int postBegin, int postEnd) {
if (inBegin>=inEnd || postBegin>=postEnd) return NULL;
// 后序的最后一位就是根节点
int n = postOrder[postEnd-1];
int rootIndex = getIndex(inOrder, inBegin, inEnd, n);
int leftLength = rootIndex - inBegin;
Node* left = buildTree(inOrder, inBegin, rootIndex,
postOrder, postBegin, postBegin + leftLength);
Node* right = buildTree(inOrder, rootIndex + 1, inEnd,
postOrder, postBegin + leftLength, postEnd - 1);
Node* root = new Node(n, left, right);
return root;
}
// 释放树空间
void freeTree(Node* root) {
if (root == NULL) return;
freeTree(root->left_);
freeTree(root->right_);
delete root;
}
// 找到从根节点到叶节点路径和最小的叶节点,若有多个最小和,则输出最小的叶节点
// 返回值,first 表示当前最小和,second 表示对应叶节点的值
pair<int, int> getLeastLeafNode(Node* root) {
if (root->left_ == NULL && root->right_ == NULL) {
return make_pair(root->n_, root->n_);
}
pair<int, int> least;
if (root->left_ == NULL && root->right_ != NULL) {
least = getLeastLeafNode(root->right_);
} else if (root->left_ != NULL && root->right_ == NULL) {
least = getLeastLeafNode(root->left_);
} else {
pair<int, int> left = getLeastLeafNode(root->left_);
pair<int, int> right = getLeastLeafNode(root->right_);
if (left.first < right.first) {
least = left;
} else if (left.first > right.first) {
least = right;
} else { // left.first == right.first
least = left.second < right.second ? left : right;
}
}
return make_pair(least.first + root->n_, least.second);
}
int main(int argc, char const *argv[])
{
#ifndef ONLINE_JUDGE
freopen("548_i.txt", "r", stdin);
freopen("uva_o.txt", "w", stdout);
#endif
// 输入
string inLine;
string postLine;
while (getline(cin, inLine) && getline(cin, postLine)) {
int n;
vector<int> inOrder, postOrder;
// 读取一行中的各个 int
// 中序
istringstream issIn(inLine);
while (issIn >> n) inOrder.push_back(n);
// 后序
istringstream issPost(postLine);
while (issPost >> n) postOrder.push_back(n);
// 建树,分析,输出
Node* root = buildTree(inOrder, 0, inOrder.size(),
postOrder, 0, postOrder.size());
cout << getLeastLeafNode(root).second << endl;
freeTree(root);
}
return 0;
}
环境: C++ 4.5.3 - GNU C++ Compiler with options: -lm -lcrypt -O2 -pipe -DONLINE_JUDGE
参考: http://hi.baidu.com/knowledgetime/item/bc9a13f74566cee71b111fa5
这里提到给定的数据时有重复的节点, 那么你在中序序列中应找最靠后的那个节点, 因为后序遍历是 左右中, 只有这样才能正确的遍历这棵树.