548 - Tree

题意:
由连续两行来表示一棵二叉树, 第一行为中序遍历, 第二行为后序遍历; 要求输出从根节点到叶子节点的各个路径中, 和最小的路径的叶子节点的值; 如果存在多个最小和, 则输出值最小的叶子节点.

思路:
1. 根据输入的中序遍历和后序遍历创建树:
(1). 中序:左右;
(2). 后序:左右;
(3). 由上可以看到,从后序遍历最后开始,找到“根”,然后在中序中找到“根”,其左边就是左子树,右边就是右子树.依此递归, 可建树.

2. 从左/右子树递归寻找最小路径和, 为了确保当路径和相等时, 取最小叶节点, 需要把叶子节点的值也和一起返回.

要点:
1. 使用 istringstream 读取 string 中的各个 int, 速度并不比自己解析字符串, 然后 atoi 慢太多, 如果提交时提示 Time limit exceeded, 应该是别的地方出了问题, 无需在这里优化太多.
2. vector back 函数返回最后一个值.

题目:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=104&page=show_problem&problem=489

代码:

# 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

改进: 548 - Tree(UPDATE)

参考: http://hi.baidu.com/knowledgetime/item/bc9a13f74566cee71b111fa5
这里提到给定的数据时有重复的节点, 那么你在中序序列中应找最靠后的那个节点, 因为后序遍历是 左右中, 只有这样才能正确的遍历这棵树. 

转载于:https://my.oschina.net/zenglingfan/blog/151814

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值