先用c语言,实现了一个栈结构,中缀表达式 转换成 后缀表达式 , 在用栈完成逆波兰表达式计算。 最后写了一些测试用例 验证.
中缀表达式是最常用的算术表达式,运算符在运算数中间,运算需要考虑运算符优先级.
后缀表达式是计算机容易运算的表达式,运算符在运算数后面,从左到右进行运算,无需考虑优先级,运算呈线性结构.
先举个简单的转换例子
2+9/3-5 (中缀)-> 2 9 3 / + 5 - (后缀)
先进行乘除再进行加减
运算规律,运算数位置不变,改变的是运算符位置
可以推栈实现,用堆栈储存等待中的运算符.
将当前运算符与最后一个等待的运算符比较.
具体转换方式:
1.从左到右进行遍历
2.运算数,直接输出.
3.左括号,直接压入堆栈,(括号是最高优先级,无需比较)(入栈后优先级降到最低,确保其他符号正常入栈)
4.右括号,(意味着括号已结束)不断弹出栈顶运算符并输出直到遇到左括号(弹出但不输出)
5.运算符,将该运算符与栈顶运算符进行比较,
如果优先级高于栈顶运算符则压入堆栈(该部分运算还不能进行),
如果优先级低于等于栈顶运算符则将栈顶运算符弹出并输出,然后比较新的栈顶运算符.
(低于弹出意味着前面部分可以运算,先输出的一定是高优先级运算符,等于弹出是因为同等优先级,从左到右运算)
直到优先级大于栈顶运算符或者栈空,再将该运算符入栈.
6.如果对象处理完毕,则按顺序弹出并输出栈中所有运算符.
#import <Foundation/Foundation.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
double data;
struct Node * next;
} Node;
typedef struct Stack{
Node * data; // 存的是栈顶的元素
int count;
} Stack;
Stack * s ;
// 生成栈
void initStack(){
s = (Stack *)malloc(sizeof(Stack));
s->count = 0;
s->data = NULL;
}
// 入栈,push
void push(double data){
printf("%f 即将被入栈\n",data);
Node * n = malloc(sizeof(Node));
n->data = data;
n->next = s->data;
s->data = n;
s->count ++;
}
// 出栈,pop
double pop(){
if(s->data == NULL){
printf("已经是空栈了\n");
return CGFLOAT_MAX;
}
Node * n = s->data;
double r = n->data;
printf("数据 %f 即将出栈\n",n->data);
s->data=n->next;
free(n);
s->count--;
return r;
}
// 清空栈
void clearStack(){
while (s->data != NULL){
pop();
}
}
// 获取栈顶元素
double topItem(){
if (s->data==NULL){
printf("此栈为空栈\n");
return CGFLOAT_MAX;
}
printf("此栈中元素数量为 %d 个,栈顶为 %f\n",s->count,s->data->data);
return s->data->data;
}
// 把字符串转成double,没有做错误检测,比如用户输入 12.35.5 5abc.64,就会出错。
double charToDouble(char * num){
double a = 0.0;
// 把一个字符串转成浮点型数字,记录小数点后的位数,忽略小数点转成整数,
// 然后用这个整数 除以小数点后的位数,即可得到浮点数,比如字符串"12.35"->先变成1235,记录有小数点,小数点有2位->在转成12.35
int havePoint = 0; //这个数中是否有小数点
int j=0; // 记录小数点后面有几位的
for(int i=0; num[i]!='\0'; i++){
if(num[i]=='.'){
havePoint = 1;
continue;
}
if(havePoint==1){
j++;
}
a = a * 10 + num[i] - '0';
}
while(j>0){
a = a/10;
j--;
}
printf("字符串%s 转成 浮点数 %f\n",num,a);
return a;
}
int main(){
initStack();
// 测试用例
// char * str = "3*4/2*3"; //18
// char * str = "3*4+2*3"; //18
// char * str = "3/9+3"; //3.33
// char * str = "5*14+2*3"; //76
// char * str = "(18+2)/10-2"; //0
// char * str = "2*(7-2)+12"; //22
// char * str = "2.1*(7-2)+12"; //22.5
// char * str = "2.1*(7+3)+12"; //33
// char * str = "2.1/(10-3)+12"; //12.3
// char * str = "9/(6-3)/2"; //1.5
// char * str = "6-3+2"; //5
char str[100] ;
printf("输入一串表达式\n");
// 键盘要换成英文输入,不然无法输入。哭...
gets(str);
// 记录逆波兰表达式的结果,
// 结构是 【0或1】 【对应值】,由于加减乘除 都是char类型,char -> int -> double,所以用double存储。
// 0 表示是数字类型, 1 表示后面的是符号类型 ,
// 40 = ( 41 = ) 42 = * 43 = + 45 = - 47 = /
double niBoLan[100]={0.0};
int niI = 0; // 逆波兰表达式的下标
char num[100] = "\0"; // 把字符串导出数字使用,
int j = 0;//记录这个数字char的长度,比如 12.3 就会记录为4
// 把中缀表达式->后缀表达式,刚开始栈中只保存符号,
//从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;
//若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于找顶符号(乘除优先加减)则栈顶元素依次出找并输出,并将当前符号进栈,
//一直到最终输出后缀表达式为止。
for(int i=0; str[i]!='\0'; ){
// 区分出是数字还是符号
if ((str[i]<='9'&&str[i]>='0') || str[i]=='.'){
// 把数字转换出来
while ((str[i]<='9'&&str[i]>='0') || str[i]=='.'){
char a = str[i];
num[j]=a;
j++;
num[j]='\0';
i++;
}
double b = charToDouble(num);
num[0] ='\0';
j=0;
niBoLan[niI] = 0;
niI ++;
niBoLan[niI] = b;
niI ++;
} else {
// 是符号,入栈,比较优先级
char a = str[i];
switch (a) {
case '+':
case '-':
while(topItem()=='*'||topItem()=='/'||topItem()=='+'||topItem()=='-'){
niBoLan[niI] = 1;
niI ++;
niBoLan[niI] = pop();
niI++;
}
push(a);
break;
case '*':
case '/':
while(topItem()=='*'||topItem()=='/'){
niBoLan[niI] = 1;
niI ++;
niBoLan[niI] = pop();
niI++;
}
push(a);
break;
case '(':
push(a);
break;
case ')':
while(topItem()!='('){
niBoLan[niI] = 1;
niI ++;
niBoLan[niI] = pop();
niI++;
}
pop(); // 需要把 ( 弹出
break;
}
i++;
}
}
// 栈中剩余的符号弹出
while (s->data != NULL){
niBoLan[niI] = 1;
niI ++;
niBoLan[niI] = pop();
niI++;
}
for(int i = 0;i<niI;i++) {
printf("%f ",niBoLan[i]);
}
printf("\n中缀表达式->逆波兰转换完毕,检查一下\n");
clearStack();
printf("\n清空栈结构,开始计算结果\n");
double num1;
double num2;
for(int i = 0;i<niI;i+=2) {
// 判断标志位, 0 是数字, 1 是符号。
int isChar = niBoLan[i];
if(isChar == 0 ) {
// 数字直接入栈
push(niBoLan[i+1]);
} else {
char a = niBoLan[i+1];
switch (a) {
case '+':
push( pop() + pop() );
break;
case '-':
num1 = pop();
num2 = pop();
push( num2 - num1 );
break;
case '*':
push( pop() * pop() );
break;
case '/':
num1 = pop();
num2 = pop();
push( num2 / num1 );
break;
}
}
}
printf("\n计算结果 %f\n",pop());
}