逆波蘭表示法(後綴表達式)消除了括號與優先級歧義,極適合計算機求值。本文實現一個中綴轉後綴並求值的完整程序,支持 `+ - * /`、括號 `()`、階乘 `!`,採用雙棧法(運算符棧 + 操作數棧)單遍掃描完成。
#define OPSTACK_LENGTH 50
#define NUMSTACK_LENGTH 100
#define MAX_STRING_LENGTH 100
typedef struct {
char name; // 運算符字符
int priority; // 優先級
int opnum; // 目數:1=一元,2=二元
} OPERATOR;
OPERATOR opStack[OPSTACK_LENGTH];
int opStackTop = -1;
double numStack[NUMSTACK_LENGTH];
int numStackTop = -1;
// 運算符優先級
int getPriority(char c) {
if (c == '(' || c == ')') return 0;
if (c == '!') return 3;
if (c == '*' || c == '/') return 2;
if (c == '+' || c == '-') return 1;
exit(1);
}
// 運算符目數
int getOpNum(char c) {
if (c == '+' || c == '-' || c == '*' || c == '/') return 2;
if (c == '!') return 1;
if (c == '(' || c == ')') return 0;
exit(1);
}
// 從字符串解析數字(支持小數),更新索引 i
double getNumFromString(char *s, int *i) {
char buf[MAX_STRING_LENGTH];
int j = 0;
while (*s >= '0' && *s <= '9') buf[j++] = *s++;
if (*s == '.') {
buf[j++] = *s++;
while (*s >= '0' && *s <= '9') buf[j++] = *s++;
}
*i += j;
buf[j] = '\0';
return atof(buf);
}
void pushOperator(OPERATOR op) { opStack[++opStackTop] = op; }
OPERATOR popOperator() { return opStack[opStackTop--]; }
void pushNumber(double v) { numStack[++numStackTop] = v; }
double popNumber() { return numStack[numStackTop--]; }
double operate2(OPERATOR op) {
double b = popNumber(), a = popNumber();
switch (op.name) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b; // 原代碼有誤 a+b,此處修正
}
exit(1);
}
double operate1(OPERATOR op) {
double a = popNumber();
if (op.name == '!') {
double r = 1;
while (a > 1) r *= a--;
return r;
}
exit(1);
}
double operate(OPERATOR op) {
return (op.opnum == 1) ? operate1(op) : operate2(op);
}
int main() {
char str[MAX_STRING_LENGTH];
scanf("%s", str); // 輸入如 "3+4*2/(1-5)!" 以 '=' 結束或直接以 '\0' 結束
// 初始化運算符棧放入哨兵 '#'
OPERATOR sentinel = {'#', 0, 0};
pushOperator(sentinel);
for (int i = 0; str[i] && str[i] != '='; ) {
if (str[i] >= '0' && str[i] <= '9') {
pushNumber(getNumFromString(&str[i], &i));
} else {
OPERATOR cur = {str[i], getPriority(str[i]), getOpNum(str[i])};
OPERATOR top = popOperator();
if (cur.name == '(') { // 左括號直接入棧
pushOperator(top);
pushOperator(cur);
} else if (cur.name == ')') { // 右括號:彈出運算直到遇到 '('
while (top.name != '(') {
pushNumber(operate(top));
top = popOperator();
}
// '(' 已彈出並丟棄
} else { // 普通運算符
if (top.name != '#' && cur.priority <= top.priority) {
pushNumber(operate(top)); // 棧頂優先級 >= 當前,先算棧頂
} else {
pushOperator(top); // 否則棧頂壓回
}
pushOperator(cur); // 當前入棧
}
i++;
}
}
// 清空剩餘運算符
OPERATOR top;
while ((top = popOperator()).name != '#')
pushNumber(operate(top));
printf("%f\n", popNumber());
return 0;
}
| 輸入表達式 | 輸出結果 | 說明 |
|---|---|---|
| 3+4*2/(1-5)! | 3.5 | 階乘優先級最高,(1-5)=-4, !=24, 4*2/24=0.333, 3+0.333=3.333... |
| 2*3+4 | 10 | 乘法優先 |
| (2+3)*4 | 20 | 括號改變優先級 |
| 5! | 120 | 階乘 |