数据结构万字总结(超级详细)第三章——栈和队列

栈和队列

定义

只允许在一端进行插入或删除操作的线性表

操作

初始化、进栈、出栈、销毁栈等

特征

  • 后进先出(非常重要的特征)

实现

  • 顺序栈

    顺序存储,用静态数组实现,并记录栈顶指针。顺序栈的缺点:栈的大小不可变

  • 共享栈

    两个栈共享同一片内存空间,两个栈从两左边往中间增长

    • 初始化

      0号栈栈顶指针初始时top0 = -1; 1号栈栈顶指针初始时top1 = MaxSize;

    • 栈满条件

      top0+1 == top1

  • 链栈

    • 头插法建立单链表对应进栈,单链表的删除操作对应:出栈。都是只对第一个元素进行操作

若有n个元素进栈,则出栈顺序有1/(n+1) * C(2n,n),其中C(2n,n)为组合公式

队列

定义

只允许在一端进行插入,在另一端进行删除的线性表

操作

增、删、查、改、初始化、销毁

特征

  • 先进先出(非常重要的特征)

循环队列

  • 注意:我们默认循环队列的front指针指向当前元,和rear指针都是指向下一个地址。默认牺牲一个存储单元不存放元素

  • 确定判空的条件

    Q.rear==Q.front

  • 确定判满的条件

    (Q.rear+1)%MaxSixe==Q.front

  • 队列元素个数

    (rear + maxSize - front) % maxSize

  • 出队操作

    Q.front=(Q.front+1)%MaxSize

  • 入队操作

    Q.rear=(Q.rear+1)%MaxSize

  • 注意:循环队列,题目没有说明的情况下,默认使用牺牲一个存储单元的方法。出队、入队、和求队列元素个数都要模maxSize。其中尤其要注意求队列元素,因为rear-front可能会存在负值,要加上maxSize再与maxSize取模

  • 如何有效利用所有存储单元

    • 牺牲一个存储单元,这是默认使用的一种方式

    • 增加辅助变量size,则队空:size=0,队满:size=maxSize

    • 增加一个标记变量tag,每次删除操作成功时,都令tag=O;每次插入操作成功时,都令tag=1。只有删除操作,才可能导致队空只有插入操作,才可能导致队满

      初始化时 rear=front=0;tag = 0;

      队空条件: front==rear && tag ==0

      队满条件: front==rear && tag ==1

双端队列

  • 定义

    只允许从两端插入、两端删除的线性表

  • 分类

    • 输入受限的双端队列

      只允许从一端插入、两端删除的线性表

    • 输出受限的双端队列

      只允许从两端插入、一端删除的线性表

  • 如何判断输入受限的双端队列的合法顺序

    看输出的第一个元素,比如输入顺序时1、2、3、4,而输出顺序是4、2、1、3,这显然不对,因为第一个元素是4,说明队列中的顺序是1,2,3,4了,输出4以后不可能先输出中间的2,如下,输出4以后只能输出1或3。简记:输入受限,看第一个元素。(根据第一个元素的值可以知道已经进去了多少元素,从而可以推导出序列的合法性)如下图:

  • 如果4第一个出队,那么说明队列里的元素已经是1、2、3、4.则4输出以后不可能先输出2,所以以4、2开头的序列都是非法的

  • 如何判断输出受限的双端队列的合法顺序

    根据输出顺序推倒输入顺序,比如输入顺序是1、2、3、4,但是一个输出顺序是4、1、3、2,这显然是不对的,因为1先进入,2后进入,1的旁边一定有2,所以该序列非法。输出受限的双端队列是比较好推导的。简记:输出受限:逆推导(根据输出序列推导输入序列是否合法),如下图:

 

根据双端队列输出受限的特点,1进入队列以后,2会接着进入队列,因此不管怎么样,2都在1的旁边,要么在左边,要么在右边,因此不可能出现2在1的隔壁。所以4,、2、3、1是非法序列。 

他们俩都有一个相似点:如果输出没有受限,只看输出的合法性,不用关心输入的过程。如果输入没有受限,就看输入的合法性,不用关心输出的过程。

