• 数据结构应用实例#栈&单链表#简易计算器


    修改BUG的时候一不小心BUG越修越多,鉴于维护程序并不是学习数据结构的初衷,我已经果断的弃坑了!!

    以下内容再不更新,Github上的代码直接无法正常编译运行。。。。

    参考参考就好,学习到栈的作用就好。。。

    ===========================================================

    使用数据结构中的栈以及链表来制作一个能够处理中缀表达式的计算程序。

    在该程序中输入的数字可以是任意正数、小数。

    负数似乎也可以正常运行,即使我没有特意添加检测负数的代码。。。。。

    输入的运算符可以是 + - * / ( )

    但请确保输入的表达式合法。

    中缀表达式:

    形如

    9+(3-1)*3+10/2

    的数学表达式。特点是运算符在被运算的两个数字中间,也就是我们日常接触的算式。

    后缀表达式:

    以上面的中缀表达式 9+(3-1)*3+10/2 为例

    转换成后缀表达式为 9 3 1-3*+ 10 2/+ 

    特点是运算符紧跟在被运算的两个数之后,这样方便程序的理解与计算。

    只要顺序读取数字并根据其后的运算符进行相应的操作即可得到结果。

    对于以上的转换如有不懂可参考

    将中缀表达式转化为后缀表达式

    GitHub地址

    程序大致分为3步实现:

    1)将输入的字符串拆分为数字与运算符,用一个链表储存它们,方便接下来的计算

    2)使用仅储存运算符的栈将该中缀表达式转换为后缀表达式

    3)使用仅储存数字的栈来运算后缀表达式得到最终答案

    为实现这3步,代码有点繁琐。。。用了多个文件来分别实现具体功能

    以下是各个代码及大概作用,由于比较长,全部折叠了。

    Stack.h & Stack.c

    实现了一个储存运算符的栈(用于进行第2步)和一个储存数字的栈(用于第3步)

     1 //////////////
     2 // Stack.h //
     3 //////////////
     4 
     5 #ifndef _STACK_H_
     6 #define _STACK_H_
     7 
     8 #include<stdlib.h>
     9 
    10 #define STACK_INIT_SIZE 10
    11 #define STACKINCREMENT 10
    12 
    13 //优先级的定义
    14 #define TopLow -1
    15 #define Equal 0
    16 #define TopHigh 1
    17 //输入的是个括号
    18 #define InputIsBrace_1 2
    19 #define InputIsBrace_2 3
    20 //当前栈顶是个括号
    21 #define TopIsBrace 4
    22 
    23 typedef char SymType;
    24 typedef float NumType;
    25 
    26 //第一步
    27 //将中缀表达式转换为后缀表达式
    28 //该栈用于储存符号
    29 struct _Operator
    30 {
    31     SymType *Top,*Bottom;
    32     int stacksize;
    33 };
    34 typedef struct _Operator OPERATOR;
    35 
    36 //新建栈
    37 int InitStack_O(OPERATOR *S);
    38 
    39 //销毁栈
    40 int DestroyStack_O(OPERATOR *S);
    41 
    42 //检测栈是否为空
    43 //返回1表示为空
    44 int StackEmpty_O(OPERATOR S);
    45 
    46 //获取栈顶部元素
    47 SymType GetTop_O(OPERATOR S);
    48 
    49 //与栈顶的优先级进行比较
    50 int TopIsPriority_O(OPERATOR S, SymType Input,SymType Top);
    51 
    52 //Push操作,入栈,压栈
    53 int Push_O(OPERATOR *S, SymType e);
    54 
    55 //Pop操作,出栈
    56 SymType Pop_O(OPERATOR *S);
    57 
    58 //以上为符号栈
    59 //=====================================================================
    60 //以下为数字栈
    61 
    62 //第二步
    63 //计算后缀表达式的值
    64 //用于储存数据
    65 struct _Number
    66 {
    67     NumType *Top, *Bottom;
    68     int stacksize;
    69 };
    70 typedef struct _Number NUMBER;
    71 
    72 //新建栈
    73 int InitStack_N(NUMBER *S);
    74 
    75 //销毁栈
    76 int DestroyStack_N(NUMBER *S);
    77 
    78 //获取栈顶部元素
    79 NumType GetTop_N(NUMBER S);
    80 
    81 //Push操作,入栈,压栈
    82 int Push_N(NUMBER *S, NumType e);
    83 
    84 //Pop操作,出栈
    85 NumType Pop_N(NUMBER *S);
    86 
    87 //接受运算符
    88 //取栈顶的两个数字
    89 //进行相应运算
    90 void DoTheMath(NUMBER*,char);
    91 
    92 #endif
    Stack.h
      1 //////////////
      2 // Stack.c //
      3 /////////////
      4 
      5 #include"Stack.h"
      6 
      7 int InitStack_O(OPERATOR *S)
      8 {
      9     //创建出设定长度的顺序表,地址赋给bottom指针
     10     S->Bottom = (SymType*)malloc(STACK_INIT_SIZE*sizeof(SymType));
     11     if (!S->Bottom)
     12     {
     13         return 0;
     14     }
     15     S->stacksize = STACK_INIT_SIZE;
     16     S->Top = S->Bottom;
     17     return 1;
     18 }
     19 
     20 int DestroyStack_O(OPERATOR *S)
     21 {
     22     S->Top = NULL;
     23     free(S->Bottom);
     24     S->Bottom = NULL;
     25     return 1;
     26 }
     27 
     28 int StackEmpty_O(OPERATOR S)
     29 {
     30     if (S.Bottom == S.Top)
     31     {
     32         return 1;
     33     }
     34     else
     35     {
     36         return 0;
     37     }
     38 }
     39 
     40 SymType GetTop_O(OPERATOR S)
     41 {
     42     if (S.Bottom != S.Top)
     43     {
     44         //由于top指向的是最顶上元素的下一个位置
     45         //所以取出最顶上元素的时候
     46         //要把top减去1
     47         return *(S.Top - 1);
     48     }
     49     return 0;
     50 }
     51 
     52 //与栈顶的优先级进行比较
     53 int TopIsPriority_O(OPERATOR S, SymType Input,SymType top)
     54 {
     55     if (Input == '(')
     56     {
     57         return InputIsBrace_1;
     58     }
     59     if (Input == ')')
     60     {
     61         return InputIsBrace_2;
     62     }
     63     if (top == '(')
     64     {
     65         return TopIsBrace;
     66     }
     67     //-+优先级为1
     68     //*/优先级为2
     69     int p_top, p_input;
     70     if (top == '-' || top == '+')
     71     {
     72         p_top = 1;
     73     }
     74     if (top == '*' || top == '/')
     75     {
     76         p_top = 2;
     77     }
     78     if (Input == '-' || Input == '+')
     79     {
     80         p_input = 1;
     81     }
     82     if (Input == '*' || Input == '/')
     83     {
     84         p_input = 2;
     85     }
     86 
     87     if (p_input > p_top)
     88     {
     89         return TopLow;
     90     }
     91     if (p_input == p_top)
     92     {
     93         return Equal;
     94     }
     95     if (p_input < p_top)
     96     {
     97         return TopHigh;
     98     }
     99     return 0;
    100 }
    101 
    102 int Push_O(OPERATOR *S, SymType e)
    103 {
    104     //当超出当前栈的容量时进行扩容
    105     //这里应该只有等于的情况
    106     //而不会大于
    107     if ((S->Top - S->Bottom) >= S->stacksize)
    108     {
    109         //realloc函数将开辟指定大小的储存空间
    110         //并将原来的数据全部移到这个新的储存空间
    111         S->Bottom = (SymType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(SymType));
    112         if (!S->Bottom)
    113         {
    114             return 0;
    115         }
    116         //由于重新开辟了空间
    117         //需要重新根据bottom指针的位置指定top
    118         S->Top = S->Bottom + S->stacksize;
    119         //最后增加当前栈容量
    120         S->stacksize += STACKINCREMENT;
    121     }
    122     //再把入栈的数据存放好
    123     *(S->Top++) = e;
    124     return 1;
    125 }
    126 
    127 SymType Pop_O(OPERATOR *S)
    128 {
    129     if (S->Bottom == S->Top)
    130     {
    131         return 0;
    132     }
    133     //top指针先减1再取值
    134     return *(--S->Top);
    135 }
    136 
    137 //以上为符号栈的函数
    138 //========================================================================
    139 //以下为数字栈的函数
    140 
    141 int InitStack_N(NUMBER *S)
    142 {
    143     //创建出设定长度的顺序表,地址赋给bottom指针
    144     S->Bottom = (NumType*)malloc(STACK_INIT_SIZE*sizeof(NumType));
    145     if (!S->Bottom)
    146     {
    147         return 0;
    148     }
    149     S->stacksize = STACK_INIT_SIZE;
    150     S->Top = S->Bottom;
    151     return 1;
    152 }
    153 
    154 int DestroyStack_N(NUMBER *S)
    155 {
    156     S->Top = NULL;
    157     free(S->Bottom);
    158     S->Bottom = NULL;
    159     return 1;
    160 }
    161 
    162 NumType GetTop_N(NUMBER S)
    163 {
    164     if (S.Bottom != S.Top)
    165     {
    166         //由于top指向的是最顶上元素的下一个位置
    167         //所以取出最顶上元素的时候
    168         //要把top减去1
    169         return *(S.Top - 1);
    170     }
    171     return 0;
    172 }
    173 
    174 int Push_N(NUMBER *S, NumType e)
    175 {
    176     //当超出当前栈的容量时进行扩容
    177     //这里应该只有等于的情况
    178     //而不会大于
    179     if ((S->Top - S->Bottom) >= S->stacksize)
    180     {
    181         //realloc函数将开辟指定大小的储存空间
    182         //并将原来的数据全部移到这个新的储存空间
    183         S->Bottom = (NumType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(NumType));
    184         if (!S->Bottom)
    185         {
    186             return 0;
    187         }
    188         //由于重新开辟了空间
    189         //需要重新根据bottom指针的位置指定top
    190         S->Top = S->Bottom + S->stacksize;
    191         //最后增加当前栈容量
    192         S->stacksize += STACKINCREMENT;
    193     }
    194     //再把入栈的数据存放好
    195     *(S->Top++) = e;
    196     return 1;
    197 }
    198 
    199 NumType Pop_N(NUMBER * S)
    200 {
    201     if (S->Bottom == S->Top)
    202     {
    203         return 0;
    204     }
    205     //top指针先减1再取值
    206     return *(--S->Top);
    207 }
    208 
    209 void DoTheMath(NUMBER *S,char sym)
    210 {
    211     //从栈顶顺序读入两个数字
    212     //进行运算
    213     //并将结果压回栈内
    214     float num1, num2;
    215     num2 = Pop_N(S);
    216     num1 = Pop_N(S);
    217     switch (sym)
    218     {
    219     case '+':
    220         Push_N(S, num1 + num2);
    221         break;
    222     case '-':
    223         Push_N(S, num1 - num2);
    224         break;
    225     case '*':
    226         Push_N(S, num1*num2);
    227         break;
    228     case '/':
    229         Push_N(S, num1 / num2);
    230         break;
    231     default:
    232         break;
    233     }
    234 }
    Stack.c

    StringToNumAndSym.h & StringToNumAndSym.c

    这个文件的功能是。。。。

    将输入的一大串字符串拆分成一个数字或一个运算符,并将他们按顺序储存到一个链表里。

    用于进行第1步。

     1 ////////////////////////////////
     2 //StringToNumAndSym.h//
     3 ///////////////////////////////
     4 
     5 #ifndef _STRINGTONUMANDSYM_H_
     6 #define _STRINGTONUMANDSYM_H_
     7 
     8 #include<stdlib.h>
     9 
    10 //用于储存输入的数字或者符号
    11 //IsNum=1 将读取数字
    12 //IsNum=0 将读取字符
    13 struct data_node
    14 {
    15     int IsNum;
    16     float Num;
    17     char Sym;
    18     struct data_node *next;
    19 };
    20 typedef struct data_node EQUATION;
    21 
    22 //该函数接受输入的字符串
    23 //输出一串处理好的等式的指针
    24 //用于表示数字或者运算符号加减乘除等等
    25 void StringToEquation(char *,EQUATION**);
    26 
    27 void DestroyEquation(EQUATION*);
    28 
    29 #endif
    StringToNumAndSym.h
      1 /////////////////////////////////
      2 // StringToNumAndSym.c //
      3 ///////////////////////////////
      4 
      5 #include"StringToNumAndSym.h"
      6 
      7 void StringToEquation(char *input, EQUATION **ptr_source)
      8 {
      9     EQUATION *ptr = NULL;
     10     int lop;
     11     //记录正数部分的temp
     12     int Ptmp;
     13     //记录小数部分的temp
     14     float Dtmp;
     15     //PNum记录正数的位数
     16     //NNum记录小数的个数
     17     float Dmulti;
     18     int Pmulti;
     19 
     20     for (lop = 0; input[lop] != 0; lop++)
     21     {
     22         //接下来有个数字
     23         //除错,确保输入合法
     24         if (input[lop] >= '0' && input[lop] <= '9'
     25             || input[lop] == '+' || input[lop] == '-' || input[lop] == '/' || input[lop] == '*'
     26             || input[lop] == '(' || input[lop] == ')' || input[lop] == '.')
     27         {
     28             if (input[lop] >= '0' && input[lop] <= '9')
     29             {
     30                 Ptmp = 0;
     31                 Dtmp = 0;
     32                 Pmulti = 1;
     33                 Dmulti = 1;
     34 
     35                 //计算整数部分
     36                 for (; input[lop] >= '0' && input[lop] <= '9'; lop++)
     37                 {
     38                     Ptmp = Ptmp * 10 + (input[lop] - '0');
     39                 }
     40 
     41                 //如果有小数
     42                 //计算小数部分
     43                 if (input[lop] == '.')
     44                 {
     45                     Dmulti = (float)0.1;
     46                     //计算出小数部分
     47                     for (lop += 1; input[lop] >= '0' && input[lop] <= '9'; lop++)
     48                     {
     49                         Dtmp += Dmulti*(float)(input[lop] - '0');
     50                         Dmulti *= (float)0.1;
     51                     }
     52                 }
     53                 //得到数字了
     54                 //创建节点保存
     55                 if (ptr == NULL)
     56                 {
     57                     ptr = (EQUATION*)malloc(sizeof(EQUATION));
     58                     //创建第一个节点之后
     59                     //由于传入的是指针的指针
     60                     //需要更改ptr_source的值
     61                     //保存头指针
     62                     *ptr_source = ptr;
     63                 }
     64                 else
     65                 {
     66                     ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
     67                     ptr = ptr->next;
     68                 }
     69                 ptr->next = NULL;
     70                 ptr->IsNum = 1;
     71                 ptr->Num = (float)Ptmp + Dtmp;
     72                 ptr->Sym = '0';
     73 
     74                 lop--;
     75             }
     76             else
     77             {
     78                 //第一个节点
     79                 if (ptr == NULL)
     80                 {
     81                     ptr = (EQUATION*)malloc(sizeof(EQUATION));
     82                     *ptr_source = ptr;
     83                 }
     84                 //之后的节点
     85                 else
     86                 {
     87                     ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
     88                     ptr = ptr->next;
     89                 }
     90                 ptr->next = NULL;
     91                 ptr->IsNum = 0;
     92                 ptr->Num = 0;
     93                 ptr->Sym = input[lop];
     94             }
     95         }
     96     }
     97 }
     98 
     99 /*
    100 //反转整数
    101 int Invert(int input)
    102 {
    103     int output = 0;
    104     while (input > 0)
    105     {
    106         output = output * 10 + input % 10;
    107         input /= 10;
    108     }
    109     return output;
    110 }
    111 */
    112 
    113 void DestroyEquation(EQUATION *N)
    114 {
    115     EQUATION *temp = NULL;
    116     if (N != NULL)
    117     {
    118         temp = N;
    119         N = N->next;
    120         free(temp);
    121         temp = NULL;
    122 
    123         DestroyEquation(N);
    124     }
    125 }
    StringToNumAndSym.c

    main.c

    主函数在此,实现了判断运算符运算顺序的逻辑

      1 /////////////
      2 // main.c //
      3 /////////////
      4 
      5 #include<stdio.h>
      6 
      7 #include"Stack.h"
      8 #include"StringToNumAndSym.h"
      9 
     10 int main()
     11 {
     12     int lop = 0;
     13 
     14     //新建用于储存输入数据的字符串
     15     char string[30] = { 0 };
     16     //新建用于储存处理后数据的字符串
     17     EQUATION *Eqinput;
     18 
     19     //以下4行创建并初始化两个栈
     20     //分别用于存放运算符和数字
     21     OPERATOR Stack_Ope;
     22     NUMBER Stack_Num;
     23 
     24     InitStack_O(&Stack_Ope);
     25     InitStack_N(&Stack_Num);
     26 
     27     //输入合法的中缀表达式
     28     //不要太长
     29     printf("请输入合法的中缀表达式(不带空格):
    ");
     30     scanf("%s", string);
     31 
     32     //将输入的字符串转化为能够处理的数据
     33     StringToEquation(string, &Eqinput);
     34 
     35     //将中缀表达式转换为后缀表达式
     36     //同时包含对数据的运算
     37 
     38     //如果输入的数据还没有处理完
     39     while (Eqinput != NULL)
     40     {
     41         //看看当前对象是不是数字
     42         if (Eqinput->IsNum == 1)
     43         {
     44             //是数字就直接丢到处理数字的栈里去
     45             Push_N(&Stack_Num, Eqinput->Num);
     46         }
     47         else
     48         {
     49             if (StackEmpty_O(Stack_Ope))
     50             {
     51                 Push_O(&Stack_Ope, Eqinput->Sym);
     52             }
     53 
     54             //是运算符的话
     55             //将他与上一个运算符,也就是栈顶的那个运算符进行比较
     56             //然后根据两者的优先级大小来做出相应操作
     57             else
     58             {
     59                 //优先级判断
     60                 switch (TopIsPriority_O(Stack_Ope,Eqinput->Sym,GetTop_O(Stack_Ope)))
     61                 {
     62                     //如果栈顶是括号
     63                     //不进行优先级的比较
     64                     //直接入栈
     65                 case TopIsBrace:
     66                     Push_O(&Stack_Ope, Eqinput->Sym);
     67                     break;
     68 
     69                     //输入了左括号
     70                 case InputIsBrace_1:
     71                     Push_O(&Stack_Ope, '(');
     72                     break;
     73 
     74                     //输入了右括号
     75                     //一直出栈
     76                     //直到遇到左括号
     77                 case InputIsBrace_2:
     78                     while((GetTop_O(Stack_Ope) != '('))
     79                     {
     80                         //printf("%c ", Pop_O(&Stack_Ope));
     81                         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
     82                     }
     83                     Pop_O(&Stack_Ope);
     84                     break;
     85 
     86                     //当前运算符优先级比栈顶的低或者相等
     87                 case TopHigh:
     88                 case Equal:
     89                     while (!StackEmpty_O(Stack_Ope)  )
     90                     {
     91                         if (GetTop_O(Stack_Ope) == '(')
     92                         {
     93                             break;
     94                         }
     95                         //printf("%c ", Pop_O(&Stack_Ope));
     96                         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
     97                     }
     98                     Push_O(&Stack_Ope, Eqinput->Sym);
     99                     break;
    100 
    101                     //当前运算符优先级比栈顶的高
    102                 case TopLow:
    103                     Push_O(&Stack_Ope, Eqinput->Sym);
    104                     break;
    105                 default:
    106                     break;
    107                 }
    108             }
    109         }
    110         //读入下一个数据
    111         Eqinput = Eqinput->next;
    112     }
    113 
    114     //如果栈里还有残留的运算符
    115     //全部出栈
    116     while (!StackEmpty_O(Stack_Ope))
    117     {
    118         //printf("%c ", Pop_O(&Stack_Ope));
    119         DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
    120     }
    121 
    122     printf("
    ");
    123 
    124     //////////////
    125     //运算完毕//
    126     /////////////
    127 
    128     printf("%s = %.3f
    ", string, GetTop_N(Stack_Num));
    129 
    130     //销毁输入的式子
    131     DestroyEquation(Eqinput);
    132     Eqinput = NULL;
    133 
    134     DestroyStack_N(&Stack_Num);
    135     DestroyStack_O(&Stack_Ope);
    136 
    137     return 0;
    138 }
    main.c

    运行效果:

  • 相关阅读:
    TensorFlow 2.0 笔记(三)神经网络搭建八股
    TensorFlow 2.0 笔记(四)功能扩展
    Zabbix监控告警系统
    TensorFlow 2.0 笔记(二)神经网络优化
    TensorFlow 2.0 笔记(一)神经网络计算过程及模型搭建
    TensorFlow 2.0 笔记(五)卷积神经网络
    js手机校验正则表达式
    【ASP.NET Core】自己编程来生成自签名的服务器证书
    golang之带指针的结构体格式化打印
    go使用logrus同时输出屏幕和文件日志
  • 原文地址:https://www.cnblogs.com/makejeffer/p/4820310.html
Copyright © 2020-2023  润新知