【课上笔记】第三章 栈和队列

3.1栈

3.1.1栈的定义及基本运算

​ 栈是限制在表的一端进行插入和删除的线性表。允许插入、删除的这一端称为栈顶,另一个固定端称为栈底。当表中没有元素时称为空栈。

​ 栈又称为后进先出的线性表,简称LIFO表。

栈的基本操作

(1)栈初始化:Init_Stack(s)

​ 操作结果是构造了一个空栈。

(2)判栈空:Empty_Stack(s)

​ 操作结果是若s为空栈则返回1,否则返回0。

(3)入栈:Push_Stack(s,x)

​ 操作结果是在栈s的顶部插入一个新元素x,x成为新的栈顶元素。

(4)出栈:Pop_Stack(s)

​ 在栈s存在且非空的情况下,操作结果是将栈s的顶部元素从栈中删除。

(5)读栈顶元素:Top_Stack(s)

​ 在栈s存在且非空情况下,操作结果是读取栈顶元素,栈不变化。

3.1.2栈的存储实现和运算实现

1.顺序栈

​ 栈中的数据元素用一个预设的足够长度的一维数组来实现:

datatype data[MAXSIZE]

​ 栈底位置可以设置在数组的任一个端点,而栈顶是随着插入和删除而变化的,用一个int top来作为栈顶的指针,指明当前栈顶的位置,将data和top封装在一个结构中,顺序栈的类型描述如下:

​	#define MAXSIZE 1024typedef struct{datatype data[MAXSIZE];int top;}SeqStack;
定义一个指向顺序栈的指针:	SeqStack  *s;

​ 通常0下标端设为栈底,这样空栈时栈顶指针top=-1;入栈时,栈顶指针+1,即s->top++;出栈时,栈顶指针减1,即s->top–。

​ (1)置空栈

首先建立栈空间,然后初始化栈顶指针。

SeqStack *Init_SeqStack(){
SeqStack *s;
s=new SeqStack;//申请空间
if(!s)
{
cout<<"空间不足"<<endl;
return NULL;//未申请到足够大的空间,返回空指针
}
else
{
s->top=-1;//初始化栈顶指针
return s;//申请到栈空间,返回栈空间地址
}
}

(2)判空栈

int Empty_SeqStack(SeqStack *s){
if(s->top==-1)
    return 1;//栈顶指针指向栈底,空栈
else
    return 0;
}

(3)入栈

int Push_SeqStack(SeqStack *s,DataType x){
    if(s->top==MAXSIZE-1)
        return 0;//栈满不能入栈,返回错误代码0
    else{
        s->top++;//栈顶指针向上移动
        s->data[s->top]=x;//将x置入新的栈顶
        return 1;//入栈成功,返回成功代码1
    }
}

(4)出栈

int Pop_SeqStack(SeqStack *s,DataType *x){
    if(Empty_SeqStack(s))
        return 0;//栈空不能出栈,返回错误代码0
    else{
        *x=s->s->data[s->top];//保存栈顶元素值
        s->top--;//栈顶指针向下移动
        return 1;//返回成功代码1
    }
}

(5)取栈顶元素

DataType Top_SeqStack(SeqStack*s){
    if(Empty_SeqStack(S)) 
        return 0;//栈空没有栈元素,返回错误代码0
    else
        return s->data[s->top];//返回栈顶元素值
}

需要说明的几点:

​ 1.对于顺序栈,入栈时,首先判栈是否已满,栈满时,不能入栈;否则出现空间溢出,引起错误,这种现象称为上溢。

​ 栈满的条件为:s->top==MAXSIZE-1

​ 2.出栈和读栈顶元素操作,先判栈是否为空,为空时不能操作,否则产生错误。通常栈空时常作为一种控制转移的条件。

2.链栈

​ 通常链栈用单链表表示,因此其结点结构与单链表的结构相同:

typedef struct node

{

​	datatype data;struct node*next;

}StackNode,*LinkStack;

​	说明top为栈顶指针:LinkStack top;

​ 因为栈中的主要运算是在栈顶插入、删除,显然在链表的头部做栈顶是最方便的,而且没有必要像单链表那样为了运算方便附加一个头结点。

