数据结构笔记(三)

 

数据结构——栈和队列

1、第三章:栈和队列

通过下面的思维导图来依次分享「栈和队列」里面重要知识点的笔记。

 

2、第一节:栈

1.  栈的定义:

(stack):只允许在一端进行插入或删除操作的线性表。

栈顶(Top):线性表允许进行插入和删除的那一端。

栈底(Bottom): 固定的,不允许进行插入和删除的另一端。

空栈:不含任何元素的空表。

栈的操作特性:后进先出(Last In First Out, LIFO),即后进入栈的元素先出栈。

2.  栈的基本操作

InitStack(&S): 初始化一个空栈S。

②StackEmpty(S): 判断一个栈是否为空,若栈S为空返回true,否则返回false。

③Push(&S,x): 进栈,若栈S未满,将x加入,使之成为新栈顶。

④Pop(&S,&x): 出栈,若栈S非空,弹出栈顶元素,并用x返回。

⑤GetTop(S,&x): 读栈顶元素,若栈S非空,用x返回栈顶元素。

⑥ClearStack(&S): 销毁栈,并释放栈S占用的存储空间。

3.  栈的顺序存储结构:

①顺序栈的实现:利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置。栈的顺序存储类型的描述为:

1#define MaxSize 50   //定义栈中元素的最大个数
2typedef struct{
3    Elemtype data[MaxSize];   //存放栈中元素
4    int top;   //栈顶元素
5} SqStack;

说明:

栈顶指针:S.top,初始时设置S.top = -1,

栈顶元素:S.data[S.top];

进栈操作:栈不满时,栈顶指针先加1,再送值到栈顶元素。

出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。

栈空条件:S.top==-1;

栈满条件:S.top==MaxSize-1;

栈长:S.top+1。

 

②顺序栈的基本运算的实现:

a.栈的初始化,如下:

1void InitStack(&S){
2    s.top = -1;   //初始化栈顶指针
3}

b.判断栈是否为空,如下:

1bool StackEmpty(S){
2    if(s.top==-1)   //栈空
3        return true;
4    else            //不空
5        return false;   
6}

c.进栈,如下:

1bool Push(SqStack &S,ElemType x){
2    if(S.top==MaxSize-1)   //栈满,报错
3       return false;
4    S.data[++S.top]=x;   //指针先加1,再入栈
5       return true;
6}

d.出栈,如下:

1bool Pop(SqStack &S,ElemType x){
2    if(S.top==-1)   //栈空,报错
3       return false;
4    x=S.data[S.top--];   //先出栈,指针再减1
5       return true;
6}

e.读栈顶元素,如下:

1bool GetTop(SqStack S,ElemType &x){
2    if(S.top==-1)   //栈空,报错
3       return false;
4    x=S.data[S.top];   //x记录栈顶元素
5       return true;
6}

 

③共享栈:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸,如下图:

说明:两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减1再赋值,出栈时刚好相反。

 

4.  栈的链式存储结构:采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行的,一般规则链栈没有头结点。

栈的链式存储类型可描述为,如下:

1typedef struct Linknode{
2    ElemType data;   //数据域
3    struct Linknode *next;   //指针域
4} *LiStack;   //栈类型定义

 

3、第二节:队列

1.  队列的定义:

队列(Queue):队列简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。

队头(Front):允许删除的一端,又称为队首。

队尾(Rear): 允许插入的一端。

空队列:不含任何元素的空表。

2.  队列的基本操作

InitQueue(&Q): 初始化队列,构造一个空队列Q。

②QueueEmpty(Q): 判断一个队列是否为空,若队列Q为空返回true,否则返回false。

③EnQueue(&Q,x): 入队,若队列Q未满,将x加入,使之成为新的队尾。

④DeQueue(&Q,&x): 出队,若队列Q非空,删除队头元素,并用x返回。

⑤GetHead(Q,&x): 读队头元素,若队头Q非空,则将队头元素赋值给x.

3.  队列的顺序存储结构:

①队列的顺序实现:是指分配一块连续的存储单元存放队列中的元素,并附设两个指针front和rear分别指示队头元素和队尾元素的位置。

②队列的顺序存储类型可描述为:

1#define MaxSize 50   //定义队列中元素的最大个数
2typedef struct{
3ElemType data[MaxSize];   //存放队列元素
4int front,rear;   //队头指针和队尾指针
5} SqQueues; 

说明:

初始状态(队空条件):Q.front==Q.rear==0。

进队操作:队不满时,先送值到队尾元素,再将队尾指针加1。

