后缀表达式的转换(栈的运用)

简介: 后缀表达式又称逆波兰式,在后缀表达式中,操作符始终在两个操作数之后。将表达式转换为后缀表达式主要借助字符栈来实现,下面让我们一起来看看吧~

一、主要思路

由于操作符具有优先级,因此我们应该先设置操作符的优先级
操作符的优先级
操作符 # ( + - * /
优先级 -1 0 1 1 2 2
主要的方法为:
(1)初始化一个顺序栈
(2)设表达式的结束符为“#”,预设操作符栈的栈底为“#”
(3)若当前字符是操作数,则直接放入后缀式
(4)若当前字符是操作符且优先级大于栈顶操作符,则入栈,否则退出栈顶操作符放到后缀式,继续下次判断,直到可以入栈
(5)若当前字符是“#”,则自栈顶至栈底依次将栈中所有操作符发送给后缀式
(6)遇到“(”直接入栈
(7)遇到“)” ,从栈顶开始判断,并放入后缀式,一直到栈顶元素为“(”时,出栈“(”

二、具体分析

接下来我们通过一个例子来分析后缀表达式的生成过程
将 23-(2-4)*2+36/(20-14)# 转换为后缀表达式的过程:
序号 读取字符 类型 操作 栈内变化 输出的后缀式
0 初始化顺序栈,置栈底 # #
1 2 操作数 # 2
2 3 操作数 # 23
3 - 操作符 - 优先级大于 # ,入栈 # - 23
4 ( 操作符 ( 直接入栈 # - ( 23
5 2 操作数 # - ( 23 2
6 - 操作符 - 优先级大于 ( ,入栈 # - ( - 23 2
7 4 操作数 # - ( - 23 2 4
8 ) 操作符 先出栈再判断,直到遇到 ( # - ( 23 2 4 -
9 遇到 ( 不用放到后缀式 # - 23 2 4 -
10 操作符 优先级大于 - ,入栈 # - * 23 2 4 -
11 2 操作数 # - * 23 2 4 - 2
12 + 操作符 + 优先级小于 出栈 # - 23 2 4 - 2 *
13 + 优先级等于 - 优先级,出栈 # 23 2 4 - 2 * -
14 + 优先级大于 #,入栈 # + 23 2 4 - 2 * -
15 3 操作数 # + 23 2 4 - 2 * - 3
16 6 操作数 # + 23 2 4 -2 * - 36
17 / 操作符 / 优先级大于+,入栈 # + / 23 2 4 -2 * - 36
18 ( 操作符 ( 直接入栈 # + / ( 23 2 4 -2 * - 36
19 2 操作数 # + / ( 23 2 4 -2 * - 36 2
20 0 操作数 # + / ( 23 2 4 -2 * - 36 20
21 - 操作符 - 优先级大于 ( ,入栈 # + / ( - 23 2 4 -2 * - 36 20
22 1 操作数 # + / ( - 23 2 4 -2 * - 36 20 1
23 4 操作数 # + / ( - 23 2 4 -2 * - 36 20 14
24 ) 操作符 先出栈栈顶元素,再判断 # + / ( 23 2 4 -2 * - 36 20 14 -
25 遇到 ( 出栈,不需要放到后缀式 # + / 23 2 4 -2 * - 36 20 14 -
26 # 结束符 依次出栈发送给后缀式 # + 23 2 4 -2 * - 36 20 14 - /
27 # 23 2 4 -2 * - 36 20 14 - / +
这个过程很复杂,其中包括了遇到连续的操作数时,需要将他们放到一起的过程
而这个过程在表格里没有体现,我们需要用代码将他体现出来
例如两位数23,你读取的时候分别读取2,3,但是放入后缀式的时候你需要将他们放到一起,使他们保持自己是一个整体,这就需要动一下小脑筋了

三、核心算法

void Change_Char(char *s1,char *s2){
    Stack s;
    int i=0,e;
    Push_Stack(&s,'#');   //初始化栈,置栈底为“#”
    while(*s1!='\0'){          //当遇到空格时结束循环
        if(*s1>='0'&&*s1<='9'){    //如果遇到操作数
            while(*s1>='0'&&*s1<='9'){     //这个方法将连续的操作数放到一起
                s2[i++]=*s1;
                s1++;
            }
            s2[i++]=' ';    //打印空格用于分隔
            s1--;
        }else{            //当遇到操作符时
            Get_Top(&s,&e);        //获取栈顶元素
            if((f(*s1)>f(e)&&*s1!=')')||*s1=='('){  //如果该操作符优先级大于栈顶元素且不等
                Push_Stack(&s,*s1);                 //于“)”或者是“(”时,入栈
            }else if(*s1==')'){    //当操作符时“)”时
                Pop_Stack(&s,&e);  //先出栈
                while(e!='('){     //再判断,如果不是“(”,放到后缀式
                    s2[i++]=e;   
                    s2[i++]=' ';
                    Pop_Stack(&s,&e);   //再次执行出栈,继续判断
                }   
            }else{         
                while(f(*s1)<=f(e)){    //当操作符优先级小于栈顶元素时
                    Pop_Stack(&s,&e);   //出栈
                    s2[i++]=e;          //放到后缀式
                    s2[i++]=' ';
                    Get_Top(&s,&e);     //重新获取栈顶元素继续执行判断
                }
                Push_Stack(&s,*s1);     //最后当前操作符优先级大于栈顶操作符时,入栈
            }
        }
        s1++;    //指针指向下一个字符,继续判断
    }
    while(!Empty_Stack(&s)){    //当栈非空时,将栈中操作符依次出栈放入后缀式
        Pop_Stack(&s,&e);       
        s2[i++]=e;
    }
    int k=0; 
    while(1){             //打印后缀式
        if(s2[k]=='#') break;
        printf("%c",s2[k++]);
    }
}

四、完整代码

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define SIZE 10
#define INCREAM 10
typedef struct Stack{
    int *base;
    int *top;
    int size;
}Stack,*PStack;
int Init_Stack(PStack S){
    S->base=(int *)malloc(sizeof(int)*SIZE);
    S->top=S->base;
    S->size=SIZE;
    return 1;
} 
int Push_Stack(PStack S,int e){
    if(S->top-S->base>=S->size){
        S->base=(int *)realloc(S->base,(S->size+INCREAM)*sizeof(int));
        S->top=S->base+S->size;
        S->size+=INCREAM;
    }
    *S->top=e;
    S->top++;
    return 1;
}
int Pop_Stack(PStack S,int *e){
    if(S->base==S->top){
        return 0;
    }
    --S->top;
    *e=*S->top;
    return 1;
}
int Get_Top(PStack S,int *e){
    if(S->base==S->top) return 0;
    *e=*(S->top-1);
    return 1;
}
int Empty_Stack(PStack S){
    if(S->base==S->top) return 1;
    else return 0;
}
int f(char s){
    if(s=='#') return -1;
    else if(s=='('||s==')') return 0;
    else if(s=='+'||s=='-') return 1;
    else if(s=='*'||s=='/') return 2;
}
void Change_Char(char *s1,char *s2){
    Stack s;
    int i=0,e;
    Push_Stack(&s,'#');
    while(*s1!='\0'){
        if(*s1>='0'&&*s1<='9'){
            while(*s1>='0'&&*s1<='9'){
                s2[i++]=*s1;
                s1++;
            }
            s2[i++]=' ';
            s1--;
        }else{
            Get_Top(&s,&e);
            if((f(*s1)>f(e)&&*s1!=')')||*s1=='('){
                Push_Stack(&s,*s1);
            }else if(*s1==')'){
                Pop_Stack(&s,&e);
                while(e!='('){
                    s2[i++]=e;
                    s2[i++]=' ';
                    Pop_Stack(&s,&e);
                }
            }else{
                while(f(*s1)<=f(e)){
                    Pop_Stack(&s,&e);
                    s2[i++]=e;
                    s2[i++]=' ';
                    Get_Top(&s,&e);
                }
                Push_Stack(&s,*s1);
            }
        }
        s1++;
    }
    while(!Empty_Stack(&s)){
        Pop_Stack(&s,&e);
        s2[i++]=e;
    }
    int k=0;
    while(1){
        if(s2[k]=='#') break;
        printf("%c",s2[k++]);
    }
}
int main(){
    char s1[100],s2[100];
    scanf("%s",s1);
    Change_Char(s1,s2);
    return 0;
}

五、运行结果

1.png

目录
相关文章
|
10月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
845 9
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
213 59
|
3月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
41 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
328 77
|
7月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
143 11
|
7月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
8月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
236 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
130 9
|
8月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
182 7