(1)置空栈

LinkStack Init_LinkStack(){
    return NULL;//返回栈顶(空指针)
}

(2)判栈空

int Empty_LinkStack(LinkStack top){
    if(top==NULL)
        return 1;//栈顶指针为空,空栈
    else
        return 0;
}

(3)入栈

LinkStack Push_LinkStack(LinkStack top,DataType x){
    StackNode *s;
    s=new StackNode;//申请新的栈顶结点空间
    s->data=x;//将元素值置入结点数据域
    s->next=top;//原栈顶结点作为新结点后继
    top=s;//将新节点置为栈顶
    return top;
}

(4)出栈

LinkStack Pop_LinkStack(LinkStack top,DataType *x){
    StackNode *p;
    if(top==NULL)
        return NULL;//栈空不能出栈,返回空指针
    else
    {
        *x=top->data;//保存栈顶元素值
        p=top;
        top=top->next;//置新的栈顶指针
        delete p;//释放原栈顶元素节点空间
        return top;//出栈成功,返回新的栈顶指针
    }
}

3.2队列

3.2.1队列的定义及基本运算

​ 在实际问题中经常使用一种”先进先出“的数据结构:即插入在表一端进行,删除在表另一端进行,这种数据结构称为队或队列,把允许插入的一端叫队尾(rear),把允许删除的一段叫队头(front)。

​ 队列也是一种运算受限制的线性表,又叫先进先出表。

​ 在队列上进行的基本操作有:

(1)队列初始化:Init_Queue(q)

​ 操作结果是构造一个空队。

(2)入队操作:In_Queue(q,x)

​ 在队q存在的情况下,操作结果是对已存在的队列q,插入一个元素x到队尾。

(3)出队操作:Out_Queue(q,x)

​ 队q存在且非空的情况下,操作结果是删除队首元素,并返回其值。

(4)读队头元素:Front_Queue(q,x)

​ 队q存在且非空的情况下,操作结果是读出队头元素,并返回其值。

(5)判队空操作:Empty_Queue(q)

​ 队q存在时,操作结果是若q为空则返回1,否则返回0.

3.2.2队列的存储实现及运算实现

1.顺序队:顺序存储的队称为顺序队

​ 因为队的队头和队尾是活动的,因此,除了队列的数据区外还要有队头、队尾两个指针。

​ 顺序队的类型定义如下:

define MAXSIZE ...//根据实际需要设置队列的最大容量

typedef struct

{

datatype data[MAXSIZE];/*队员的存储空间*/

int rear,front;/*队头队尾指针*/

}SeQueue;



定义一个指向队的指针变量:

SeQueue *sq;

相关说明:

·申请一个顺序队的存储空间:

sq=new SeQueue;

·队列的数据区为:sq->data[0]---sq->data[MAXSIZE-1]

·队头指针:sq->front

·队尾指针:sq->rear

·设队头指针指向队头元素前面一个位置,队尾指针指向队尾元素(这样的设置是为了某些运算的方便,并不是唯一的方法)。

·置空队则为:sq->front=sq->rear=-1;

·在不考虑溢出的情况下,入队操作队尾指针加1,指向新位置后,元素入队。操作如下:

​	sq->rear++;

​	sq->data[sq->rear]=x;

·在不考虑队空的情况下,出队操作队头指针加1,表明队头元素出队。操作如下:

​	sq->front++;

​	x=sq->data[sq->front];/*原队头元素送x中*/

·队中元素的个数:m=(sq->rear)-(sq->front);

·队满时:m=MAXSIZE; 队空时:m=0;

但是,随着入队出队的进行,会使整个队列整体向后移动,就出现了这样现象:队尾指针已经移到了最后,再有元素入队就会出现溢出,而事实上此时队中并未真的“满员”,这种现象为“假溢出” 。

解决假溢出的方法之一是将队列的数据区data[0… MAXSIZE-1]看成头尾相接的循环结构,头尾指针的关系不变,将其称为“循环队列”。

​ 因为是头尾相接的循环结构,入队时的队尾指针加1操作修改为:

sq->rear=(sq->rear+1)%MAXSIZE;

​ 出队时的队头指针加1操作修改为:

