逆波兰式
即我们平时所说的后缀表达式(将运算符写在操作数之后)
实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中序表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
举个例子:
1+2
就是一个中缀式,转换成后缀式为1 2 +
1+2*3
…… ……1 2 3 * +
1+(2+5)
…… ……1 2 5 + +
在计算这些表达式的值时,通过将数字入栈,遇到运算符后在把数字出栈,计算,计算完再次入栈。一直循环……
比如1 2 3 * +
通过以上实例,我们可以看到,计算机利用栈的性质计算后缀式的结果与我们人类用中缀式计算的结果并无差别。但是,我们不要忘了计算机的计算速度可是很快的,在进行一些比较复杂或是计算量较大的运算时,计算机可以迅速的得出答案,这样让计算机帮助我们去计算可以省下大量的时间何精力。
但前面也说了,计算机的思维方式和我们人的思维方式不同,计算机是硅基生物,而我们是碳生物。硅基生物虽然计算快我们比不上,但它不像我们碳基生物那么聪明,不懂得变通。因此,想让计算机帮我们计算,我们还需要把计算表达式转换成计算机看得懂的方式才行。
中缀转后缀
在中缀式中,有运算符的优先级以及括号的限制,因此,在计算的时候需要考虑计算先后的问题,但在后缀式中只有入栈与出栈的操作,因此,我们在中缀转后缀的时候需要提前把计算顺序考虑进去,以便于计算机可以直接运算。
比如:符号入栈
比如:符号出栈
比如:括号
代码
/* 判断优先级 --> 决定是否出入栈
** 符号优先级高于符号栈,如符号栈
** 符号优先级低于符号栈,符号栈里的符号出栈,入数字栈,继续向下比较
*/
bool IsPop(char a, char b) //a为待比较符号,b为符号栈栈顶元素
{
if (b == '(') return false;
if (a == '*' || a == '/')
{
if (b == '+' || b == '-')
{
//可以如符号栈
return false;
}
else
{
return true;
}
}
//if (a == '+' || a == '-')
//{
//a符号为最低优先级,符号栈出栈
return true;
//}
}
/* 中缀转后缀 src带入中缀式,des带出后缀式 */
void InfixToSuffix(char* src, char* des)
{
/* 符号栈 */
Stack symbol;
symbol.data = (char*)malloc(strlen(src)/2);
symbol.top = 0;
int i = 0;
int k = 0;
while (src[i] != '\0')
{
/*----------- 特殊符号判断------------------------------*/
if (src[i] == ' ') //如果当前字符是空格,则往后走一个
{
i++;
continue;
}
else if (src[i] == '(') //左括号直接入栈
{
Push(&symbol, src[i]);
}
else if (src[i] == ')') //遇到 ) ,输出( )之间的所有符号
{
while (Top(&symbol) != '(')
{
des[k++] = Top(&symbol);
des[k++] = ' ';
Pop(&symbol);
}
Pop(&symbol);
}
/*----------------- 数字 -------------------------------*/
else if (isdigit(src[i]))
{
des[k++] = src[i];
if (!isdigit(src[i + 1])) //数字后加空格
{
des[k++] = ' ';
}
}
/*----------------- 运算符 -------------------------------*/
else
{
switch (src[i])
{
case '+':case '-':case '*':case '/':
if (Empty(&symbol)) //如果符号栈为空,直接入符号
{
Push(&symbol, src[i]);
}
else //否则,判断是否选择入符号栈还是出栈顶元素
{
if (IsPop(src[i], Top(&symbol))) //出符号栈
{
des[k++] = Top(&symbol);
Pop(&symbol);
continue;
}
else //当前符号优先级高,入符号栈
{
Push(&symbol, src[i]);
}
}
break;
default:
break;
}
}
i++; /* 遍历下一字符 */
}
/*字符串已遍历完,把符号栈中剩余的符号入栈到数字栈中 */
while (!Empty(&symbol))
{
des[k++] = Top(&symbol);
des[k++] = ' ';
Pop(&symbol);
}
des[k] = '\0';
free(symbol.data);
}
当然,以上代码运行的条件是要有一个栈,栈的实现到没有多难,以下为栈的代码
Stack.h
#ifndef _STACK_H
#define _STACK_H
#define ElemType char
#define MAXSIZE 20
typedef struct _Stack
{
ElemType *data;
int top; //栈顶指针(有效元素的个数)
int size;
}Stack;
/* 初始化 */
void Init_Stack(Stack* st);
/* 判空 */
bool Empty(Stack* st);
/* 入栈 */
void Push(Stack* st, ElemType val);
/* 出栈 */
ElemType Pop(Stack* st);
/* 获取栈顶元素 */
ElemType Top(Stack* st);
#endif
Stack.cpp
#include <stdbool.h>
#include <malloc.h>
#include "Stack.h"
/* 判满 */
static bool Full(Stack* st)
{
return st->top == MAXSIZE;
}
/* 判空 */
bool Empty(Stack* st)
{
return st->top == 0;
}
/* 初始化 */
void Init_Stack(Stack* st)
{
st->data = (ElemType*)malloc(sizeof(ElemType) * 20);
st->size = MAXSIZE;
st->top = 0;
}
/* 入栈 */
void Push(Stack* st, ElemType val)
{
if (Full(st)) return;
st->data[st->top++] = val;
}
/* 出栈 */
ElemType Pop(Stack* st)
{
if (Empty(st)) return -1;
st->top--;
return st->data[st->top];
}
/* 获取栈顶元素 */
ElemType Top(Stack* st)
{
if (Empty(st)) return -1;
return st->data[st->top - 1];
}
测试
main.cpp
int main()
{
char str[50] = "";
printf("请输入表达式:");
scanf("%[^\n]", str); /* 输入字符串,以回车为结束标志*/
char strRet[50] = ""; /* 接收中缀式转换成的后缀式 */
InfixToSuffix(str, strRet); /* 中缀转后缀 */
printf("%s\n", strRet); /* 输出后缀式*/
return 0;
}