数据结构学习笔记之栈与队列的经典应用

栈与队列的经典应用

  • 一、栈的经典应用
    • 1、括号匹配
      • 1.1、算法
    • 2、表达式求值
    • 3、递归栈
  • 二、队列的经典应用
    • 1、队列在二叉树的层次遍历应用
    • 2、队列在计算机系统中的应用

一、栈的经典应用

  • 主要介绍常见的三个:括号匹配、表达式求值和递归函数的递归栈

1、括号匹配

  • 在算术表达式中,括号的出现是非常普遍的,以圆括号和方括号为例,如何判断一个括号序列是否合法呢?当然是判断括号序列从最深层的左括号到最外层的左括号,在序列中是否都能找到另一半即右括号,当匹配完成后括号序列中没有剩余的左括号或右括号,那么这个括号序列便是合法的。
  • 我们在匹配括号时,往往是从括号序列的中间为起点,左括号往左遍历依次与中间位置右边半部分开始匹配,也就是说假设括号序列为2n,则合法的匹配序列是:n与n+1构成封闭括号,n-1与n+2 构成封闭括号……1 与 2n 构成封闭括号
  • 因此,我们不妨将括号序列从中间为准划分为两个部分,前部分依次入栈,根据栈的先进后出规律,最先入栈的第一个左括号,也就是优先级最低的处于栈底,而栈顶的是最后输入的括号,也就是优先级最高——最急于找到匹配的右括号。然后,后半部分括号序列不用入栈,而是与在栈中的括号进行匹配检测,当与栈顶括号匹配,则弹栈,进行后一次匹配。
  • 当然我这是出于叙述需要,进行特殊化,实际上括号序列的前半部分不可能都是左括号的,而是可能包含一组封闭括号的情况。
  • 所以最一般的做法是整个括号序列根据是否是左括号决定入栈?右括号是否与上一个括号匹配决定弹栈和判断括号序列是否合法?
  • 因此算法归纳如下:

1.1、算法

  • 1) 创建一个空栈,顺序读入括号序列。
  • 2)当输入的括号是否是序列中的第一个括号,是的话且如果是左括号则压栈,是的话且是右括号则直接断定括号序列不合法,终止算法。
  • 3)在非第一个括号入栈时,如果是左括号则入栈;如果是右括号,则判断与栈顶的括号是否匹配,若匹配则右括号不入栈,且将栈顶括号弹出;如果是右括号,且与栈顶括号不匹配,则该括号序列为非法序列,终止算法。
  • 4)整个括号序列已经遍历完成,且栈中无剩余未匹配的左括号,则说明括号序列合法;反之,括号序列遍历完成,但是栈中有未得到匹配的左括号,则括号序列是不合法的。
  • 该算法最核心步骤就是用右括号与栈顶进行匹配操作,左括号入栈。

2、表达式求值

  • 这里的表达式主要指算术表达式中的后缀表达式。我们日常所用的算术表达式,是一种中缀表达式,后缀表达式的运算符都在操作数后面,已经考虑了运算符的优先级,没有括号,只有操作数和运算符;而中缀表达式则是需要进行括号处理和运算符优先级的处理。请看例子:
    中缀表达式:A+B*(C-D)-E/F
    后缀表达式:ABCD-*+EF/-
    
  • 用后缀表达式计算表达式值的算法如下:
  • 1)顺序扫描表达式的每一项,根据其类型做相应操作。
  • 2)若当前项为操作数,则进行入栈;
  • 3)若当前项为操作符,则从栈中依次弹出两个操作数,与操作符进行运算,再将运算结果压入栈中。
  • 4)扫描完表达式所有的项并处理完后,栈顶存放的就是最后的运算结果。

3、递归栈

  • 递归,指的是在一个函数、过程或数据结构的定义中又应用了自身,则称该函数、过程或数据结构是递归定义的,简称递归。
  • 这里以递归函数的递归栈为例,典型的递归函数有求斐波那契数列,定义如下:
    在这里插入图片描述
  • 递归模型不能是循环定义的,必须有出口,所以必须满足:
  • 递归表达式,即递归体。
  • 边界条件,即递归出口。
  • 在递归函数运行过程中,系统为每一层的返回点、局部变量、传入参数等开辟了递归工作栈来进行数据存储。因此,递归次数最大值与计算机的配置有关,递归次数太大超过了系统分配给进程的递归栈则会形成栈溢出;同时由于递归调用过程中包含很多重复的计算,因此效率不高。
  • 递归算法可以借用栈,开发者自己设计工作栈,将递归算法转变称非递归算法。
  • 以求斐波那契数列为例,转为非递归算法。
  • 1)根据传入参数 n>=2 的值确定循环次数,跳出循环的条件是 i<= n
  • 2)在循环体内进行反复的压栈和弹栈操作,具体如下,先以 10顺序往栈中压入值1和0;当 i=2 时按计算规则计算F(1)+F(0),即从栈中弹出两个操作数进行加法运算,将运算结果和F(1),按照F(2)、F(1) 压入栈中,此时栈顶是F(2),栈底是F(1);当 i=3 时,弹出 F(2) 、F(1) 加法计算得出 F(3) ,后以 F(3)、F(2) 顺序压入栈……依次类推,总之每次计算后保留当前结果和上一次运算的结果。
  • 当 i=n 时,弹出 F(n-1) 、 F(n-2) 加法计算,将结果 F(n) 和 F(n-1) 以 F(n-1)、F(n) 的次序依次压入栈后,结束循环。
  • 此时栈中存放着 F(n) 和F(n-1),而栈顶的F(n) 就是所求的值。
  • 总体来说,这个过程的栈大小只是存放两个元素,空间复杂度是常数阶O(2),而时间复杂度则与 n 的规模有关。这样一来,便不会发生栈溢出的问题了。

二、队列的经典应用

  • 主要介绍队列在二叉树的层次遍历中的使用已经在计算机系统里的应用。

1、队列在二叉树的层次遍历应用

  • 对二叉树进行层次遍历就是,从树的根节点往树叶按层次顺序依次遍历,其规则如下:
  • ① 根节点入队。
  • ② 若队空,则结束遍历;否则重复步骤③。
  • ③ 队列中的第一个结点出队,并访问之。若其有左子节点,则将左子节点入队;若其有右子节点,则将右子节点入队,并返回②。
    数据结构学习笔记之栈与队列的经典应用_第1张图片

2、队列在计算机系统中的应用

  • 在对设备与主机间的速度不匹配的问题和多用户的资源竞争问题这两个方面,计算机系统应用了队列来解决问题。
  • 例如,打印机的打印队列。由于打印机的打印速度远比主机的处理速度慢,因此在主机和打印机直接开辟一个打印数据缓冲区,并按照队列的模式设计,送往打印机的数据按照先来后到的次序写入缓冲区,当缓冲区写满时主机便可以暂时其处理其他事务;而打印机则按照先进先出的原则依次从缓冲池中取出数据进行打印,当打印缓冲池中的数据后向主机反馈,若此时打印任务未完成,则继续往缓冲池写入数据,否则让打印机空闲。
  • 在资源竞争方面,CPU 的竞争是个典型。在多用户操作系统中,操作系统将每个请求使用CPU的请求按照时间先后,排列成队;每次把 CPU 分配给队首请求的用户使用,当相应的程序运行结束或者时间片用完,则令其出队,出队后是为完成任务的则插入到队尾。

你可能感兴趣的:(数据结构学习笔记)