出队操作:队不空,先取队头元素值,再将队头指针加1。

 

4.  队列的链式存储结构:

①队列的链式表示:称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。

②队列的链式存储类型可描述为:

1typedef struct{   //链式队列结点
2    Elemtype data;
3    struct LinkNode *next;
4}LinkNode;
5typedef struct{   //链式队列
6    LinkNode *front,*rear;   //队列的队头和队尾指针
7}LinkQueue;

说明:当Q.front==NULL且Q.rear==NULL时,链式队列为空。出队时,首先判断对是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front指向下一个结点(若该结点为最后一个结点,则置Q.front和Q.rear都为NULL)。入队时,建立一个新结点,将新结点插入到链表的尾部,并改让Q.rear指向这个新插入的结点(若原队列为空,则令Q.front也指向该结点)。

③链式队列的基本操作

a.初始化,如下:

1void InitQueue(LinkQueue &Q){
2Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));  //建立头结点
3Q.front->next=NULL;   //初始为空
4}

b.判队空,如下:

1bool IsEmpty(LinkQueue Q){
2    if(Q.front==Q.rear)   return true;
3    else return false;
4}

c.入队,如下:

1void EnQueue(LinkQueue &Q,ElemType x){
2s=(LinkNode *)malloc(sizeof(LinkNode));
3s->data=x;    //创建新结点,插入到链尾
4s->next=NULL;   
5Q.rear->next=s;
6Q.rear=s;
7}

d.出队,如下:

 1bool DeQueue(LinkQueue &Q,Elemtype &x){
 2    if(Q.front==Q.rear)   return false;   //空队
 3    p=Q.front->next;
 4    x=p->data;
 5    Q.front->next=p->next;
 6    if(Q.rear==p)
 7       Q.rear=Q.front;   //若原队列中只有一个结点,删除后变空
 8    free(p);
 9    return true;
10}

 

4、第三节:栈和队列的应用

1.  栈在括号匹配中的应用:

例如:在下面的括号序列中

括号       [   (   [   ]   [   ]   )   ]

括号编号1  2  3  4   5  6  7  8

计算机接收第1个括号“[”后,期待与之匹配的第8个括号“]”出现。

获得了第2个括号“(”,此时第1个括号“[”暂时放在一边,而急迫期待与之匹配的第7个括号“)”的出现。

获得了第3个括号“[”,  此时第2个括号“(”暂时放在一边,而急迫期待与之匹配的第4个括号“]”的出现。第3个括号的期待得到满足,消解之后,第2个括号的期待匹配又成为当前最急迫的任务了。

依此类推,可见,该处理过程与栈的思想吻合。

算法的思想如下:

初始设置一个空栈,顺序读入括号。

若是右括号,则或者使置于栈顶的最急迫期待得以消解,或者是不合法的情况(括号序列不匹配,退出程序)。

若是左括号,则作为一个新的更急迫的期待压入栈中,自然使原有的在栈中的所有未消解的期待的急迫性降了一级。算法结束时,栈为空,否则括号序列不匹配。

2.  队列在计算机系统中应用:

①解决主机与外部设备之间速度不匹配的问题 :

以主机和打印机之间速变不匹配的问题为例,主机输出数据给打印机打印,输出数据的速度比打印数据的速度要快得多,由于速度不匹配,若直接把输出的数据送给打印机打印显然是不行的。

解决的方法是:设置一个打印数据缓冲区, 主机把要打印输出的数据依次写入到这个缓冲区中,写满后就暂停输出,转去做其他的事情。打印机就从缓冲区中按照先进先出的原则依次取出数据并打印,打印完后再向主机发出请求。主机接到请求后再向缓冲区写入打印数据。这样做既保证了打印数据的确,又使主机提高了效率。故打印数据缓冲区中所存储的数据就是一个队列。

②解决由多用户引起的资源竞争问题  :

CPU(即中央处理器,它包括运算器和控制器)资源的竞争为例,在个带有多终端的计算机系统上,有多个用户需要CPU各自运行自己的程序,它们分别通过各自的终端向最操作系统提出占用CPU的请求。操作系统通常按照每个请求在时间上的先后顺序,把它们排成个队列,每次把CPU分配给队首请求的用户使用。当相应的程序运行结束或用完规定的时间间隔后,则令其出队,在把CPU分配给新的队首请求的用户使用。这样既满足了每个用户的请求,又使CPU能够正常运行。

将自己的学习笔记通过本博客展现出来,也是对自己学习的一种记录。

             扫一扫,关注公众号

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值