合計制限時間:1000ms
メモリ制限:65535kB
説明
ご存知のように、どの式も式ツリーで表すことができます。たとえば、式a + b * cは、次の式ツリーとして表すことができます。
+
/ \
a *
/ \
bc
ここで、中置式を指定します。この中置式は、変数(数値なし)で表されます。この中置式は、式の二分木の形式で出力してください。
入る
入力は3つの部分に分かれています。
最初の部分は行、つまり中置式です(長さは50以下です)。中置式には、変数(az)を表す小文字を含めることができます。また、数字やスペースを含まない演算子(+、-、*、/、括弧)を含めることもできます。
2番目の部分は整数n(n <10)で、中置式の変数の数を表します。
3番目の部分にはn行あり、各行の形式はC x、Cは変数の文字、xは変数の値です。
出力
出力は3つの部分に分割されます。最初の部分は、式の逆ポーランド式式です。これは、式ツリーの後根をトラバースした結果です。1行を占めます。
2番目の部分は、サンプル出力に示されている式ツリーの表示です。二分木が完全な二分木である場合、一番下のリーフノードはそれぞれ横座標の1、3、5、7 ...の位置を占め(左端の座標は1)、次にそれらの親ノード横座標は中央にあります2つの子ノードの。二分木がいっぱいでない場合は、ノードがない場所にスペースを入力します(ただし、行末のスペースはすべて省略してください)。親ノードと子ノードの各行は1行で区切られ、スラッシュ(/)と円記号(\)はツリーの関係を示すために使用されます。/の横座標の位置は、親ノードの横座標の左側の1つの正方形であり、表示される横座標の位置は、親ノードの横座標の右側の1つの正方形です。つまり、ツリーの高さがmの場合、出力は2m-1行になります。
3番目の部分は整数であり、値を変数に代入した後の中置式の値を表します。注意すべき点の1つは、除算は算術演算を表すことです。つまり、小数点以下の部分は破棄されます。同時に、テストデータはゼロによる除算がないことを保証します。
サンプル入力
a+b*c
3
a 2
b 7
c 5
サンプル出力
abc*+
+
/ \
a *
/ \
b c
37
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <map>
#include <vector>
using namespace std;
map<char,int> priority = {
{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'(', 0}};
map<char,int> value;
struct node_t {
char val;
node_t *left;
node_t *right;
node_t(char val_, node_t *left_ = NULL, node_t *right_ = NULL) : val(val_), left(left_), right(right_) {}
};
stack<char> op;
stack<node_t*> expression;
inline void doit()
{
node_t *b = expression.top(); expression.pop();
node_t *a = expression.top(); expression.pop();
node_t *c = new node_t(op.top(), a, b); op.pop();
expression.push(c);
}
node_t *build_tree(char *in) {
int l = strlen(in);
for (int i = 0; i < l; ++i) {
if (isalpha(in[i])) {
expression.push(new node_t(in[i]));
}
else {
if (in[i] == '(')
op.push(in[i]);
else if (in[i] == ')') {
while (op.top() != '(')
doit();
op.pop(); // for '('
}
else {
while (!op.empty() && priority[op.top()] >= priority[in[i]])
doit();
op.push(in[i]);
}
}
}
while (!op.empty()) {
doit();
}
return expression.top();
}
void postfix_dfs(node_t *cur){
if (cur->left)
postfix_dfs(cur->left);
if (cur->right)
postfix_dfs(cur->right);
putchar(cur->val);
}
int cal(int a, int b, char o)
{
if (o == '+') return a+b;
if (o == '-') return a-b;
if (o == '*') return a*b;
if (o == '/') return a/b;
cerr << "bad o" << endl;
return 0;
}
int cal_tree(node_t* cur){
if (isalpha(cur->val))
return value[cur->val];
return cal(cal_tree(cur->left), cal_tree(cur->right), cur->val);
}
int tree_height(node_t *cur)
{
if (isalpha(cur->val))
return 1;
return 1+max(tree_height(cur->left), tree_height(cur->right));
}
void print_tree(node_t *rt)
{
struct Node{
node_t *node;
int pos;
};
int tree_h = tree_height(rt);
vector<Node> *v1 = new vector<Node>(), *v2 = new vector<Node>();
v1->push_back({rt, (1<<(tree_h-1))-1});
for (int h = tree_h; h >= 1; --h) {
int last_pos = 0;
int bias = 1<<(h-2);
/* print node value */
for (Node nd : *v1) {
for (int i = last_pos; i < nd.pos; ++i)
putchar(' ');
putchar(nd.node->val);
last_pos = nd.pos+1;
if (h > 1 && !isalpha(nd.node->val)) {
v2->push_back({nd.node->left, nd.pos - bias});
v2->push_back({nd.node->right, nd.pos + bias});
}
}
putchar('\n');
if (h == 1)
break;
/* print branch */
last_pos = 0;
for (Node nd : *v1) {
if (isalpha(nd.node->val))
continue;
for (int i = last_pos; i < nd.pos-1; ++i)
putchar(' ');
printf("/ \\");
last_pos = nd.pos + 2;
}
putchar('\n');
/* get ready for next loop */
v1->clear();
swap(v1, v2);
}
}
int main()
{
char in[55];
cin >> in;
node_t *rt = build_tree(in);
postfix_dfs(rt);
printf("\n");
print_tree(rt);
int n; cin >> n;
for (int i = 0; i < n; ++i) {
char c; int v;
cin >> c >> v;
value[c] = v;
}
cout << cal_tree(rt) << endl;
system("pause");
return 0;
}
【分析】
- 「中置式を直接見つける」という考えを借りて、中置式を使用して式ツリーを直接構築することもできます
- 違いは、中置式を直接求める場合、スタックの一番上にある2つの数値の演算結果をスタックに戻すことです。今回は、スタックの一番上の2つのノードによって形成された新しいノードをスタックに戻します。スタック。
- データサイズが大きすぎるため、配列を開くことができず、再帰印刷にdfsを使用できません。代わりに、bfs印刷が使用されます。
- last_posマークが付いた中央のスペースを印刷するのに適した方法です。
- 階層型bfsメソッド:キューはキュー構造ではなくベクトル構造を使用し、深度情報はノード情報に格納されません。2つのベクトルポインターを使用すると、スワップはサイクルの最後に書き込むための便利な方法です。