数据结构(二)栈与队列---栈的应用:四则运算实现

栈的应用:四则运算实现

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

(一)预备知识

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

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!='