zoukankan      html  css  js  c++  java
  • 数据结构:四 栈和队列

    1. 栈的定义

    栈(stack):是限定仅在表尾进行插入和删除操作的线性表

    栈顶(top)允许插入和删除的一端称为栈顶;另一端称为栈底(bottom)

    空栈:不包含任何数据元素的栈

    栈又被称为后进先出(Last In First Out)的线性表,简称 LIFO 结构

    栈的插入操作,叫做进栈,也称压栈,入栈

    栈的删除操作,叫做出栈,也有的叫作弹栈

    2. 栈的抽象数据类型

    ADT 栈(stack)
    
    Data
        同线性表。元素具有相同的类型,相邻元素具有前驱和后堆关系。
    
    Operation
        InitStack ( *S ):初始化操作.建立一个空栈S。
        DestroyStack ( *S ):若栈存在,則销毁它。
        ClearStack (*S):将栈清空。
        StackEmpty ( S ):若栈为空,返回true,否則返回 false。
        GetTop (S,*e):若栈存在且非空,用e返回S的栈顶元素。
        Push (*S,e):若栈S存在,插入新元素e到栈S中并成为栈頂元素。
        Pop (*S,*e):删除栈S中栈顶元素,并用e返回其值。
        StackLength (S):返回回栈S的元素个数。
    
    endADT

     

    3. 栈的顺序存储结构及实现

    定义一个 top 变量来指示栈顶元素在数组中的位置

    进栈操作

    • 栈顶指针加一
    • 将新插入元素赋值给栈顶空间
    •  

    出栈操作

    • 将要删除的栈顶元素赋值给 e
    • 栈顶指针减一

    进栈和出栈都未涉及循环语句,时间复杂度为 O(1)

     

    4. 两栈共享空间

    数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为 0 处,另一个栈为栈的末端,即下标为数组长度 n-1 处

    这样,两个栈如果增加元素,就使两端点向中间延伸

    关键思路

    • 它们是在数组的两端,向中间靠拢
    • top1 和 top2 是栈 1 和栈 2 的栈顶指针

     

    5. 栈的链式存储结构及实现

    存储结构

    • 把栈顶放在单链表的头部,已经有了栈顶在头部了,单链表中常用的头结点就失去了意义
    • 通常对于链栈来说,是不需要头结点的
    • 对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是 top=NULL 的时候
    •  

    进栈操作

    • 假设元素值为 e 的新结点是 s,top 为栈顶指针
    • 插入元素 e 为新的栈顶元素
    • 把当前的栈顶元素赋值给新结点的直接后继,如图中 1
    • 将新的结点 s 赋值给栈顶指针,如图中 2

    出栈操作

    • 假设变量 p 用来存储删除的栈顶结点,将栈顶执政下移一位,最后释放 p 即可
    • 将栈顶结点赋值给 p ,如图 3
    • 使得栈顶指针下移一位,指向后一节点,如图 4
    • 释放结点 p

    链栈的进栈 push 和出栈 pop,时间复杂度均为 O(1)

    顺序栈,需先确定一个固定的长度,可能会存在内存空间浪费问题;优势是存取时定位方便

    链栈,要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制

    选择

    • 如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈
    • 反之,如果他的变化在可控范围内,建议使用顺序栈会更好一些

     

    6. 栈的作用

    栈的引用简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心

     

    7. 栈的应用——递归

    递归定义

    • 一个直接调用自己或通过一系列的调用语句间接地调用自己的函数

    • 每个递归定义必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出

    • 迭代和递归的区别

      • 迭代使用的时循环结构,不需要反复调用函数和占用额外的内存
      • 递归能使程序更清晰,更简洁,更易理解,从而减少读懂代码的时间
      • 大量的递归调用会建立函数的副本,会耗费大量的时间和内存

     

    8. 栈的应用——四则运算表达式求值

    后缀(逆波兰)表示法定义

    • 一种不需要括号的后缀表示法
    • 所有的符号都是在要运算数字的后面出现
    • ”9+(3-1)*3+10/2“ 的后缀表达式:”9 3 1 - 3 * + 10 2 / +“

    后缀表达式计算结果

    • 后缀表达式:9 3 1 - 3 * + 10 2 / +

    • 规则

      • 从左到右遍历表达式的每个数字和符号,遇到数字就进栈;遇到符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果

    中缀表达式转后缀表达式

    • ”9+(3-1)*3+10/2“,平时所用的这种标准四则运算表达式,叫做中缀表达式

    • 中缀转后缀规则

      • 从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;
      • 若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先于加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止
    • 中缀转化为后缀,栈用来进出运算的符号

    • 后缀转化为中缀,栈用来进出运算的数字

     

    9. 队列的定义

    队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表

    队列是一种先进先出(First In First Out)的线性表,简称FIFO

    允许插入的一端称为队尾,允许删除的一端称为队头

     

    10. 队列的抽象数据类型

    ADT 队列(Queue)
    
    Data
        同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
    
    Operation
        InitQueue(*Q):初始化操作,建立一个空队列Q。
        DestroyQueue(*Q):若队列Q存在,則销毀它。
        ClearQueue(*Q):将队列 Q 清空。
        QueueEmpty(Q):若队列Q为空,送回true,否則退回false。
        GetHead(Q, *e):若队列Q存在且非空,用e返因队列Q的队头元素。
        EnQueue(*Q,e):若队列Q存在,插入新元素e到队列Q中并成为队尾元素。 
        DeQueue(*Q, *e):刪除队列Q中队头元素,并用e返回其值。    
        QueueLength(Q):送回队列Q的元素个教。
    
    endADT

     

    11. 循环队列

    不足

    • 为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,所以引入两个指针
    • front 指针指向队头元素,rear 指针指向队尾元素的下一个位置
    • 当 front 等于 rear 时,此队列不是还剩一个元素,而是空队列

    定义

    • 我们把队列的头尾相接的顺序存储结构称为循环队列

    问题:当 front 等于 rear 时,如何判断此时队列是空还是满?

    • 办法一

      • 设置一个标志变量 flag,当 front == rear,且 flag = 0时为队列空,当 front == rear,且 flag = 1 时为队列满
    • 办法二

      • 当队列孔时,条件就是 front = rear,当队列满时,我们修改其条件,保留一个元素空间。
      • 也就是说,队列满时,数组中还有一个空闲单元
      • 若队列的最大长度为 QueueSize ,队列满的条件是:(reat + 1) % QueueSize == front

     

    12. 队列的链式存储结构及实现

    队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,简称链队列

    为了操作上的方便,我们将队头指针指向链队列的头结点

    入队操作

    • 在链表尾部插入结点
    • 把拥有元素 e 新结点 s 赋值给原队尾结点的后继,如图 1
    • 把当前的 s 设置为队尾结点,rear 指向 s,如图 2

    出队操作

    • 出队操作时,就是头结点的后继结点出队,将头结点的后继改为它后面的结点
    • 若链表除头结点外只剩一个元素时,则需将 rear 指向头结点

  • 相关阅读:
    神经网络的数学推导
    矩阵乘法的梯度计算
    深入理解设计矩阵(Design Matrix)
    拉格朗日乘子
    PRML中文版(马春鹏)勘误表
    增强学习笔记 第三章 马尔科夫决策过程
    贝叶斯统计推断的阅读笔记
    Kalman Filter的数学推导
    线性代数随笔(二):矩阵和向量乘法
    线性代数随笔(一):线性变换,特征分解,二次型
  • 原文地址:https://www.cnblogs.com/dc2019/p/13663631.html
Copyright © 2011-2022 走看看