zoukankan      html  css  js  c++  java
  • 数据结构(二)栈与队列---栈的应用:四则运算实现

    栈的应用:四则运算实现

    注:我们这里使用链栈来实现,当然前面的顺序栈同样可以实现,而且更加容易理解。这里我们使用链栈来练习

    (一)预备知识

    前缀、中缀、后缀表达式(逆波兰表达式)

    union联合体使用详解

    前缀表达式、中缀表达式、后缀表达式都是四则运算的表达方式,用以四则运算表达式求值

    中缀表达式

    中缀表达式就是常见的运算表达式,如(3+4)×5-6

    前缀表达式

    前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前
    比如:- × + 3 4 5 6

    后缀表达式(逆波兰表达式)<这是我们使用的>

    后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
    比如:3 4 + 5 × 6 -

    (二)中缀转后缀

    例如,将中缀表达式“1+((2+3)×4)-5”转换为后缀表达式的过程如下

    扫描到的元素s2(栈底->栈顶)s1 (栈底->栈顶)说明
    1 1 数字,直接入栈
    + 1 + s1为空,运算符直接入栈
    ( 1 + ( 左括号,直接入栈
    ( 1 + ( ( 同上
    2 1 2 + ( ( 数字
    + 1 2 + ( ( + s1栈顶为左括号,运算符直接入栈
    3 1 2 3 + ( ( + 数字
    ) 1 2 3 + + ( 右括号,弹出运算符直至遇到左括号
    × 1 2 3 + + ( × s1栈顶为左括号,运算符直接入栈
    4 1 2 3 + 4 + ( × 数字
    ) 1 2 3 + 4 × + 右括号,弹出运算符直至遇到左括号
    - 1 2 3 + 4 × + - -与+优先级相同,因此弹出+,再压入-
    5 1 2 3 + 4 × + 5 - 数字
    到达最右端 1 2 3 + 4 × + 5 - s1中剩余的运算符

    因此结果为“1 2 3 + 4 × + 5 -”

    步骤:

    1. 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
    2. 从左至右扫描中缀表达式;
    3. 遇到操作数时,将其压s2;
    4. 遇到运算符时,比较其与s1栈顶运算符的优先级:
      1. 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
      2. 否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况);
      3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
    5. 遇到括号时:
      1. 如果是左括号“(”,则直接压入s1;
      2. 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
    6. 重复步骤2至5,直到表达式的最右边;
    7. 将s1中剩余的运算符依次弹出并压入s2;
    8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)

    (三)计算后缀表达式结果

    从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

    例如后缀表达式“3 4 + 5 × 6 -”

    1. 从左至右扫描,将3和4压入堆栈;
    2. 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素,注意与前缀表达式做比较),计算出3+4的值,得7,再将7入栈;
    3. 将5入栈;
    4. 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
    5. 将6入栈;
    6. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果。

    (四)代码实现

    //栈中数据类型
    typedef struct Data
    {
        int flag;    //1为字符,2为浮点数
        union 
        {
            double num;
            char sign;
        }number;
    }data;
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    
    #define STACK_INIT_SIZE 100    //定义栈的初始大小
    #define STACK_INCR_SIZE 10    //定义栈的增长大小
    
    #define MAXSIZE 100    //中缀表达式的长度
    
    typedef struct Data
    {
        int flag;    //1为字符,2为浮点数
        union 
        {
            double num;
            char sign;
        }number;
    }data;
    
    typedef data ElemType;
    typedef int Status;
    
    typedef struct
    {
        ElemType *base;    //栈底指针
        ElemType *top;    //栈顶指针
        int stackSize;    //最大容量,这是可修改的
    }sqStack;
    
    
    
    //四个基础操作
    Status InitStack(sqStack *s);    //初始化操作,建立一个空栈
    Status ClearStack(sqStack *s);    //将栈清空
    Status StackEmpty(sqStack s);    //若栈存在,返回true,否则返回false
    int StackLength(sqStack s);        //返回栈S的元素个数
    
    Status GetTop(sqStack s, ElemType *e);    //若是栈存在且非空,用e返回S的栈顶元素
    Status Push(sqStack *s, ElemType e);    // 若是栈存在,则插入新的元素e到栈S中并成为栈顶元素
    Status Pop(sqStack *s, ElemType *e);    //若是栈存在且非空,删除栈顶元素,并用e返回其值
    Status DestroyStack(sqStack *s);        //若是栈存在,则销毁他
    
    void PrintStack(sqStack s);    //打印所有数据,测试用,会依次将数据出栈
    
    
    //初始化操作,建立一个空栈
    Status InitStack(sqStack *s)
    {
        s->base = (ElemType *)malloc(STACK_INIT_SIZE*sizeof(ElemType));
        if (!s->base)
            return ERROR;
        s->top = s->base;    //最开始,栈顶就是栈底
        s->stackSize = STACK_INIT_SIZE;
        return OK;
    }
    
    //将栈清空,将栈顶指针移动到栈底即可,容量大小不要修改,数据不需要清空,数据入栈会覆盖
    Status ClearStack(sqStack *s)
    {
        if (s == NULL)
            return ERROR;
        s->top = s->base;
        return OK;
    }
    
    //若栈存在,返回true,否则返回false
    Status StackEmpty(sqStack s)
    {
        if (s.base == s.top)
            return TRUE;
        return FALSE;
    }
    
    //返回栈S的元素个数
    int StackLength(sqStack s)
    {
        int length = s.top - s.base;    //指针之间运算,是按照其中数据大小字节来算的
        return length;
    }
    
    //若是栈存在且非空,用e返回S的栈顶元素,注意:只是获取栈顶数据,不出栈
    Status GetTop(sqStack s, ElemType *e)
    {
        if (!e || StackEmpty(s) || !s.base)
            return ERROR;
        *e = *(s.top - 1);
        return OK;
    }
    
    //入栈操作:若是栈存在,则插入新的元素e到栈S中并成为栈顶元素
    Status Push(sqStack *s, ElemType e)
    {
        ElemType* newStack;
        if (!s->base)
            return ERROR;
    
        if (s->top-s->base>=s->stackSize)    //栈满,需要再分配
        {
            newStack = (ElemType *)realloc(s->base, (s->stackSize + STACK_INCR_SIZE)*sizeof(ElemType));    //重新分配大小
            if (!newStack)    //若是分配失败,会返回NULL
            {
                free(s->base);
                exit(0);    //分配失败,直接退出
            }
            s->base = newStack;
            //分配后需要将栈顶指针进行移动到新的位置
            s->top = s->base + s->stackSize;
        }
        *(s->top) = e;
        s->top++;
        return OK;
    }
    
    //若是栈存在且非空,删除栈顶元素(只需要将栈顶指针下移即可),并用e返回其值
    Status Pop(sqStack *s, ElemType *e)
    {
        if (!s->base || !e || StackEmpty(*s))
            return ERROR;
        *e = *(--s->top);
        return OK;
    }
    
    //若是栈存在,则销毁他(直接将栈底指针释放即可,置为空)
    Status DestroyStack(sqStack *s)
    {
        if (!s->base)    //若是栈存在
        {
            s->stackSize = 0;
            free(s->base);
            s->base = s->top = NULL;
        }
        return OK;
    }
    
    //打印数据
    void PrintStack(sqStack s)
    {
        data d;
        int len = StackLength(s);
        for (int i = 0; i < len;i++)
        {
            Pop(&s, &d);
            if (d.flag == 1)
                printf("%c", d.number.sign);
            else
                printf("%lf", d.number.num);
        }
    }
    栈的实现和基本操作实现
    //四则运算需要的函数
    
    Status MatchBrack(char* str);    //匹配括号是否正确
    
    Status RotateStack(sqStack* s);    //将栈中的数据翻转
    
    Status GetMidStack(sqStack *s, char* str);    //获取中缀表达式,将字符串转换到栈中
    Status GetBackStack(sqStack *s);    //获取后缀表达式
    Status GetBackValue(sqStack *s, double* val);    //获取后缀表达式计算出来的结果
    //括号匹配
    Status MatchBrack(char* str)
    {
        char *cur = str;
        sqStack BrkSK;
        data d;
        char ch;
        Status sta = OK;;
    
        if (!str || !InitStack(&BrkSK))
            return ERROR;
    
        while (*cur!='')
        {
            if (*cur=='('||*cur=='['||*cur=='{')
            {
                d.flag = 1;
                d.number.sign = *cur;
                Push(&BrkSK,d);
            }
            else if (*cur == ')' || *cur == ']' || *cur == '}' )
            {
                if (!GetTop(BrkSK, &d))
                {
                    sta = ERROR;
                    break;
                }
                ch = d.number.sign;
                if ((*cur == ')'&&ch == '(') || (*cur == ']'&&ch == '[') || (*cur == '}'&&ch == '{'))
                    Pop(&BrkSK, &d);
                else
                {
                    sta = ERROR;
                    break;
                }
            }
            cur++;
        }
        DestroyStack(&BrkSK);
        return sta;
    }
    
    //获取中缀表达式
    Status GetMidStack(sqStack *s, char* str)
    {
        char* cur, *start, *end;
        cur = start = end = str;
        char fnum[10] = { 0 };
        data d;
    
        if (!s || !str)
            return ERROR;
    
        while (*cur != '')
        {
            if (!isdigit(*cur) && *cur != '.')
            {
                if (cur > str&&isdigit(*(cur - 1)))
                {
                    end = cur;
                    memset(fnum, 0, 10);
                    memcpy(fnum, start, (end - start));
                    d.flag = 2;
                    d.number.num = atof(fnum);
                    Push(s, d);
                }
                d.flag = 1;
                d.number.sign = *cur;
                if (isdigit(*(cur + 1)))
                    start = cur + 1;
                Push(s, d);
            }
            cur++;
        }
        if (start > end)    //最后以数字结尾,需要再进行判断
        {
            end = cur;
            memset(fnum, 0, 10);
            memcpy(fnum, start, (end - start));
            d.flag = 2;
            d.number.num = atof(fnum);
            Push(s, d);
        }
        return OK;
    }
    
    //将栈中的数据翻转
    Status RotateStack(sqStack* s)
    {
        sqStack tempSk, *freeSk;
        if (!InitStack(&tempSk) || !s)
            return ERROR;
        data d;
        int length = StackLength(*s);
        for (int i = 0; i < length; i++)
        {
            Pop(s, &d);
            Push(&tempSk, d);
        }
        freeSk = s;
        *s = tempSk;
        DestroyStack(freeSk);
    }
    
    //获取后缀表达式
    Status GetBackStack(sqStack *s)
    {
        sqStack OperaSk, ResSk, *tempSk;
        OperaSk.base = ResSk.base = NULL;
        data d, top;
        char pc;    //存储单个运算符
        int length = StackLength(*s);
        if (!s)
            return ERROR;
        if (!InitStack(&OperaSk) || !InitStack(&ResSk))
        {
            DestroyStack(&OperaSk);
            DestroyStack(&ResSk);
            return ERROR;
        }
        for (int i = 0; i < length; i++)
        {
            Pop(s, &d);
            if (d.flag == 2)
                Push(&ResSk, d);
            if (d.flag == 1)
            {
                if (GetTop(OperaSk, &top))
                    pc = top.number.sign;
    
                if (d.number.sign == ')')
                {
                    //将'('之上的运算符全部弹出到ResSk
                    while (pc != '(')
                    {
                        Pop(&OperaSk, &d);
                        Push(&ResSk, d);
                        GetTop(OperaSk, &top);
                        pc = top.number.sign;
                    }
                    Pop(&OperaSk, &d);    //将(一起弹出
                    continue;
                }
    
                if (StackEmpty(OperaSk) || pc == '(' || d.number.sign == '(')    //若是OperaSk为空或者栈顶为(或者获取的运算符为(,我们直接将这个运算符压栈
                    Push(&OperaSk, d);
                else if ((pc == '-' || pc == '+') && (d.number.sign == '*' || d.number.sign == '/'))    //若是栈顶的优先级低,也压栈,但是要先将栈顶的
                    Push(&OperaSk, d);
                else if (pc == d.number.sign || (pc == '-'&&d.number.sign == '+') || (pc == '+'&&d.number.sign == '-') || (pc == '*'&&d.number.sign == '/') || (pc == '/'&&d.number.sign == '*'))    //当栈顶优先级和当前运算符一致,先弹出到ResSk,在进行压栈
                {
                    Pop(&OperaSk, &top);
                    Push(&ResSk, top);
                    Push(&OperaSk, d);
                }
                else  //当获取的运算符优先级低于栈顶优先级,先将栈顶运算符移栈到ResSk,然后再将当前运算符与下一次进行比较,压栈到OperaSK栈
                {
                    Pop(&OperaSk, &top);    //获取OperaSK栈顶数据
                    Push(&ResSk, top);        //将数据放入ResSK栈
    
                    Push(s, d);    //由于当前数据没有完成入栈,我们将它放回原来栈中,再次进行比较
                    length++;
                }
            }
        }
    
        while (!StackEmpty(OperaSk))    //将运算符栈中的剩余的数据全部移到结果栈
        {
            Pop(&OperaSk, &d);
            Push(&ResSk, d);
        }
    
        tempSk = s;
        *s = ResSk;
        DestroyStack(tempSk);
        DestroyStack(&OperaSk);
        return OK;
    }
    
    //获取后缀表达式计算出来的结果
    Status GetBackValue(sqStack *s, double* val)
    {
        sqStack OpValSk;    //获取运算结果的栈
        int length = StackLength(*s);
        double op1, op2, value = 0;
        data d, top;
    
        if (!s || !InitStack(&OpValSk))
            return ERROR;
    
        for (int i = 0; i < length; i++)
        {
            Pop(s, &d);
            if (d.flag == 2)    //数字
                Push(&OpValSk, d);
            else   //运算符,取两个数运算后,放回栈中
            {
                Pop(&OpValSk, &top);
                op2 = top.number.num;
                Pop(&OpValSk, &top);
                op1 = top.number.num;
                switch (d.number.sign)
                {
                case '+':
                    value = op1 + op2;
                    break;
                case '-':
                    value = op1 - op2;
                    break;
                case '*':
                    value = op1 * op2;
                    break;
                case '/':
                    value = op1 / op2;
                    break;
                default:
                    break;
                }
                d.flag = 2;
                d.number.num = value;
                Push(&OpValSk, d);
            }
        }
        *val = value;
        DestroyStack(&OpValSk);
        return OK;
    }
    int main()
    {
        sqStack sk;
        double value;
        ElemType e;
        char str[MAXSIZE] = { 0 };
        sk.base = sk.top = NULL;    //用于判断是否存在
        //初始化空栈
        InitStack(&sk);
        //接收输入的中缀表达式
        printf("please enter expression<no space>:");
        scanf("%s", str);
        //先进行括号匹配
        if (!MatchBrack(str))
            return 0;
        //将字符串中的中缀表达式分割,转换为中缀表达式存放在栈中'1+2+3'--->3.0 + 2.0 + 1.0每一个都是一个栈空间数据
        GetMidStack(&sk, str);
        //将上面的中缀表达式翻转,变为正常的
        RotateStack(&sk);
        //下面将中缀表达式转后缀表达式
        GetBackStack(&sk);
        //将上面的后缀表达式翻转,变为正常的
        RotateStack(&sk);
        //获取后缀表达式结果
        GetBackValue(&sk, &value);
        //PrintStack(sk);
        printf("Operation values:%lf", value);
        //PrintStack(sk);
        DestroyStack(&sk);
        system("pause");
        return 0;
    }

    (五)结果演示

    (六)反思

    实现了小数运算,但是不足的是对方法不熟悉,导致耗费太多时间
  • 相关阅读:
    HAProxy、Keepalived 在 Ocatvia 的应用实现与分析
    Octavia 的 HTTPS 与自建、签发 CA 证书
    Octavia 创建 loadbalancer 的实现与分析
    OpenStack Rally 质量评估与自动化测试利器
    自建 CA 中心并签发 CA 证书
    Failed building wheel for netifaces
    通过 vSphere WS API 获取 vCenter Datastore Provisioned Space 置备空间
    OpenStack Placement Project
    我们建了一个 Golang 硬核技术交流群(内含视频福利)
    没有图形界面的软件有什么用?
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9440307.html
Copyright © 2011-2022 走看看