栈和队列对比

顺序栈、顺序队列

  • 扩展容量不方便

  • 存储密度高

链栈、链队列

  • 一般不会出现容量不足的问题

  • 存储密度稍低

栈的应用:

1、括号匹配

依次扫描所有字符,遇到左括号入栈,遇到右括号则弹出栈顶元素检查是否匹配

  • 匹配失败的三种情况:

    左括号单身、有括号单身、左右括号不匹配

2、表达式求值

  • 逆波兰表达式又叫后缀表达式 波兰表达式又叫前缀表达式

  • 后缀表达式求值

    • 中缀转后缀

      1、按左优先原则确定运算符的运算次序

      2、根据运算次序,依次将各个运算符和与之相邻的两个操作数按<左操作数、右操作数、运算符>的规则合体

    • 后缀转中缀

      从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算合体为一个操作数。先弹出的为右操作数

    • 计算,按左优先原则,先弹出的是右操作数

      用栈实现后缀表达式的计算:

    • ①从左往右扫描下一个元素,直到处理完所有元素

    • ②若扫描到操作数则压入栈,并回到①;否则执行③

    • ③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①

  • 前缀表达式求值

    • 中缀转前缀

      ①确定中缀表达式中各个运算符的运算顺序

    • ②选择下一个运算符,按照<运算符,左操作数,右操作数的>,组合成一个新的操作数 ③如果还有运算符没被处理,就继续② “右优先”原则:只要右边的运算符能先计算,就优先算右边的

    • 计算,按右优先原则,先弹出的为左操作数

      从右往左扫描下一个元素,直到处理完所有元素②若扫描到操作数则压入栈,并回到①;否则执行③ ③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①

3、递归、图的深度优先遍历

队列的应用:

树的层次遍历

图的广度优先遍历

操作系统分配资源(先来先服务算法)

打印数据缓冲区

特殊矩阵在一维数组中的下标计算

只需要计算出当前元素前面有几个元素即可。但是很多时候有的元素是a00开头,有的是a11开头。甚至更复杂的行以0开头,列以1开头。 我们只需要记住一点,以a11开头的,aij前面有i-1行,j-1个元素。即aij前面有(i - 1)* 列数 + j-1

以a00开头的,aij前面有i行,j个元素。即aij前面有 i* 列数 + j

不管哪种方式开头,只需计算出aij前面有K个元素, 一般情况下默认存在一维数组的下标从0开始存放,则当前坐标为a[K],如果以1开始存放则下标为 a[ K+1 ]。从M开始存放,则下标为a[M+K]

可能上面的叙述有点混乱,没关系,只要看两个题你就懂了:

 比如一个m*n的二维矩阵,下标以A11开头:

如果把这些元素按行存储方式存入一维数组,则Aij应该存在一维数组的哪个下标?

首先我们知道矩阵是以 1开头,则Aij前面有i-1行(比如,A23前面就有1行),j-1个元素(比如A23前面有2个元素),故A23前面一共有n*1+2个元素。

现在我们已经计算出Aij前面有K个元素了,如果数组下标默认是从0开始,则A23存入的下标就是n*1+2,如果一维数组下标从1开始,则则A23存入的下标就是n*1+2+1。更一般的,如果一维数组下标从M开始,则则A23存入的下标就是n*1+2+M

如果矩阵以A00开头:

首先我们知道矩阵是以 0开头,则Aij前面有i行(比如,A21前面就有2行),j个元素(比如A21前面有1个元素),故A23前面一共有n*2+1个元素。

现在我们已经计算出Aij前面有K个元素了,如果数组下标默认是从0开始,则A21存入的下标就是n*2+1,如果一维数组下标从1开始,则则A21存入的下标就是n*2+1。更一般的,如果一维数组下标从M开始,则则A23存入的下标就是n*2+1+M

稀疏矩阵

定义

在矩阵中,若数值0的元素数目远多于非0元素的数目,并且非0元素分布没有规律

存储方式

  • 三元组表(行、列、值)

  • 十字链表

特殊矩阵

对称矩阵

三角矩阵

三对角矩阵

稀疏矩阵

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值