栈和队列

(Stack)是限制在表的一端进行插入和删除运算的线性表,通常称插入、删除的这一端为栈顶(Top),另一端为栈底(Bottom),先进后出。当top=-1时,为空栈;当top=0时,栈中只有一个元素,并且元素进栈时top应该自增。

  1. 顺序存储栈:顺序存储结构。
  2. 链栈:链式存储结构。插入和删除操作仅限在链头位置上进行。栈顶指针就是链表的头指针,通常不会出现栈满的情况,不需要判断栈满但需要判断栈空
  3. 两个栈共用静态存储空间,对头使用也存在空间溢出问题。栈1的底在V[1],栈2的底在V[m],则栈满的条件是top[1]+1=top[2]。
  4. 基本操作:删除栈顶元素、判断栈是否为空以及将栈置为空栈等。
  5. 对于n个元素的入栈问题,可能的出栈顺序有C(2n,n)/(n+1)个。
  6. 堆栈溢出一般是循环的递归调用、大数据结构的局部变量导致的。

栈的应用

  1. 进制的转换

  2. 括号匹配的检验

  3. 行编辑程序

  4. 迷宫求解:若当前位置“可通”,则纳入路径,继续前进;若当前位置“不可通”,则后退,换方向继续探索;若四周“均无通路”,则将当前位置从路径中删除出去。

  5. 表达式求解:前缀、中缀、后缀。

    • 操作数之间的相对次序不变;
    • 运算符的相对次序不同;
    • 中缀式丢失了括弧信息,致使运算的次序不确定;
    • 前缀式的运算规则:连续出现的两个操作数和在它们之间且紧靠它们的运算符构成一个最小表达式;
    • 后缀式的运算规则:运算符在式中出现的顺序恰为表达式的运算顺序,每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。
  6. 实现递归:多个函数嵌套调用的规则:后调用先返回。

    不是所有的递归程序都需要栈来保护现场,例如阶乘,是单向递归,直接用循环去替代从1乘n就是结果。另外,一些需要栈保存的也可以用队列等来替代。不是所有的递归转化为非递归都要用到栈。转化为非递归主要有两种方法:对于尾递归或单向递归,可以用循环结构来代替。

  7. 浏览器历史记录,Android中的最近任务,Activity的启动模式,CPU中栈的实现,Word自动保存,解析计算式,解析xml和json。解析xml时,需要校验结点是否闭合,结点闭合的话,有头尾符号相对应,遇到头符号将其放入栈中,遇到尾符号时,弹出栈的内容,看是否有与之对应的头符号,栈的特性刚好符合匹配的就近原则。

队列

队列(Queue)也是一种运算受限的线性表,它只允许在表的一端进行插入,而在另一端进行删除,允许删除的一端称为队头(front),允许插入的一端称为队尾(rear),先进先出

  1. 顺序队列:顺序存储结构。当头尾指针相等时,队列为空。在非空队列里,头指针始终指向队头前一个位置,而尾指针始终指向队尾元素的实际位置。
  2. 循环队列:在循环队列中进行出队、入队操作时,头尾指针仍要加1,超前移动。当头尾指针指向向量上界(MaxSize-1)时,其加1操作的结果是指向向量的下界0。除非向量空间真的被队列元素全部占用,否则不会上溢。因此,除一些简单的应用之外,真正实用的顺序队列是循环队列。故队空和队满时头尾指针均相等,因此,不能通过front=rear来判断队列空还是满。
  3. 链队列:链式存储结构,限制仅在表头删除和表尾插入的单链表。仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。
  4. 设尾指针的循环链表表示队列,则入队和出队的时间复杂度均为O(1)。用循环链表来表示队列,必定有链表的头结点,入队操作在链表尾插入,直接插入在尾指针指向的结点后面,时间复杂度是常数级的;出队操作在链表表头进行,也就是删除表头指向的结点,时间复杂度也是常数级的。
  5. 队空条件:rear==front,但是一般需要引入新的标记来说明栈满还是栈空,比如每个位置的布尔值。
  6. 队满条件:(rear+1)%QueueSize == front,QueueSize为循环队列的最大长度。
  7. 计算队列的长度:(rear-front+QueueSize)%QueueSize。
  8. 入队:(rear+1)%QueueSize。
  9. 出队:(front+1)%QueueSize。
  10. 假设以数组A[N]为容量存放循环队列的元素,其头指针是front,当前队列有X个元素,则队列的尾指针值为 (front+X mod N)。
作者:凯旋.Lau
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/KX-Lau/p/12383346.html