栈的数据结构就不多说了,后进先出,只有一个出口。最典型的应用就是中缀表达式转后缀表达式,因为计算机不能识别人类所认知的中缀表达式,逆波兰式多用于编译器实现、自然语言处理这些常用的领域。下面介绍中缀转后缀的具体过程。
比如1+3*(2+5)转化成后缀表达式是:1325+*+,转化算法为:
1、对于数字,直接输出
2、对于符号:
左括号:进栈,不管栈中是否有元素
运算符:若此时栈为空,直接进栈。
若栈中有元素,则与栈顶符号运算符进行优先级比较:若新符号优先级高,则新符号进栈(默认左括号优先级最低)
若新符号优先级低,将栈顶符号弹出并输出,之后使新符号进栈。
右括号:不断将栈顶符号弹出并输出,直到匹配到左括号为止,再接着读取下一个符号,需注意,左右括号匹配即可,不需
要将其输出。
3、遍历结束时,将栈中所有符号弹出并输出。
直接上代码。
linkstack.h头文件,其实就是栈的操作和节点类型声明
#pragma once
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_
typedef struct Node * pNode;
typedef struct Stack * LinkStack;
struct Node
{
char data;
pNode next;
};
struct Stack
{
pNode top;
int size;
};
LinkStack Create();//创建栈
int IsEmpty(LinkStack lstack);//判断栈的大小是否为空
int getSize(LinkStack lstack);//获取栈的大小
int Push(LinkStack lstack, char val);//元素入栈
pNode getTop(LinkStack lstack);//获取栈顶元素
pNode Pop(LinkStack lstack);//弹出栈顶元素
void Destory(LinkStack lstack);//销毁栈
#endif // !_LINKSTACK_H_
linkstack.cpp文件是栈的所有操作,创建、栈是否为空、压栈出栈等
#include<stdio.h>
#include<stdlib.h>
#include"linkstack.h"
LinkStack Create()
{
LinkStack lstack = (LinkStack)malloc(sizeof(struct Stack));
if (lstack != NULL)
{
lstack->top = NULL;
lstack->size = 0;
}
return lstack;
}
int IsEmpty(LinkStack lstack)
{
if (lstack->top == NULL || lstack->size == 0)
return 1;
return 0;
}
int getSize(LinkStack lstack)
{
return lstack->size;
}
int Push(LinkStack lstack, char val)
{
pNode node = (pNode)malloc(sizeof(struct Node));
if (node != NULL)
{
node->data = val;
node->next = getTop(lstack);
lstack->top = node;
lstack->size++;
}
return 1;
}
pNode getTop(LinkStack lstack)
{
if (lstack->size != 0)
{
return lstack->top;
}
return NULL;
}
pNode Pop(LinkStack lstack)
{
if (IsEmpty(lstack))
return NULL;
pNode node = lstack->top;
lstack->top = lstack->top->next;
lstack->size--;
return node;
}
void Destory(LinkStack lstack)
{
if (IsEmpty(lstack))
{
free(lstack);
printf("栈已空,不必销毁!\n");
return;
}
do
{
pNode pTmp;
pTmp = Pop(lstack);
free(pTmp);
} while (lstack->size > 0);
printf("栈销毁成功!\n");
}
main.cpp逆波兰式转换实现的代码段
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"linkstack.h"
/*
中缀转后缀,后缀表达式也叫逆波兰式,不需要括号,但是也能按照有括号的中缀表达式的正确优先级执行
栈在中缀转后缀过程中起到了关键作用。
1、对于数字,直接输出
2、对于符号:
左括号:进栈,不管栈中是否有元素
运算符:若此时栈为空,直接进栈。
若栈中有元素,则与栈顶符号运算符进行优先级比较:若新符号优先级高,则新符号进栈(默认左括号优先级最低)
若新符号优先级低,将栈顶符号弹出并输出,之后使新符号进栈。
右括号:不断将栈顶符号弹出并输出,直到匹配到左括号为止,再接着读取下一个符号,需注意,左右括号匹配即可,不需
要将其输出。
3、遍历结束时,将栈中所有符号弹出并输出。
*/
char buffer[256] = { 0 };
void Put(char ch)
{
static int index = 0;
buffer[index++] = ch;//把字符存进来,然后索引index后移
}
//优先级比较函数
int Priority(char ch)
{
int ret = 0;
switch (ch)
{
case '+':
case '-':
ret = 1;
break;
case '*':
case '/':
ret = 2;
break;
default:
break;
}
return ret;
}
//判断字符是否是数字
int isNumber(char ch)
{
return (ch >= '0' && ch <= '9');
}
//判断字符是否是运算符
int isOperator(char ch)
{
return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}
//判断字符是否是左括号
int isLeft(char ch)
{
return (ch == '(');
}
//判断字符是否是右括号
int isRight(char ch)
{
return (ch == ')');
}
//中缀转后缀
int Transform(const char *str)
{
//先创建一个栈
LinkStack lstack = Create();//创建栈
//创建完栈后就开始遍历字符串,如果是数字就输出,运算符入栈……
int i = 0;
while (str[i] != '\0')
{
//判断是否是数字
if (isNumber(str[i]))
{
Put(str[i]);//存到buffer中
}
//判断是否是运算符
else if (isOperator(str[i]))
{
//如果是运算符,则先判断栈是否为空
if (!IsEmpty(lstack)) //如果栈不为空
{
//比较此运算符与栈顶符号的优先级
while (!IsEmpty(lstack) && Priority(*((char *)getTop(lstack))) >= Priority(str[i]))
{
//如果栈顶符号优先级高,就将栈顶符号弹出并输出
//直到栈顶符号的优先级小于此符号
//或者栈空
Put(Pop(lstack)->data);//将栈顶元素弹出,并输出到buffer中
}
}
Push(lstack, str[i]);//如果栈为空,或者所有比当前符号优先级高的都已经弹出,则将新符号入栈
}
else if(isLeft(str[i]))//如果是左括号直接入栈
{
Push(lstack, str[i]);
}
else if (isRight(str[i]))//如果是右括号
{
//判断栈顶是不是左括号,如果不是,就弹出,直到匹配到左括号
while (!isLeft(*((char *)getTop(lstack))))
{
//弹出栈顶元素存入到buffer中
Put(Pop(lstack)->data);
if (IsEmpty(lstack))
{
printf("没有匹配到左括号!\n");//如果直到栈空都没有发现左括号
return -1;
}
}
Pop(lstack);//直到循环结束,就匹配到了左括号,左括号出栈,但是不保存
}
else
{
printf("有不能识别的字符!\n");
return -1;
}
i++;
}
//遍历结束
if (str[i] == '\0')
{
//遍历结束后将栈中所有元素弹出
while (!IsEmpty(lstack))
{
if (getTop(lstack)->data == '(') //如果栈中还有左括号,证明缺少括号
{
printf("有没匹配的“(”,缺少“)”!\n");
return -1;
}
Put(Pop(lstack)->data);
}
}
else
{
printf("遍历没有完成!\n");
}
return 1;
}
int main()
{
char str[1024] = { 0 };
printf("请输入四则表达式:\n");
scanf("%s", str);
if (Transform(str) == -1)
printf("遍历中出现错误,无法完成转换!\n");
else
printf("转化后的表达式是:%s\n", buffer);
system("pause");
return 0;
}
以上就是逆波兰式生成的代码。至于逆波兰式如何计算,稍后有时间再写吧。