今天是清明节放假的第一天,博主也没闲着(拒绝偷懒),早上起来看了数据结构。今天就趁热打铁来讲讲《栈的应用之C语言实现逆波兰表达式》吧。
波兰表达式存在的意义就是使运算式中即使没有括号,也可以执行正确的运算顺序。例如a+b的波兰表达式可以写作+ab,逆波兰表达式则是ab+。
实现逻辑:
1.首先我们拿到一个存放运算式的字符串。创建一个符号栈,用来放-+x/().
2.对字符串逐一扫描。如果是数字我们直接输出。
3.如果是运算符,而且此时符号栈空着的话就直接放进去。如果不是,我们就比较栈顶符号与扫描符号的优先级,±最低,x/中间,()最高。
3.1.扫描符号高于栈顶符号,那就直接入栈,注意是高,等于不行。
3.2扫描符号不高于栈顶符号,输出栈顶符号,然后重复比较栈顶元素。也就是重复第3点。
4.如果扫到(,在定义时我们给了(最高优先级,所以(可以直接入栈,不用特殊操作,但是接下来扫描到的符号优先级肯定比(低,大家想想是不是。但是不能把(由3.2步骤弹出去!(弹出去的唯一条件就是扫描到了)。所以这里就需要在3.2的出栈条件再增加一条:栈顶不是(即可。
5.一直到扫描完这个字符串,然后将符号栈中剩下元素一直出栈输出就行。
这主要是利用栈的先进后出的灵活性。用C语言实现代码(编译环境:vs2019):
#include<stdio.h>
#include<string.h>//使用字符串必备头文件
#include<stdlib.h>//使用malloc必备的头文件
int operatorvalue[6] = { 40,41,42,43,45,47 }; //这是*-+/()的ascll码,用来判别运算式中扫描的字符是否是符号的
typedef struct stack {
char data;
struct stack* next;
}stack, * liststack;
void initstack(liststack& s)
{
s = (liststack)malloc(sizeof(liststack));
s->next = NULL;
printf("init finished.\n");
}
void push(liststack& s, char c) { //入栈
stack* node;
node = (liststack)malloc(sizeof(stack));
node->data = c;
node->next = s->next;
s->next = node;
}
char pop(liststack& s) { //出栈
stack* top;
top = s->next;
char t = top->data;
s->next = top->next;
return t;
}
int priority(char c) { //return的数字就是运算优先级,这样才能比较
if (c == '+' || c == '-')
{
return 1;
}
else if (c == '*' || c == '/')
{
return 2;
}
else
{
return 3;
}
}
char gettop(liststack s) { //获得栈顶元素
if (s->next != NULL) return s->next->data;
}
int numORoperator(char c) { //判断字符是数字还是运算符
for (int i = 0; i < 6; i++)
{
if (operatorvalue[i] == int(c)) //operatorvalue[i]是开篇就定义的运算符ascll码数组
return 1; //运算符
}
return 0; //数字
}
void nibolan(liststack& lsop, char arr[]) { //通过此函数求得逆波兰表达式
for (int i = 0; i < strlen(arr); i++) //遍历字符串中所有字符
{
if (numORoperator(arr[i])) //如果是运算符执行if里面语句
{
if (lsop->next == NULL) //如果符号栈空,直接入栈
{
push(lsop, arr[i]);
}
else if (arr[i] == ')') //如果符号栈不空并且扫描到了‘)’,我们的逆波兰表达式不需要),所以不要入栈,(入栈都只是因为要它当作一个界限。
{
while (gettop(lsop) != '(') //如果栈顶不是(,那么就一直出栈
{
char c = pop(lsop);
printf("%c", c);
}pop(lsop); //注意这里再出栈的目的是让(出栈,我们的逆波兰表达式不需要()。上面循环结束条件就是栈顶为(。
}
else //如果符号栈不空并且扫描到的不是),就执行这里面的语句
{
int toppriority = priority(gettop(lsop)); //获得栈顶元素优先级
int arripriority = priority(arr[i]); //获得当前扫描元素的优先级
if (arripriority > toppriority || gettop(lsop) == '(') //如果扫描元素优先级高于栈顶优先级,或者栈顶元素是(,则扫描元素入栈
{
push(lsop, arr[i]);
}
else //如果扫描元素优先级不高于栈顶优先级,那就要栈顶出栈了
{
while (arripriority <= toppriority && gettop(lsop) != '(') //这里是重复判断栈顶与扫描符的优先级
{
if (lsop->next == NULL) break;
char c = pop(lsop);
printf("%c", c);
toppriority = priority(gettop(lsop));
}push(lsop, arr[i]); //高于或等于扫描符的符号都出栈完了。让扫描符入栈
}
}
}
else //如果是数字,直接输出,这是与最上面的if一起的
{
printf("%c", arr[i]);
}
}
while (lsop->next) //将符号栈中剩余元素出栈
{
printf("%c", pop(lsop));
}
}
int main() {
liststack lsop;
initstack(lsop);
int temp = 0;
char arr[255];
printf("请输入中缀表达式:");
scanf_s("%s", arr,255);//注意这里scanf_s的第三个参数表示输入字符串的最大长度,都是博主 踩过的坑
printf("逆波兰表达式为:\n");
nibolan(lsop, arr);
return 0;
}
程序执行结果:
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.