栈技巧之Note001-前缀和中缀及后缀表达式
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈技巧之Note001-前缀和中缀及后缀表达式相关的知识,希望对你有一定的参考价值。
前缀表达式、中缀表达式、后缀表达式的转换
前缀、中缀和后缀表达式概念
前缀、中缀和后缀表达式是对表达式的不同记法,其区别在于运算符相对于操作数的位置不同,其中前缀表达式的运算符位于操作数的前面,后缀表达式的运算符位于操作数的后面。例如:
- 中缀表达式:
1+(2+3)*4-5
。 - 前缀表达式:
-+1*+2345
。 - 后缀表达式:
123+4*+5-
。
我们常用的就是中缀表达式,对于人很容易理解,但是对于计算机来说中缀表达式很难理解,因此在计算表达式的值时,需要先将中缀表达式转换成前缀或后缀表达式,然后再进行求值,对于计算机来说,计算前缀或后缀表达式的值非常简单。总结:
- 前缀、中缀、后缀是根据运算符与操作数的相对位置来划分的。
- 中缀表达式符合人的计算习惯,而前缀和后缀表达式适合计算机计算。
- 前缀表达式和后缀表达式计算的时候都是从一个方向扫描表达式,遇到数字压入栈,遇到运算符弹出栈顶的两个数进行运算并将结果入栈,重复直到结束。
- 前缀和后缀表达式已经内在地包含运算顺序,因此不用括号来确定优先级。
前缀表达式的计算机求值
算法思想
算法思想:从右往左扫描表达式,遇到数字时,将数字压入栈;遇到运算符时,弹出栈顶的两个数字,用运算符对它们进行相应的计算,并且将结果压入栈。重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。
示例
例如,有前缀表达式:-*+3456
。求值步骤如下:
- (1)从右往左扫描,将 6、5、4、3 压入栈中。
- (2)然后遇到
+
运算符,弹出栈顶的两个数字,即弹出 3 和 4(其中 3 为栈顶元素、4 为次顶元素),计算出3+4
的值,得到 7,然后再将 7 压入栈中。 - (3)接下来是
*
运算符,弹出栈顶的两个数字,即弹出 7 和 5,计算出7*5
的值,得到 35,将 35 压入栈中。 - (4)最后是
-
运算符,弹出栈顶的两个数字,即弹出 35 和 6,计算出35-6
的值,得到 29, 由此得出最终结果。
实现代码
求前缀表达式的核心代码如下:
/**
* 根据运算符计算两数的结果
* @param sign 运算符
* @param a 第一个数
* @param b 第二个数
* @return 两数计算的结果
*/
int evaluate(char sign, int a, int b)
int result = 0;
switch (sign)
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = a / b;
break;
case '%':
result = a % b;
break;
default:
printf("非可计算的运算符:%c", sign);
break;
return result;
/**
* 计算前缀表达式
* @param exps 前缀表达式,以 '\\0' 字符结束
* @param n 字符数组的实际字符个数
* @return 表达式的计算结果
*/
int evaluatePrefixExpression(char exps[], int n)
// 0.解题需要用到栈,所以创建顺序栈并初始化栈
SeqStack stack;
init(&stack);
// 1.从右往左扫描前缀表达式,所以要倒序遍历字符数组
for (int i = n - 1; i >= 0; i--)
// 1.1 如果当前字符是数字字符
if (exps[i] >= '0' && exps[i] <= '9')
// 1.1.1 则将该数字压入栈中,注意数字字符要转换成数字才能存入栈中,而数字字符要转换成数字可以用数字字符减去'0'字符即可得到所对应的数字
push(&stack, exps[i] - '0');
// 1.2 如果当字符不是数字字符,而是运算符
else
// 1.2.1 那么弹出栈顶两个数字,用 a 和 b 来保存
int a, b;
pop(&stack, &a);
pop(&stack, &b);
// 1.2.2 根据运算符调用函数计算 a 和 b 的结果
int result = evaluate(exps[i], a, b);
// 1.2.3 将计算结果压入栈中
push(&stack, result);
// 2.最终结果也是存在栈中的,就算栈顶元素,所以获得栈顶元素返回即可
int top;
getTop(stack, &top);
return top;
完整代码如下:
#include <stdio.h>
/**
* 顺序栈最大存储的元素个数
*/
#define MAXSIZE 100
/**
* 顺序栈结构体定义
*/
typedef struct
/**
* 数据域,数组,用来存储栈中元素
*/
int data[MAXSIZE];
/**
* 指针域,表示栈顶指针,实际上就是数组下标
*/
int top;
SeqStack;
/**
* 初始化顺序栈,即将栈顶指针指向 -1 表示空栈
* @param stack 顺序栈
*/
void init(SeqStack *stack)
// 设定让栈顶指针指向 -1 表示为栈空
stack->top = -1;
/**
* 将元素入栈
* @param stack 顺序栈
* @param ele 元素值
* @return 如果栈满则返回 0 表示入栈失败;如果插入成功则返回 1
*/
int push(SeqStack *stack, int ele)
// 1.参数校验,如果栈满则不能入栈元素
if (stack->top == MAXSIZE - 1)
// 如果栈满,则返回 0,表示不能入栈
return 0;
// 2.先将栈顶指针加一,指向新空数组位置
stack->top++;
// 3.将新元素值填充到新位置中
stack->data[stack->top] = ele;
return 1;
/**
* 将元素出栈
* @param stack 顺序栈
* @param ele 用来保存出栈的元素
* @return 如果栈空则返回 0 表示出栈失败;否则返回 1 表示出栈成功
*/
int pop(SeqStack *stack, int *ele)
// 1.参数校验,栈空不能出栈
if (stack->top == -1)
// 栈空,没有元素可出栈
return 0;
// 2.用 ele 来保存顺序栈栈顶元素
*ele = stack->data[stack->top];
// 3.然后栈顶指针减一,表示出栈一个元素
stack->top--;
return 1;
/**
* 获取栈顶元素,但不出栈
* @param stack 顺序栈
* @param ele 用来保存出栈元素
* @return 如果栈空则返回 0 表示出栈失败;否则返回 1 表示出栈成功
*/
int getTop(SeqStack stack, int *ele)
// 1.参数校验,如果栈空则不能出栈
if (stack.top == -1)
// 栈空,没有元素可出栈
return 0;
// 2.保存栈顶元素返回
*ele = stack.data[stack.top];
return 1;
/**
* 根据运算符计算两数的结果
* @param sign 运算符
* @param a 第一个数
* @param b 第二个数
* @return 两数计算的结果
*/
int evaluate(char sign, int a, int b)
int result = 0;
switch (sign)
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = a / b;
break;
case '%':
result = a % b;
break;
default:
printf("非可计算的运算符:%c", sign);
break;
return result;
/**
* 计算前缀表达式
* @param exps 前缀表达式,以 '\\0' 字符结束
* @param n 字符数组的实际字符个数
* @return 表达式的计算结果
*/
int evaluatePrefixExpression(char exps[], int n)
// 0.解题需要用到栈,所以创建顺序栈并初始化栈
SeqStack stack;
init(&stack);
// 1.从右往左扫描前缀表达式,所以要倒序遍历字符数组
for (int i = n - 1; i >= 0; i--)
// 1.1 如果当前字符是数字字符
if (exps[i] >= '0' && exps[i] <= '9')
// 1.1.1 则将该数字压入栈中,注意数字字符要转换成数字才能存入栈中,而数字字符要转换成数字可以用数字字符减去'0'字符即可得到所对应的数字
push(&stack, exps[i] - '0');
// 1.2 如果当字符不是数字字符,而是运算符
else
// 1.2.1 那么弹出栈顶两个数字,用 a 和 b 来保存
int a, b;
pop(&stack, &a);
pop(&stack, &b);
// 1.2.2 根据运算符调用函数计算 a 和 b 的结果
int result = evaluate(exps[i], a, b);
// 1.2.3 将计算结果压入栈中
push(&stack, result);
// 2.最终结果也是存在栈中的,就算栈顶元素,所以获得栈顶元素返回即可
int top;
getTop(stack, &top);
return top;
int main()
char prefixExp[] = "-*+3456";
int n = 7;
int result;
result = evaluatePrefixExpression(prefixExp, n);
printf("前缀表达式计算结果:%d", result);
后缀表达式的计算机求值
算法思想
算法思想:与前缀表达式的计算类似,只是顺序是从左向右扫描表达式。如果遇到数字,则将数字压入栈中;如果遇到运算符,则弹出栈顶的两个数字,用运算符对它们做相应的计算,并且将计算结果压入栈中。重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。
示例
例如,有后缀表达式:34+5*6-
。求值步骤如下:
- (1)从左往右扫描,将 3 和 4 压入栈中。
- (2)遇到
+
运算符,因此弹出栈顶的两个元素,分别是 3 和 4(其中 4 是栈顶元素、3 是次栈顶元素),计算3+4
的值,得到结果 7,再将 7 入栈。 - (3)遇到数字 5,然后将 5 入栈。
- (4)接下来是
*
字符,因此弹出栈顶的两个元素,分别是 5 和 7,计算5*7
的值,得到结果 35,将 35 入栈。 - (5)遇到数字 6,然后将 6 入栈。
- (6)最后是
-
运算符,计算出6-35
的值,即 -29,由此得出最终结果。
实现代码
求后缀表达式的核心代码如下:
/**
* 根据运算符计算两数的结果
* @param sign 运算符
* @param a 第一个数
* @param b 第二个数
* @return 两数计算的结果
*/
int evaluate(char sign, int a, int b)
int result = 0;
switch (sign)
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = a / b;
break;
case '%':
result = a % b;
break;
default:
printf("非可计算的运算符:%c", sign);
break;
return result;
/**
* 计算前缀表达式
* @param exps 前缀表达式,以 '\\0' 字符结束
* @param n 字符数组的实际字符个数
* @return 表达式的计算结果
*/
int evaluateSuffixExpression(char exps[], int n)
// 0.解题需要用到栈,所以创建顺序栈并初始化栈
SeqStack stack;
init(&stack);
// 1.从左往右扫描后缀表达式,所以要正序遍历字符数组
for (int i = 0; i < n; i++)
// 1.1 如果当前字符是数字字符
if (exps[i] >= '0' && exps[i] <= '9')
// 1.1.1 则将该数字压入栈中,注意数字字符要转换成数字才能存入栈中,而数字字符要转换成数字可以用数字字符减去'0'字符即可得到所对应的数字
push(&stack, exps[i] - '0');
// 1.2 如果当字符不是数字字符,而是运算符
else
// 1.2.1 那么弹出栈顶两个数字,用 a 和 b 来保存
int a, b;
pop(&stack, &a);
pop(&stack, &b);
// 1.2.2 根据运算符调用函数计算 a 和 b 的结果
int result = evaluate(exps[i], a, b);
// 1.2.3 将计算结果压入栈中
push(&stack, result);
// 2.最终结果也是存在栈中的,就算栈顶元素,所以获得栈顶元素返回即可
int top;
getTop(stack, &top);
return top;
完整代码如下:
#include <stdio.h>
/**
* 顺序栈最大存储的元素个数
*/
#define MAXSIZE 100
/**
* 顺序栈结构体定义
*/
typedef struct
/**
* 数据域,数组,用来存储栈中元素
*/
int data[MAXSIZE];
/**
* 指针域,表示栈顶指针,实际上就是数组下标
*/
int top;
SeqStack;
/**
* 初始化顺序栈,即将栈顶指针指向 -1 表示空栈
* @param stack 顺序栈
*/
void init(SeqStack *stack)
// 设定让栈顶指针指向 -1 表示为栈空
stack->top = -1;
/**
* 将元素入栈
* @param stack 顺序栈
* @param ele 元素值
* @return 如果栈满则返回 0 表示入栈失败;如果插入成功则返回 1
*/
int push(SeqStack *stack, int ele)
// 1.参数校验,如果栈满则不能入栈元素
if (stack->top == MAXSIZE - 1)
// 如果栈满,则返回 0,表示不能入栈
return 0;
// 2.先将栈顶指针加一,指向新空数组位置
stack->top++;
// 3.将新元素值填充到新位置中
stack->data[stack->top] = ele;
return 1;
/**
* 将元素出栈
* @param stack 顺序栈
* @param ele 用来保存出栈的元素
* @return 如果栈空则返回 0 表示出栈失败;否则返回 1 表示出栈成功
*/
int pop(SeqStack *stack, int *ele)
// 1.参数校验,栈空不能出栈
if (stack->top == -1)
// 栈空,没有元素可出栈
return 0;
// 2.用 ele 来保存顺序栈栈顶元素
*ele = stack->data[stack->top];
// 3.然后栈顶指针减一,表示出栈一个元素
stack->top--;
return 1;
/**
* 获取栈顶元素,但不出栈
* @param stack 顺序栈
* @param ele 用来保存出栈元素
* @return 如果栈空则返回 0 表示出栈失败;否则返回 1 表示出栈成功
*/
int getTop(SeqStack stack, int *ele)
// 1.参数校验,如果栈空则不能出栈
if (stack.top == -1)
// 栈空,没有元素可出栈
return 0;
// 2.保存栈顶元素返回
*ele = stack.data[stack.top];
return 1;
/**
* 根据运算符计算两数的结果
* @param sign 运算符
* @param a 第一个数
* @param b 第二个数
* @return 两数计算的结果
*/
int evaluate(char sign, int a, int b)
int result = 0;
switch (sign)
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = a / b;
break;
case '%':
result = a % b;
break;
default:
printf("非可计算的运算符:%c", sign);
break;
return result;
/**
* 计算前缀表达式
* @param exps 前缀表达式,以 '\\0' 字符结束
* @param n 字符数组的实际字符个数
* @return 表达式的计算结果
*/
int evaluateSuffixExpression(char exps[], int n)
// 0.解题需要用到栈,所以创建顺序栈并初始化栈
SeqStack stack;
init(&stack);
// 1.从左往右扫描后缀表达式,所以要正序遍历字符数组
for (int i = 0; i < n; i++)
// 1.1 如果当前字符是数字字符
if (exps[i] >= '0' && exps[i] <= '9')
// 1.1.1 则将该数字压入栈中,注意数字字符要转换成数字才能存入栈中,而数字字符要转换成数字可以用数字字符减去'0'字符即可得到所对应的数字
push(&stack, exps[i] - '0');
// 1.2 如果当字符不是数字字符,而是运算符
else
// 1.2.1 那么弹出栈顶两个数字,用 a 和 b 来保存
int a, b;
pop(&stack, &a);
pop(&stack, &b);
// 1.2.2 根据运算符调用函数计算 a 和 b 的结果
int result = evaluate(exps[i], a, b);
// 1.2.3 将计算结果压入栈中
push(&stack, result);
// 2.最终结果也是存在栈中的,就算栈顶元素,所以获得栈顶元素返回即可
int top;
getTop(stack, &top);
return top;
int main()
char prefixExp[] = "34+5*6-";
int n = 7;
int result;
result = evaluateSuffixExpression(prefixExp, n);
printf("后缀表达式计算结果:%d", result);
中缀表达式转后缀表达式
算法思想
算法思想如下:
- 设置一个运算符栈,从左到右扫描中缀表达式中的每个字符:
- 如果遇到操作数字符(如
1
、2
等数字),则直接输出操作数到后缀表达式中(有些算法中是用一个栈来存储这些操作数,也是可行的)。 - 如果遇到非操作数字符,进行如下判断:
- 如果遇到左括号(如
(
),则直接将左括号入栈。 - 如果遇到右括号(如
)
),则执行出栈操作,并将出栈的元素输出到后缀表达式中,直到弹出栈的是左括号为止,注意左括号不输出到后缀表达式中。 - 如果遇到其他字符(如
+
、-
、*
、/
等运算符),也需要判断操作:- 如果运算符栈为空,则将该运算符直接入栈。
- 如果运算符栈不为空:
- 如果栈顶元素是左括号,则直接入栈。
- 如果栈顶元素是运算符,则需要进行比较:
- 如果当前扫描到的运算符优先级大于栈顶运算符的优先级,则将当前运算符入栈。
- 如果当前扫描到的运算符优先级小于等于栈顶运算符,则将栈顶运算符弹出并且输出到后缀表达式中,然后比较新的栈顶运算符,直到当前扫描到的运算符优先级大于栈顶运算符或栈为空为止。再将当前扫描到的运算符入栈。
- 如果遇到左括号(如
- 如果遇到操作数字符(如
- 当从左到右扫描完整个中缀表达式后,检测运算符栈,如果非空,则依次弹栈,将弹出的运算符依次压入到后缀表达式中,最终得到中缀表达式所对应的后缀表达式。
示例
- 如
1+2*3+(4*5+6)*7
转换后的后缀表达式是123*+45*6+7*+
。 - 如
(1+2+3*4)/5
转换后的后缀表达式是12+34*+5/
。 - 如
1+2
转换后的后缀表达式是12+
。 - 如
(1+2)*3
转换后的后缀表达式是12+3*
。 - 如
1+2*3
转换后的后缀表达式是123*+
。 - 如
(6+3*(7-4))-8/2
转换后的后缀表达式是6374-*+82/-
。
例如,有中缀表达式:a+b*c+(d*e+f)*g
。转换步骤如下:
- (1)首先读到操作数 a,直接输出到后缀表达式。
- (2)接着读到运算符
+
,将其入栈。 - (3)继续读到操作数 b,直接输出到后缀表达式。此时运算符栈和后缀表达式的情况如下:
- (4)继续读到运算符
*
,因为栈顶元素+
的优先级比*
低,所以将*
直接入栈。 - (5)继续读到操作数
c
,直接输出到后缀表达式。此运算符栈和后缀表达式的情况如下:
- (6)继续读到运算符
+
,因为栈顶元素*
的优先级比+
高,所以弹出*
并输出到后缀表达式;同理,弹出后现栈顶元素+
的优先级与读到的运算符+
一样,所以也弹出+
并输出到后缀表达式。然后再将读到的+
压入栈中。此运算符栈和后缀表达式的情况如下:
- (7)继续读到的字符是
(
,是左括号,优先级最高ÿ以上是关于栈技巧之Note001-前缀和中缀及后缀表达式的主要内容,如果未能解决你的问题,请参考以下文章