C/C++语言数据结构快速入门(代码解析+内容解析)栈的应用
Posted 蓝盒子bluebox
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++语言数据结构快速入门(代码解析+内容解析)栈的应用相关的知识,希望对你有一定的参考价值。
一、括号匹配
1、算法演示
#include<stdio.h>
#define MaxSize 10 //定义栈中元素的最大值
typedef struct {
char data[MaxSize];//静态数组当中存放栈中元素
int top; //栈顶指针
}SqStack;
bool bracketCheck(char str[],int length){
SqStack S;
InitStack(S);
for(int i = 0; i < length;i++){
if(str[i] == '(' || str[i] == '[' || str[i] == '{' ){
Push(S,str[i]);
}else{
if(StackEmpty(S))
return false;
char topElem;
Pop(S,topElem);//栈顶元素出栈
if(str[i] == ')' && topElem != '(')
return false;
if(str[i] == ']' && topElem != '[')
return false;
if(str[i] == '}' && topElem != '{')
return false;
}
}
return StackEmpty(S); //检索完全括号后,栈空的说明匹配成功
}
//初始化栈
void InitStack(SqStack &S)
//判断栈是否为空
bool StackEmpty(SqStack S)
//新元素入栈
bool Push(SqStack &S,char x)
//栈顶元素出栈,用x返回
bool Pop(SqStack &S,char x)
二、表达式求值
1、大家熟悉的算数表达式
波兰数学家的灵感
2、中缀、后缀、前缀表达式
3、中缀表达式转后缀表达式
中缀转后缀的手算方法:
①确定中缀表达式中各个运算符的运算顺序
②选择下一个运算符,按照「左操作数右操作数运算符」的方式组合成-一个新的操作数
③如果还有运算符没被处理,就继续②
私房菜:
“左优先”原则,不要FreeStyle,保证手算和机算结果相同
左优先”原则:只要左边的运算符能先计算,就优先算左边的(可保证运算顺序唯)
4、后缀表达式的计算
后缀表达式的手算方法:
从左往右扫描,每遇到-一个运算符,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数
注意:两个操作数的左右顺序
(1)代码实现
5、中缀表达式转前缀表达式
中缀转前缀的手算方法:
①确定中缀表达式中各个运算符的运算顺序
②选择下一个运算符,按照「运算符左操作数右操作数」的方式组合成一一个新的操作数
③如果还有运算符没被处理,就继续②
“右优先”原则:只要右边的运算符能先计算,就优先算右边的
6、知识点总结
7、中缀表达式转后缀表达式(计算机实现)
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
从左到右处理各个元素,直到末尾。可能遇到三种情况:
①遇到操作数。直接加入后缀表达式。
②遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”不加入后缀表达式。
③遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。(*/优先级高于±)
(1)普通的表达式
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。
扫描一个操作数则直接输出
扫描到符号因为现在栈为空
扫描到B是操作数直接输出
当扫描搭配-号的时候:遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。(*/优先级高于±)
弹出+号
然后将-号放入到栈当中
扫描到C是一个操作数,直接输出
扫描到*
的时候,栈当中的减号不能弹出,将*
放入到栈当中
扫描到D,D是操作数直接输出
当移动到/
的时候,将*
弹出栈
然后将/
入栈
继续往后是一个操作数可以往后直接输出
移动到+号时将优先级更高的/
号弹出栈
移动到+号时将优先级相等 的的-
号弹出栈
+
号入栈
最后将剩余的内容全部弹出
(2)带有界限符的表达式
遇到左括号直接入栈
移动到-号的时候
继续往后操作数直接输出
当遇到)
括号的时候先弹出-号,然后弹出(
括号
移动到-
号的时候将运算符优先级高于-
号和运算符等于-
号的元素依次弹出
/
优先级要比-
号更高直接将/
号入栈
然后将栈当中的数依次弹出加到后缀表达式当中
8、中缀表达式的计算(用栈实现)(中缀转后缀+后缀表达式求值【两个算法的结合】)
(1)实现方式一:用栈实现中缀表达式的计算:
初始化两个栈,操作数栈和运算符栈,
若扫描到操作数,压入操作数栈
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈
(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)
栈实现后缀表达式的求值
用于存放当前暂时还不能确定运算次序的操作数
(2)实现方式二:用栈实现中缀表达式的计算:
初始化两个栈,操作数栈和运算符栈若扫描到操作数,压入操作数栈。
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈。
(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)
9、知识回顾与重要考点
用栈实现中缀表达式转后缀表达式:
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
从左到右处理各个元素,直到末尾。可能遇到三种情况:
(1)遇到操作数。直接加入后缀表达式。
(2)遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”不加入后缀表达式。
(3)遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。用栈实现后缀表达式的计算:
(1)从左往右扫描下一个元素,直到处理完所有元素
(2)若扫描到操作数则压入栈,并回到(1);否则执行(3)
(3)若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到(1)
用栈实现中缀表达式的计算:
初始化两个栈,操作数栈和运算符栈若扫描到操作数,压入操作数栈
若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈
(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)
三、递归
1、函数调用背后的过程
函数调用的特点:最后被调用的函数最先执行结束((LIFO)
函数调用时,需要用一个栈存储
(1)调用返回地址
(2)实参
(3)局部变量
2、栈在递归当中的应用
适合用“递归”算法解决:可以把原始问题转换为属性相同,但是规模较小的问题。
Eg1:计算正整数的阶乘n!
Eg2:求斐波那契数列
递归表达式(递归体)
边界条件(递归出口)
#include<stdio.h>
//计算正整数 n!
int factorial(int n){
if(n == 0 || n == 1)
return 1;
else
return n*factorial(n-1);
}
int main(){
//...其他代码
int x = factorial(10);
printf("奥利给x:%d",x);
}
递归调用时,函数调用栈可称为“递归工作栈”
每进入一层递归,就将递归调用所需信息压入栈顶
每退出一层递归,就从栈顶弹出相应信息
Eg3:求斐波那契数列
#include<stdio.h>
//计算正整数 n!
int Fib(int n){
if(n==0)
return 0;
else if(n==1)
return 1;
else
return Fib(n-1) + Fib(n-2);
}
int main(){
//..其他代码
int x = Fib(4);
printf("奥利给!x=%d",x);
}
3、函数调用栈
函数调用的特点:最后被调用的函数最先执行结束(LIFO)
函数调用时,需要用一个函数“函数调用栈”存储
(1)调用返回地址
(2)实参
(3)局部变量
递归调用的时候,函数调用栈可称为“递归工作栈”每进入一层递归,就将递归调用所需要的信息压入栈顶每退出一层递归,就从栈顶弹出相应信息
缺点:效率低,太多层递归可能会导致栈溢出;可能包含很多重复的计算
可以自定义栈将递归算法改造成非递归算法
以上是关于C/C++语言数据结构快速入门(代码解析+内容解析)栈的应用的主要内容,如果未能解决你的问题,请参考以下文章
C/C++语言数据结构快速入门(代码解析+内容解析)栈的应用
C/C++语言数据结构快速入门(代码解析+内容解析)队列的应用