从一张思维导图来进入这块知识。
栈的应用
进制转换
当一个十进制数N转换成八进制数时,计算过程中,把N与8求余得到的八进制数的各位依次进栈,计算完毕后将栈中的八进制依次出栈,输出结果就是待求得的八进制数。(这里顺序存储和链式存储都可以使用)
下面来看一下它的过程:
1.初始化空栈
2.十进制数N非0时,循环以下操作:
(1)N与8求余,余数入栈;
(2)N更新为N与8的商
3.栈非空时,循环以下操作:
(1)弹出栈顶元素
(2)输出e。
下面是代码实现
void conversion(){
InitStack(S);//构造空栈
scanf("%d",&N);
while(N){
Push(S,N%8);
N=N/8;
}
while(!StackEmpty(S)){
Pop(S,e);
printf("%d",e);
}
}
括号匹配
借助一个栈,每读入一个左括号,则直接入栈,等待相匹配的右括号;每次读入一个右括号,若与当前栈顶的左括号类型相同,将栈顶中的左括号出栈,直到表达式扫描完毕。
我们来模拟以下这个过程:
比如下面是我们要扫描的括号
{[()]}
第一步:将{入栈
第二步:还是左括号继续入栈
第三步:还是左括号继续入栈,所以现在栈中是{[(
第四步:遇到右括号),发现它与栈顶的左括号匹配,弹出,剩下操作一样。
详细步骤:
1.初始化空栈
2.判断是否有括号未处理(栈空),有则循环以下操作:
(1)左括号则入栈;
(2)栈顶元素出栈,右括号则看跟栈顶元素是否匹配,匹配则继续循环,否则错误匹配跳出循环
3.循环结束,若栈空则匹配成功。
下面是具体的代码实现:
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);
}
表达式求值
在了解表达式求值前,我们需要先了解几个东西。
比如一个表达式是:b - (a + 5) * 3
那么,如果将它们转换成前缀和后缀,我们这里看到的,或者比如a+b的,我们将它成为中缀表达式,而前缀就是运算符在两个操作数前面,比如:+ab。后缀表达式就是运算符在两个操作符后面,比如ab+。
当运算顺序不唯一,中缀转后缀的表达式也不唯一。
那么我们来举个例子看下一个中缀表达式如何转为后缀表达式。
比如:A+B (C-D)- E/F。
初始化一个运算符栈,用于保存在那时还不能确定运算顺序的运算符。
从左到右处理中缀表达式各个元素,
1.遇到操作符,直接加入后缀表达式
2.遇到界限符"(“直接入栈,遇到”)“则依次弹出栈内运算符并加入后缀表达式,直到弹出”("为止。
3.遇到运算符,依次弹出栈中优先级高于或等于当前运算的所有运算符,并加入后缀表达式。
来模拟一下上面这个式子的过程。
1.一开始是A,所以直接加入后缀表达式,后缀表示内容:A
2.遇到“+”号,加入符号栈。所以符号栈和后缀表达式内容入下图。
3.遇到B加入到后缀表达式。
4.遇到“ * ”,因为“+”的优先级比它低,所以也加入符号栈,现在符号栈和后缀表达式内容如下图。
5.遇到“(”,加入符号栈。
6.遇到C加入后缀表达式内容
7.遇到“-”,加入符号栈
8.遇到D加入后缀表达式,这时候的符号栈和后缀表达式入下图。
9.这时候检查到了“)”,所以将“-”出栈加入后缀表达式内容,直到将左括号也出栈。这时候符号栈和后缀表达式内容入下。
10.这时候遇到了符号“-”,但是符号栈中是“ * ”,所以将“ * ”出栈,并且“+”和“-”是同级,所以“+”也出栈,并且将“-”压入栈。得到符号栈和后缀表达式入下图。
11.将E放入后缀表达式内容
12.将“/”放入符号栈
13.将F放入后缀表达式内容
14.将符号栈的内容依次弹出得到最终结果。ABCD-+EF/-
栈的应用——递归
其实递归就是用到了栈。我们先来看一串代码:
void main(){
int a,b,c;
func1(a);
}
void func1(int a){
int x;
fun2(x);
}
void func2(int x){
int m,n;
}
这里引入一个概念,函数调用时,最后被调用的函数最先执行结束(LIFO)。比如上面代码,先调用main,那么把main压入栈,main里调用func1,那么把func1压入栈,func1里调用func2,再把func2压入栈。所以得到的栈的图如下:
有很多用到递归的都是调用了栈,比如计算阶乘,代码如下:
int f(int n){
if(n==0 || n==1)
return 1;
else retur n*f(n-1);
}
int main(){
int x = f(5);
}
这里把n写的小了点,方便画图操作。那么开始模拟吧。
1.调用f(5),那么把f(5)入栈。
2.f(5)里面调用了f(4),把f(4)入栈。
3.f(4)里面调用了f(3),把f(3)入栈
4.f(3)里面调用了f(2),把f(2)入栈
5.f(2)里面调用了f(1),把f(1)入栈
6.这时候f(1)开始返回。可以看下面这张图。
所以返回给f(2)后,f(2)=2 * 1,f(2)给f(3)之后,f(3)=3 * 2 * 1,依次返回直到f(5)。所以这样就计算出5的阶乘啦。
队列的应用
队列的应用在思维导图中有三个,树的层次遍历,和图的广度优先,这里就不详述,想要了解可以翻阅我其他关于树和图的文章即可。还有一个操作系统中的应用就是进程争夺临界资源的时候,有一个先来先服务的,比如打印机只有这个人的打印完才能打印下一个的,是需要排队的,需要了解先来先服务的可以去我操作系统相关文章了解。
总结
虽然说这篇文章是说栈与队列的应用,其实上更多,不对是都在讲栈的应用,队列的应用很多出现在其他篇章中,所以可以翻阅其他文章。如果本文中有什么错误或者理解不当的地方,欢迎指出。