Fork me on GitHub

堆栈与队列的实际应用

堆栈与队列的实际应用

 
堆栈和队列是最基本的两个ADT,简单但是重要。先讲堆栈在计算机中的应用。
 

堆栈:

 

1.用于符号匹配。

在编译器的语法检查中,一个过程就是检查各种括号是否匹配,比如 ([]) ,这就是匹配的,而 {[}] 就不匹配了。可以用堆栈来实现括号匹配。
具体算法如下:
 
建立一个空的堆栈。
while( 文件没有结束 ) {
读取一个字符。
if 遇到一个左括号,把它入栈。
else if 遇到右括号 then 检查堆栈,{
if 堆栈为空 then 报告错误,终止程序(括号不匹配)。
else if 堆栈非空 then {
if 栈顶不是对应的左括号 then 报错,终止程序。
弹出栈顶。
}
}
if 栈非空 then 报错。

  

2.用于计算代数式。( 也可以用二叉树来解决 )

 
如果我们要计算 6 + 4 * 8 ,要考虑到优先级的问题,这时候就可以用到堆栈了。
先要把代数式构造成 6 4 8 * + (构造方法也是用堆栈,在下一条会讲到)。逐个读取数据,当读到数字时, 把数字入栈,
读到运算符时,弹出栈中的两个元素(因为这里的是二元运算符,所以弹出两个,如果是sin等一元运算符就弹出一个),根据读取
的运算符执行运算,把结果压入栈中,然后继续读取数据,读取结束后栈顶元素就是结果。
比如读取6,4,8,由于是数字,所以依次入栈,
读到 "*" 时,弹出 4 和 8,相乘得到 32,把32入栈。读到 "+" 时,
弹出 6 和 32 ,执行运算得到 38,压入栈中,接着读取结束,栈顶的 38 就是结果。
 

3.构造表达式。( 也可以用二叉树来解决 )

 
比如一个正常的代数式(叫他infix), a + b * c + ( d * e + f ) * g , 转化成表达式 a b c * + d e * f + g * +, 这个表达式我们叫他 postfix。
把postfix按照 2 中的算法计算就能得到正确的计算顺序。
 
先规定优先级,加减的优先级最低,左括号优先级最高
创建一个字符串output储存 postfix。
创建一个空栈 operators。
while ( )
逐个读取 infix 中的元素,储存在 temp 中。

if temp 是数字(operand)then 把 temp 压入 output 字符串。
if temp 是除了右括号 ")" 之外的运算符(operator
if operators 栈空 then temp 入栈。
if operators 栈非空,
while ( 栈顶元素优先级大于或等于 temp 并且 栈顶元素不等于左括号 )
弹出栈顶元素到 output 。
if temp 是右括号 ")"
while ( 栈顶元素不等于左括号 )
弹出栈顶元素到 ouput.
弹出左括号, 但是不输出到 output
 
比如 a * ( b * c + d ),左边是堆栈中的情况,右边是输出
 
1.  输出 a,把 "*" 入栈                              
operators output

top * a

2. "(" 入栈,输出 b
operators output

top (
* a b

3. "*"入栈,输出 c
operators output


top * a b c
(
*
4. 读到 + 号,因为栈顶的 * 优先级大于 + 号,所以弹出栈顶到 output,也就是弹出 "*" ,并输出 "*"
operators output

top + a b c * d
(
*
5.读到 ")" 右括号,弹出左括号 "(" 上的所有运算符,并弹出左括号 "(" ,注意此时左括号没有输出
operators output

top * a b c * d +

6.最后所有元素依次出栈
operators output

top a b c * d + *

4.用于函数调用

 
因为CPU一次只能执行一个命令,而寄存器也是公用的,
当前函数 current() 在运行时,数据储存在寄存器中,如果要调用另外一个函数 target(),而target() 也要求使用寄存器,为了防止数据丢失并且在执行完 target()
能够返回到 current() 继续执行, 这时候就要把当前函数的重要数据储存起来,压入内存中的栈中( 包括变量的值和函数地址 )。这样target()函数就可以无所顾忌的使用寄存器了。
target() 函数执行结束就取栈顶的返回地址继续执行 current()。如果target()中又调用另外一个函数,相应的操作也是一样的。
 
这种机制和括号匹配有点相似,函数调用就像遇到了一个左括号,函数返回就像遇到一个右括号。
 
这种机制就是递归的原理。递归返回地址就是自己。这句话可能有问题,我就是这么理解的)。
 
栈的空间有限,如果递归没有结束条件,就会不断的压栈,然后栈溢出,程序出错。

 

队列:

 
当多个任务分配给打印机时,为了防止冲突,创建一个队列,把任务入队,按先入先出的原则处理任务。
当多个用户要访问远程服务端的文件时,也用到队列,满足先来先服务的原则。
……
有一个数学的分支,叫队列理论(queuing theory )。用来计算 预测用户在队中的等待时间,队的长度等等问题。
答案取决于用户到达队列的频率,用户的任务的处理时间。如果比较简单的情况,可以单单用分析解决。一个简单的例子就是,
一部电话,一个接线员。当一个电话打进来,如果接线员很忙,就电话放到等待线路后。接线员从队头开始应答。
 
如果有k个接线员,问题就变的复杂了。通常难以分析解决的问题就用模拟来解决。 可以用一个队列来模拟这个问题。如果k的值很大,
我们也可能需要其他数据结构来模拟问题。通过模拟,可以找到最小的 k 值,使队列满足一个合理的等待时间。
  
 
 
 
 
 
 

posted on 2012-03-10 12:24  pengyingh  阅读(16181)  评论(0编辑  收藏  举报

导航