0
点赞
收藏
分享

微信扫一扫

Programming Abstractions in C阅读笔记:p331-p337

佳简诚锄 2024-03-27 阅读 13

前言:

思维导图介绍:

开始之前,先介绍一下这篇博客主要介绍的主体内容:

栈和队列:

回顾线性表:

那现在就让我们进入正题吧——

基本定义:

栈和队列都属于一种限制出入的线性表,不过它们所限制的方式不同

属于限制线性表先进入到表里的最后才可以获取到——也就是“先进后出”,类比装在弹夹里的子弹,先装进去的最后才打出来。

队列是限制——一端是进入表中的,一端是出表的,讲究"先进先出"的规则,这个其实可以类比排队的 ,先排队的总是可以先进行活动,而一端来人,一端出人。

使用案例:

进制转换

这里的主要是在十进制转化为其余进制而使用的——对于十进制转化为二、八、十六进制,它们都有一个基本的共识——除R取余(这里的R指转化成进制的2、8、16)

而对于这个取余,我们一般都是倒着取,也就是——先除R得到的那个余数在最后一位。

这个很明显的符合我们刚才介绍的栈的特性——先进后出。

因此栈可以在这个进制转换中使用,具体使用方法等介绍完栈的知识再做讲解


括号的匹配

假如给你几个括号,让你判断它们是否都配对成功了。

