题意:
判断读入的 LISP S-expressions 表达式是否存在一条从根到叶子节的路径, 使这条路径上所有节点的和为 I.
1. I 与表达式 T 是成对出现的, 但是 T 有可能是跨行的.
2. 所有节点的出现形式都为 (integer()()), 如果有左/右子树, 则在 integer 的左/右括号里.
思路:
1. 由于表达式是跨行的, 所以需要先把表达式完整的读入, 使用 getline(cin, temp, ')'); 进行读入, 即读到 ) 就停止, 然后判断到这里为止是否左右括号完全匹配, 若已匹配完毕, 则说明表达式读入完毕.
2. 根据表达式建树.由题意(2)可知, 每个节点都包含一个整数及由两个括号包裹起来的左/右子树. 所以建树过程可以递归如下:
(1). 先获取读取到的第一个整数, 做为节点的值(注意有可能有负数存在)
(2). 由整数往后, 找到第一个使括号完全匹配的字符串, 作为左子树; 第二个使括号完全匹配的字符串, 作为右子树.
(3). 递归创建左子树及右子树.若括号匹配到的字符串为空, 说明左/右子树为空.
(4). 根据左/右子树创建当前节点.
3. 判断是否存路径满路和为 I, 同样使用递归:
(1). 判断当前节点是否为叶子节点, 且值与 I 相等, 若相等, 则返回 true.
(2). 若不等, 则在其左/右子树中递归查找.
(3). 只要左右子树有一边能查到, 就为 true.
要点:
1. 这里的 tree 的 Node 的成员变量都是直接 public 的, 虽然违反面向对象设计原则, 但是对于一个算法题, 还要弄一堆 get/set 就太过麻烦了;
2. memset(tmp, '\0', 10);
3. string substr() 函数不填第二个参数, 表示取到字符串最后.
题目:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=104&page=show_problem&problem=48
代码:
# include <iostream> # include <string> # include <cstdio> # include <cstring> # include <vector> # include <algorithm> # include <cctype> # include <iterator> # include <assert.h> # include <stack> using namespace std; // 所有的输入都是有效的, 但是有可能会存在于多行中, 每读入完一个 int, // 就按 ) 分隔的 getline 进行读入, 直到 ( 与 ) 完全匹配, 表示一个表达式读入完毕 class Node { public: Node(int n, Node* left, Node* right): n_(n), left_(left), right_(right){} int n_; Node* left_; Node* right_; }; // 从 pos 开始,找到第一个合法的 express 及其所在下标, 即左右括号必须匹配 pair<string, int> getValidExpression(const string& expression, int startPos) { int cntLeft = 0; int cntRight = 0; int endPos = startPos; for (int i=startPos; i<expression.size(); i++) { if (expression[i] == '(') { ++cntLeft; } else if (expression[i] == ')') { ++cntRight; if (cntLeft == cntRight) { endPos = i + 1; break; } } } return make_pair(expression.substr(startPos, endPos - startPos), endPos); } // 获取节点的数字,即找到的第一个整数,返回整数及其最后位所在的下标 // 如果找不到数字, 说明为空, 返回 <-1, -1> // 这里真正有效的是 second 中的 -1 pair<int, int> getNodeNumber(const string& expression) { char tmp[10]; memset(tmp, '\0', 10); int j = 0; int pos = 0; bool begin = false; for (int i=0; i<expression.size(); i++) { // digit 有可能是负数 if (isdigit(expression[i]) || expression[i] == '-') { tmp[j++] = expression[i]; begin = true; } else { if (begin) { pos = i; break; } } } // 如果没有开始,说明没找到数字, 真正有效的是 second 中的 -1 if (!begin) return make_pair(-1, -1); return make_pair(atoi(tmp), pos); } // 根据读入的 expression 创建树, 返回根节点 // expression 中可能包含空格与回车换行符 Node* buildTree(const string& expression) { pair<int, int> number = getNodeNumber(expression); if (number.second == -1) return NULL; // 不能拿 first 来判断,不然 -1 (-1()()) 的 first 正好是 -1 // if (number.first == -1) return NULL; pair<string, int> left = getValidExpression(expression, number.second); pair<string, int> right = getValidExpression(expression, left.second); Node* leftTree = buildTree(left.first); Node* rightTree = buildTree(right.first); return new Node(number.first, leftTree, rightTree); } // 判断 tree 是否存在一条路径的和为 sum // 往左右子树查找, 只要有一边找到, 就为真 bool isPathExist(const Node* root, int sum) { if (root == NULL) return false; if (root->left_ == NULL && root->right_ == NULL && root->n_ == sum) return true; // bool 值要先初始化 bool left = false; bool right = false; if (root->left_ != NULL) { left = isPathExist(root->left_, sum - root->n_); } if (root->right_ != NULL) { right = isPathExist(root->right_, sum - root->n_); } return (left || right); } // 判断一个表达式是否为合法, 即其左右括号必须匹配 bool isValid(const string& expression) { if (expression.empty()) return false; int cntLeft = 0; int cntRight = 0; for (int i=0; i<expression.size(); i++) { if (expression[i] == '(') { ++cntLeft; } else if (expression[i] == ')') { ++cntRight; } } return (cntLeft == cntRight); } // 释放树空间 void freeTree(Node* root) { if (root == NULL) return; freeTree(root->left_); freeTree(root->right_); delete root; } int main(int argc, char const *argv[]) { #ifndef ONLINE_JUDGE freopen("112_i.txt", "r", stdin); freopen("uva_o.txt", "w", stdout); #endif int sum; while (!cin.eof()) { cin >> sum; string expression; // 存放一个合法的表达式 while (!cin.eof()) { string temp; getline(cin, temp, ')'); expression = expression + temp + ")"; if (isValid(expression)) break; } if (isValid(expression)) { Node* root = buildTree(expression); if (isPathExist(root, sum)) { cout << "yes" << endl; } else { cout << "no" << endl; } freeTree(root); } } return 0; }
环境: C++ 4.5.3 - GNU C++ Compiler with options: -lm -lcrypt -O2 -pipe -DONLINE_JUDGE
参考: 比较简明的解法, 不用建树 http://blog.sina.com.cn/s/blog_64018c250100qjrt.html