Files
C_code/exercise/34.c
T

133 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 逆波兰式逐步规约打印
// 输入:仅含 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;
}