例如:{([])}这个就是配对成功了,{)[(}而这个显然不是

那么如何解决这一道题目呢——使用栈

这里先基本介绍一下使用方法——

先把一半的括号插入到栈中,然后再插另一半,插一个的时候要判断,是否和栈中的括号匹配,如果不匹配就直接报错,如果匹配则对应的括号出栈,直到所有元素出栈则可以说明所有的括号都匹配成功


表达式求值

对于一个简单的表达式,由于符号的优先级不同,则运算数据也不同,而利用表达式求值可以使用栈的方法,这个设计的算法比较复杂,在讲解完栈后再讲解。


舞伴问题

假如要你要开办一个舞会,舞会上让男女配对跳舞,则男生和女生站两列,她们位置一致则匹配成功,排在前面的,就先和对应的搭档开始跳舞,如果有一对的人比较多,配对不成,则他就成为下一首歌的第一个和异性匹配并跳舞的人。

栈(stack):

定义:

具有先进后出限制的线性表。

有顺序栈和链栈两种形式。

创建栈:

顺序栈的创建——

设置一个结构体,里面存放两个指针——一个指向栈顶,一个指向栈尾。和一个数组大小

构建基本代码:

typedef struct {
    Stackelem *top;
    Stackelem *base;
    int stacksize;
}Sqstack;

注意这里的Stackelem是指放在栈中的元素类型 

两种溢出形式

对于栈而言,有两种溢出形式:

一般而言上溢出只发生在顺序栈中,而链栈由于可以自己手动分配空间,基本不存在上溢出的问题。

1.上溢出,入栈元素过多,超过栈的最大空间,则发生错误,上溢出。

2.下溢出,一直出栈,但是栈里已经没有元素了,因此发生错误,下溢出。

但是,一般把上溢出看为错误,而把下溢出看为一种结束条件

满栈和空栈:

那么如何判断这个栈是否满?或者是否为空?

这里画一个图:

如图所示,这是一个基本的顺序栈,而在这个栈中,初始状态——也就是base和top指向第一个位置,这时候栈内为空,而当top所指向的空间是开辟空间最后一个了,那么栈满了。

那现在理论解释明白了,代码该怎么表示呢?

——

对于栈空:当top==base时栈为空

对于栈满:当top-base==stacksize时栈满

栈空和栈满在下面的一些基本操作中也会用到,所以这里先提前介绍一下如何判断栈空或是栈满的情况。

顺序栈

基本操作:

初始化

代码:

销毁栈

销毁栈,就是需要把栈所占的空间给释放掉,而刚开始开辟的时候是动态开辟,这里就用到delete关键字,这个关键字作用和c语言中的free一样,都是释放掉动态开辟的内存,直接delete base,然后把top和base指针置空,栈长=0即可。

代码:

置空栈

把栈中元素置空,而不管空间的事,这个操作就直接让top=base即可,这样top和base之间的元素为0,置空栈操作成功

入栈

假如栈为空则——入栈就是先把top指针的位置给赋值成传进来的值,然后把top指针往上移一位。

出栈

设定一个变量,把top下面位置存放的数据赋值给这个变量,然后top指向这个位置即可

判断是否为空

上面已经介绍了如何判断栈是否为空了,就是当top和base相等时栈为空。

代码:

链栈

构建:

链栈和顺序栈的最大区别就是物理结构不同,顺序栈用到了c++中的数列,而链栈要用到前面的链表,在链表中存放数据和指针。

这意味之前的结构体构建到这里不能用了,因此这里介绍一下新的构建:

结构体里要包含数据域和指针域

 

🎄注意:链表的头指针就是栈顶,这个链栈没有头结点,而这个一般不会溢出——出现栈满的情况。

基本操作:

初始化:

对于链栈的初始化就和顺序栈的有很大区别了,向初始化函数传入头指针,让头指针置为空,即头指针=NULL即可——这里会有人问了,为什么让头指针置为空?不应该是让指针所指的next(指针域)为空吗?

其实,这里要注意一点——链栈的链表没有头结点

🎄头指针和头结点不一样,头结点初始化是要把头结点的next域置为空,而头指针是直接把自己=NULL,置为空,因为指针所占用的空间是存储所指的位置的,指针位置置为空,则对于这个链栈的初始化来说——完成。

入栈

入栈就是先开辟一块空间,空间类型为自建的结构体类型,然后让这块空间的指针域指向头指针所指向的空间,然后让头指针指向新开辟的空间——这样就可以只借助一个头指针来完成栈的操作了

出栈

出栈就是先提取出来要出栈的数据域的值,然后让头指针指向出栈的下一个,最后释放掉出栈的空间。

队列(queue):

定义:

队列是一种限制进出的线性表,“先进先出”(FIFO),在生活中这个性质比较常见些,比如排队买饭……

而队列是限制一端进,一端出,每一端都有不同的操作。

常见应用:

顺序队列

构建表示:

上图就展示了一个队列拥有的基本特征,其中的rear是指向队尾,front是指向队头。

rear端进行进入队列操作,而front端进行出队列操作,所以这里可以引入——如何判断队列为空:

当front和rear指向同一位置时,队列为空。

 那顺序队列的类型构建该怎么构建呢?

示例代码如下:

#define MAXQSIZE 100
typedef struct{
    QElemType *base;//动态分配存储空间
    int rear;
    int front;
}SQueue;

这里写的的rear尾指针和front头指针为int类型,这时候会有些疑惑——不是说这个它们是指针吗?怎么写成整型的形式?

这是因为这里顺序队列所借指的是数组,而且不是动态分配的数组?所以数组的下标和指针有着一样的作用,则可用整型代表更安全。

一般顺序队列

操作:
初始化:
入队: 

入队,就是让入队的数据赋值给rear指针指向的区域,再让指针++

即:

出队:  

简易代码:

❀BUT

当我们执行这些操作直到队列满,在空间内,我们发现还有一些空间没有利用,这块空间是出队列时front往上移动而产生的结果,那这块空间我们能不能再利用呢?就像排队一样,排完队的人出队后,后面的人就会自动往前补上。

答案是——当然可以。

下面就介绍队列中比较常用的顺序队列——循环队列

循环队列

这个队列的设置是为了解决假上溢出的较好的方法,那什么是假上溢呢?其实刚才就提到了——当出队后使头指针下的位置为空,而队列内——即头指针和尾指针之间的位置已经满了,如果再入队则会导致溢出,BUT这种溢出被称为假溢出,因为所开辟的空间内还有未用的存储单元,所以解决办法就是循环队列——

将队列设想为一个循环的表:

在这样一个表中可以重复利用开辟的存储空间,当rear=maxqsize时,假如开始端空着,则可以从头利用空间,而当front=maxqsize时也可以如此,这便称为循环队列

操作:

那如何实现这循环队列呢?即当rear+1=maxqsize ,则令rear=0;

——

利用取余符号%来实现一个圈的循环

让rear=rear%maxqsize,当rear<maxqsize时rear仍=rear,当rear=maxqsize时rear=0

同理front也是。

插入元素:

那循环队列如何插入元素?

删除元素:
队空:

如何判断循环队列为空?

q.rear==q.front

队满:

那如何判断队满呢?

思考一下发现队满也是q.rear==q.front

那如何区分所谓的队空和队满呢?

方法如下:

  • 1.把循环队列空出一格出来,意思是少用一个元素空间
  • 2.设置一个变量,记录元素的个数来判断区分队满和队空
  • 3.设置一个标志区分队空和队满

而最常用的就是少用一块空间,

这样:

当空队时front==rear

当队满时(rear+1)%MAXQSIZE==front

那这个式子为什么用%符号了?——因为循环队列,不清楚到底rear大还是front大,rear指向队列中空出的区域,当rear+1就是可能和front相等,也可能和front差一个MAXQSIZE,那用取余符号列出的式子就可以概括这两种情况,完成判断队满的操作了。

具体的操作:
初始化:
 求队列长度:

如上图,我们列出了它们分别的计算式和结果,计算式的本质式分别为:rear-front,rear-front+Maxqsize,rear-front+Maxqsize

那么现在是想怎么把这两个式子合为一个——取余符号

即rear-front+MAXQSIZE再整体取余MAXQSIZE

入队:

出队:

链队

基本操作:

注意:front为头指针,它指向头结点,而头结点中没有data数据域。

类型定义:

初始化链队:

传入LinkQueue自定义类型,并动态开辟一块空间,空间类型为Qnode。front和rear指向同一块空间

入队:
出队:
销毁: 

总结:

举报

相关推荐

0 条评论