用c语言做一个栈,完成逆波兰表达式

先用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());
 
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值