133 lines
4.2 KiB
C
133 lines
4.2 KiB
C
// 逆波兰式逐步规约打印
|
||
// 输入:仅含 0-9、+ - * / 和小括号 () 的中缀表达式,数字均为一位
|
||
// 要求:
|
||
// - 先将中缀转为后缀(RPN)
|
||
// - 按从左到右每次规约一个“两个数字 + 一个运算符”的三元组,得到新的后缀表达式
|
||
// - 逐行输出:首行是完整后缀表达式;之后每行比上一行少 1 个运算符和 1 个数字;最后一行只有一个数字(最终结果)
|
||
// - 令 / 为整除(向零截断),允许中间结果为负数
|
||
// - 输出格式:各符号之间以单个空格分隔;只有首行和最后一行行末保留一个空格,其他行末无空格
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
|
||
typedef struct {
|
||
int is_num; // 1 表示数字,0 表示运算符
|
||
int val; // 数字值
|
||
char op; // 运算符字符
|
||
} Token;
|
||
|
||
// 运算符优先级:* / 高于 + -,全部左结合
|
||
static inline int prec(char c) {
|
||
if (c == '*' || c == '/') return 2;
|
||
if (c == '+' || c == '-') return 1;
|
||
return 0;
|
||
}
|
||
|
||
// 打印一行后缀表达式:tokens[0..n-1]
|
||
// 参数 trailing_space 控制是否在行末额外打印一个空格
|
||
static void print_tokens(const Token *t, int n, int trailing_space) {
|
||
for (int i = 0; i < n; ++i) {
|
||
if (t[i].is_num) {
|
||
printf("%d", t[i].val);
|
||
} else {
|
||
printf("%c", t[i].op);
|
||
}
|
||
if (i + 1 < n) putchar(' ');
|
||
}
|
||
if (trailing_space) putchar(' ');
|
||
putchar('\n');
|
||
}
|
||
|
||
// 计算 a (op) b,/ 为整除(向零),其余为常规整数算术
|
||
static inline int apply_int(int a, int b, char op) {
|
||
switch (op) {
|
||
case '+': return a + b;
|
||
case '-': return a - b;
|
||
case '*': return a * b;
|
||
case '/': return (b == 0 ? 0 : a / b); // 题面保证不会越界;防御除零
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
int main(void) {
|
||
// 读取整行
|
||
char line[256];
|
||
if (!fgets(line, sizeof(line), stdin)) return 0;
|
||
size_t len = strlen(line);
|
||
while (len && (line[len-1] == '\n' || line[len-1] == '\r')) line[--len] = '\0';
|
||
|
||
// 中缀转后缀(shunting-yard)
|
||
Token out[256];
|
||
int on = 0;
|
||
char ops[256];
|
||
int ot = 0;
|
||
|
||
for (size_t i = 0; i < len; ++i) {
|
||
char c = line[i];
|
||
if (isspace((unsigned char)c)) continue;
|
||
if (c >= '0' && c <= '9') {
|
||
out[on].is_num = 1;
|
||
out[on].val = c - '0';
|
||
out[on].op = 0;
|
||
++on;
|
||
} else if (c == '+' || c == '-' || c == '*' || c == '/') {
|
||
int p = prec(c);
|
||
while (ot > 0 && ops[ot - 1] != '(' && prec(ops[ot - 1]) >= p) {
|
||
out[on].is_num = 0;
|
||
out[on].op = ops[--ot];
|
||
++on;
|
||
}
|
||
ops[ot++] = c;
|
||
} else if (c == '(') {
|
||
ops[ot++] = c;
|
||
} else if (c == ')') {
|
||
while (ot > 0 && ops[ot - 1] != '(') {
|
||
out[on].is_num = 0;
|
||
out[on].op = ops[--ot];
|
||
++on;
|
||
}
|
||
if (ot > 0 && ops[ot - 1] == '(') --ot; // 弹出左括号
|
||
} else {
|
||
// 题面保证无需判错
|
||
}
|
||
}
|
||
while (ot > 0) {
|
||
out[on].is_num = 0;
|
||
out[on].op = ops[--ot];
|
||
++on;
|
||
}
|
||
|
||
// 打印首行(保留行末空格)
|
||
print_tokens(out, on, 1);
|
||
|
||
// 逐步规约:每次找到第一个运算符(其前必有两个数字),规约为一个数字
|
||
Token cur[256];
|
||
memcpy(cur, out, on * sizeof(Token));
|
||
int n = on;
|
||
|
||
while (n > 1) {
|
||
int idx = -1;
|
||
for (int i = 0; i < n; ++i) {
|
||
if (!cur[i].is_num) { idx = i; break; }
|
||
}
|
||
// 规约 cur[idx-2], cur[idx-1], cur[idx]
|
||
int a = cur[idx - 2].val;
|
||
int b = cur[idx - 1].val;
|
||
int r = apply_int(a, b, cur[idx].op);
|
||
// 将结果写回到 idx-2,并左移覆盖 idx-1 与 idx
|
||
cur[idx - 2].is_num = 1;
|
||
cur[idx - 2].val = r;
|
||
// 左移 n-(idx+1) 个元素
|
||
for (int j = idx - 1; j + 2 < n; ++j) {
|
||
cur[j] = cur[j + 2];
|
||
}
|
||
n -= 2;
|
||
|
||
// 是否最后一行:n==1 时保留行末空格,否则不保留
|
||
print_tokens(cur, n, (n == 1));
|
||
}
|
||
|
||
return 0;
|
||
} |