​	sq->front=(sq->front+1)%MAXSIZE;

所以在队满的情况下有front=rear。但是在队空的情况下也有front==rear。

那么我们怎么解决这个问题呢?

​ 方法之一就是附设一个存储队中元素个数的变量如num,当num==0时队空,当num ==MAXSIZE时为队满。

​ 另一种方法是少用一个元素空间,,这种情况下队满的条件是(rear+1)%MAXSIZE==front

循环队列的类型定义如下(采用前面的方法一区分队空和队满)

 typedef  struct     

{    datatype data[MAXSIZE];      /*数据的存储区*/int front,rear;         /*队头队尾指针*/int  num;                /*队中元素的个数*/    

}c_SeQueue;          /*循环队*/

⑴ 置空队

C_SeQueue *Init_SeQueue(){
    C_SeQueue q;
    q=new C_SeQueue;//申请循环队列存储空间
    q->front=q->rear=MAXSIZE-1;//队首、队尾指针置初值
    q->num=0;//队列数据元素个数置初值
    return q;
}

⑵ 入队

int In_SeQueue(C_SeQueue *q,DataType x){
    if(q->num==MAXSIZE)
    {
        cout<<"队满"<<endl;
        return 0;//队满不能入队,返回错误代码0
    }
    else
    {
        q->rear=(q->rear+1)%MAXSIZE;//队尾指针后移
        q->data[q->rear]=x;//将新数据元素插入队尾
        q->num++;//队列数据元素个数增1
        return 1;//入队完成,返回成功代码1
    }
}

⑶ 出队

int Out_SeQueue(C_SeQueue *q,DataType *x){
    if(q->num==0)
    {
        cout<<"队空"<<endl;
        return 0;//队空不能出队,返回错误代码0
    }
    else
    {
        q->front=(q->front+1)%MAXSIZE;//队首指针后移
        *x=q->data[q->front];//读出队首元素保存
        q->num--;//队列数据元素个数减1
        return 1;//出对完成,返回成功代码1
    }
}

⑷ 判队空

int Empty_SeQueue(C_SeQueue *q)
{
    if(q->num==0)
        return 1;//队列中数据元素个数为0,队空
    else
        return 0;
}

2:链队:链式存储的队称为链队

和链栈类似,用单链表来实现链队,根据队的FIFO原则,为了操作上的方便,我们分别需要一个头指针和尾指针。

头指针front和尾指针rear是两个独立的指针变量,从结构性上考虑,通常将二者封装在一个结构中。

链队的描述如下:

typedef struct node  

 { datatype  data;struct  node *next;     

}QNode;          /*链队结点的类型*/  

typedef struct    

{  

QNnode  *front, *rear;     

}LQueue;         /*将头尾指针封装在一起的链队*/

定义一个指向链队的指针:  

LQueue  *q;

(1)创建一个带头结点的空队

LQueue *Init_LQueue()
{
    LQueue *q;
    QNode *p;
    q=new LQueue;//申请头尾指针结点
    p=new QNode;//申请链队首结点
    p->next=NULL;
    q->front=q->rear=p;//首尾指针均指向头结点
    return q;
}

(2)入队

void In_Queue(LQueue *q,DataType x){
    QNode *p;
    p=new QNode;//申请新结点
    p->data=x;
    p->next=NULL;
    q->rear->next=p;//将新结点插入队尾
    q->rear=p;//队尾指针指向新的结点
}

(3) 判队空

int Empty_LQueue(LQueue *q)
{
    if(q->front==q->rear)
        return 1;//队首队尾指向同一结点(头结点),队空
    else
        return 0;
}

(4) 出队

int Out_LQueue(LQueue *q,DataType *x)
{
    QNode *p;
    if(Empty_LQueue(q)){
        cout<<"队空"<<endl;
        return 0;
    }
    else{
        p=q->front->next;//p指向队首结点
        q->front->next=p->next;//队首指针指向新的队首结点
        *x=p->data;//取出原队首数据元素值保存
        delete p;//释放原队首结点空间
        if(q->front->next==NULL)
           q->rear=q->front;//只有一个元素时,出队后队空,修改队尾指针
        return 1;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值