第三章:栈和队列
1.栈的应用
1.1括号匹配
我们在数学运算中 [(A+b)*c] - (E-F)
往往都会有[ ]
和 ( )
来表示运算的优先级,我们把这样的[ ]
和 ( )
提取出来组成的序列叫做括号匹配序列
。
匹配序列
( [ ( ) ] )
[ ] [ ] ( )
( ) [ ( ) ]
上面是正确的可以匹配的序列,每一个(
或者[
都有与之对应的)
或者]
。
不匹配序列
( [ ( ) ]
] [ ] ( )
( ] [ ( ) ]
下面是一个正确匹配的序列,我们给每一个标上号,一次输入到栈中,判断阔号是否匹配。
算法思想:
- 1.初始化一个空栈,顺序读入阔号
- 2.若是右阔号,则与栈顶元素进行匹配
-
- 若匹配,则弹出栈顶元素进行下一个元素
-
- 若不匹配,则该序列不合法
- 3.若是左阔号,则压入栈中
- 4.如果全部元素遍历完毕,栈中非空则序列不合法
1.2表达式求值
例如:A+B+C-E-F
像这种简单的表达式求值我们通过遍历,加上if
判断就可以解决,但是[(A+B)*C]-(E-F)
这种比较复杂的表达式这就有一些复杂的优先级了。这就需要栈来解决了,下面介绍几种表达式的分类。
在解决表达式求值的问题我们通常要将中缀表达式
转成前缀表达式
或者转成后缀表达式
,不过最常用的还是转成后缀表达式
。常常用后缀表达式计算表达式的值。
1.3中缀表达式转前&后缀
中缀转前缀&后缀
[(A+B)*C]-(E-F)
我们按照运算的优先级首先计算(A+B)讲它转成前缀:+AB,[+AB*C]-(E-F)
接着乘以C转成前缀为:* +ABC,这样第一个方括号的转换完毕,接着按照运算优先级,将[E-F]转成前缀:-EF,现在表达式为:* +ABC-(-EF)
,接着将中间的-
提到最前面得:-*+ABC-EF
前缀表达式:- * + A B C - E F
过程和上面得过程相同,只不过将运算符号号放后面就行。
后缀表达式:A B + C * E F - -
使用栈将中缀转后缀得算法思想:
( ( A + B ) * C ) - ( E - F )
- 数字直接加入后缀表达式
- 运算符时:
- a.若为
(
,入栈;.b - b.若为’
)
,则依次把栈中的运算符加入后缀表达式,直到出现(
,并从栈中删除(
- c.若为
+
,-
,*
,/
-
- 1.栈空,入栈;
-
- 2.栈顶元素为
(
,入栈;
- 2.栈顶元素为
-
- 3.高于栈顶元素优先级,入栈;·
-
- 4.否则,依次弹出栈顶运算符,直到一个优先级比它低的运算符或
(
为止;`
- 4.否则,依次弹出栈顶运算符,直到一个优先级比它低的运算符或
- d.遍历完成,若栈非空依次弹出所有元素。
使用此思想执行一遍将表达式装成后缀:
为了方便文字描述,定义一个H
作为后缀表达式,定义一个Z
栈。
首先( (
依次入栈(规则:a),此时Z=( (
,接着数字A,直接加入后缀表达式中此时H=A
接着+
入栈(规则:c 2),此时Z=*((+
接着B直接加入后缀表达式中:H=AB
,接着)
入栈符合规则b,则此时Z=(
,H=AB+
,接着*
入栈,Z=(*
,接着)
入栈符合规则b,则此时Z=空
,H=AB+C*
,接着-(
依次入栈,此时Z=-(
,接着E
,直接加入后缀表达式:H=AB+C*E
,接着-
入栈,此时Z=-(-
接着F直接加入后缀表达式中,H=AB+C*EF
,接着)
入栈符合规则b,则此时z=-
,H=AB+C*EF-
,然后符合规则d,此时z=空
,H=AB+C*EF- -
。
2.递归
递归 若在一个函数、过程或数据结构的定义中又应用了它自身,则称它为递归定义的,简称递归
常见得递归例子:
斐波那契数列:0,1,1,2,3,5…
此数列从第三项开始每一项等于前两项得和。
使用C实现该递归过程
int Fib(int n){//n是第几个斐波那契数
if(n==0){
return 0;
}else if(n==1){
return 1;
}else if(){
return Fib(n-1)+Fib(n-2);
}
}
递归得精髓在于能否将原始问题转换为属性相同但规模较小的问题。
2.1递归产生的问题
- 在递归调用过程中,系统为每一层的返回点、局部变量、传入实参等开辟了递归工作栈来进行数据存储,递归次数过多容易造成栈溢出。
- 通常情况下递归的效率并不高
比如上述的斐波那契数列函数,假如我们传入5
,则整个过程为:
我们会发现,这当中我们发现一些相同结果我们会重复调用很多遍,这样的重复调用会造成递归的效率不高的问题。
在递归算法转换成非递归算法时,往往需要借助栈来进行。
理木客
欢迎关注公众号理木客
,坚持原创知识分享,更多精彩等你发现,期待你的关注