本文数据结构讲解参考书目:
通过网盘分享的文件:数据结构 C语言版.pdf
链接: https://pan.baidu.com/s/159y_QTbXqpMhNCNP_Fls9g?pwd=ze8e 提取码: ze8e
个人主页:樱娆π-CSDN博客
队列的表示和操作的实现
队列的类型定义
ADT Queue {数据对象:D={ai l ai 含于ElemSet ,i=1,2,…, n ,n>=0}
数据关系:R={ <i-1,ai> | ai-1,ai含于D ,i=2, …, n}
约定其中a1端为队列头,an端为队列尾。
基本操作:
}
基础操作
基本操作 | 初始条件 | 操作结果 |
InitQueue (&Q) | / | 构造一个空队列Q |
Des t royQueue (&Q) | 构造一个空队列Q | 队列Q被销毁, 不再存在 |
ClearQueue (&Q) | 队列Q已存在 | 将Q清为空队列 |
QueueEmpty (Q) | 队列Q已存在 | 若Q为空队列,则返回true, 否则返回false |
QueueLength(Q) | 队列Q已存在 | 返回Q的元素个数,即队列的长度 |
GetHead(Q} | Q为非空队列 | 返回Q的队头元素 |
EnQueue (&Q, e} | 队列Q已存在 | 插入元素e为Q的新的队尾元素 |
DeQueue(&Q, &e) | Q为非空队列 | 删除Q的队头元素,并用e 返回其值从队头到队尾,依次对Q的每个数据元素访问 |
QueueTraverse(Q) | Q巳存在且非空 | 从队头到队尾,依次对Q的每个数据元素访问 |
循环队列一一队列的顺序表示和实现
和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队 列头到队列尾的元素之外,尚需附设两个整型变最 front 和 rear分别指示队列头元素及队列尾元素 的位置(后面分别称为头指针和尾指针)。
//----- 队列的顺序存储结构-----
#define MAXQSIZE 100
typedef struct
{
QElemType *base;
int front;
int rear;
} SqQueue;
假设当前队列分配的最大空间为 6, 则当队列处于如图所示的状态时不可再继续插入新的队尾元素,否则会出现溢出现象, 即因数组越界而导致程序的非法操作错误。 事实 上,此时队列的实际可用空间并未占满,所以这种现象称 为 “假溢出"。
这是由 "队尾入队,队头出队” 这种受限制 的操作造成的。 怎样解决这种 “假溢出” 问题呢? 一个较巧妙的办法是 亡 Q.rear 将顺序队列变为一个环状的空间,如图所示,称之为循环队列。
问:对于循环队列不能以头、尾指针的值是否相同来判别队列空间是 “满 ” 还是 “空 ”。 在这种情况下, 如何区别队满还是队空呢?
少用一个元素空间, 即队列空间大小为m时,有m-1个元素就认为是队满。这样判断队 空的条件不变, 即当头、 尾指针的值相同时, 则认为队空;而当尾指针在循环意义上加1后是等 千头指针, 则认为队满。 因此, 在循环队列中队空和队满的条件是:
队空的条件: Q.front = Q.rear
队满的条件: (Q rear+ 1)%M心CQSIZE = Q.front
循环队列的初始化
循环队列的初始化操作就是动态分配一个预定义大小为 MAXQSIZE 的数组空间
【算法步骤】
- 为队列分配一个最大容量为 MAXSIZE 的数组空间, base指向数组空间的首地址
- 头指针和尾指针置为零, 表示队列为空
【算法实现】
Status InitQueue (SqQueue &Q)
{//构造一个空队列Q
Q.base=new QElemType[MAXQSIZE] //为队列分配一个最大容扯为 MAXSIZE 的数组空间
if(!Q.base) exit(OVERFLOW); //存储分配失败
Q.front=Q.rear=O; //头指针和尾指针置为零, 队列为空
return OK;
}
求队列长度
对于非循环队列,尾指针和头指针的差值便是队列长度,而对千循环队列,差值可能为负数, 所以需要将差值加上MAXQSIZE, 然后与MAXQSIZE求余
【算法实现】
int QueueLength(SqQueue Q)
{//返回Q的元素个数, 即队列的长度
return(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
循环队列的入队
入队操作是指在队尾插入一个新的元素
【算法步骤】
- 判断队列是否满,若满则返回ERROR
- 将新元素插入队尾
- 队尾指针加1
【算法实现】
Status EnQueue (SqQueue &Q, QElemType e)
{//插入元素 e 为 Q 的新的队尾元素
if ((Q. rear+l) %MAXQSIZE==Q. front) //尾指针在循环意义上加1后等于头指针, 表明队满
return ERROR;
Q.base[Q.rear]=e; //新元素插入队尾
Q.rear=(Q.rear+l)%MAXQSIZE; //队尾指针加1
return OK;
}
循环队列的出队
出队操作是将队头元素删除
【算法步骤】
- 判断队列是否为空, 若空则返回ERROR
- 保存队头元素
- 队头指针加1
【算法实现】
Status DeQueue (SqQueue &Q, QElemType &e)
{//删除Q的队头元素, 用 e 返回其值
if(Q.front==Q. rear) return ERROR; //队空
e=Q.base[Q.front]; //保存队头元素
Q.front=(Q.front+1)%MAXQSIZE; //队头指针加1
return OK;
取循环队列的队头元素
当队列非空时, 此操作返回当前队头元素的值, 队头指针保持不变
【算法实现】
SElemType GetHead(SqQueue Q)
{//返回Q的队头元素,不修改队头指针
if(Q. front! =Q. rear) //队列非空
return Q.base[Q.front]; //返回对头的元素的值,对头指针不变
}
链队——对列的链式表示和实现
链队的操作即为单链表插入和删除操作的特殊情况, 只是需进一步修改尾指针或头指针。 下 面给出链队初始化、 入队、 出队操作的实现,具体如下:
//----- 队列的链式存储结构-----
typedef struct QNode {
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct
{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
链队的初始化
链队的初始化操作就是构造一个只有一个头结点的空队
【算法步骤】
- 生成新结点作为头结点, 队头和队尾指针指向此结点
- 头结点的指针域置空
【算法实现】
Status InitQueue (LinkQueue &Q)
{//构造一个空队列 Q
Q.front=Q.rear=new QNode;
Q.front->next=NULL;
return OK;
}
链队的入队
和循环队列的入队操作不同的是,链队在入队前不需要判断队是否满,需要为入队元素动态 分配一个结点空间
【算法步骤】
- 为入队元素分配结点空间,用指针p指向
- 将新结点数据域置为e
- 将新结点插入到队尾
- 修改队尾指针为p
【算法实现】
Status EnQueue (LinkQueue &Q, QElemType e)
{//插入元素e为Q的新的队尾元素
p=new QNode; //为人队元素分配结点空间,用指针p指向
p->data=e; //将新结点数据域置为e
p->next=NULL; Q. rear->next=p; //将新结点插入到队尾
Q.rear=p; //修改队尾指针
return OK;
}
链队的出队
和循环队列一样,链队在出队前也需要判断队列是否为空,不同的是,链队在出队后需要释 放出队头元素的所占空间
【算法步骤】
- 判断队列是否为空,若空则返回ERROR
- 临时保存队头元素的空间,以备释放
- 修改队头指针,指向下一个结点
- 判断出队元素是否为最后一个元素,若是,则将队尾指针重新赋值, 指向头结点
- 释放原队头元素的空间
【算法实现】
Status DeQueue(LinkQueue &Q,QElemType &e)
{//删除Q的队头元素, 用e返回其值
if(Q.front==Q.rear) return ERROR;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p) Q.rear=Q.front;
delete p;
return OK;
}
取链队的队头元素
与循环队列一样,当队列非空时,此操作返回当前队头元素的值,队头指针保持不变
【算法实现】
SElemType GetHead{LinkQueue Q)
{//返回Q的队头元素, 不修改队头指针
if(Q.front!=Q.rear) //队列非空
return Q.front->next->data; //返回队头元素的值,队头指针不变
}
总结
(1)栈是限定仅在表尾进行插入或删除的线性表,又称为后进先出的线性表。栈有两种存储 表示,顺序表示(顺序栈) 和链式表示(链栈)。栈的主要操作是进栈和出栈,对千顺序栈的进栈 和出栈操作要注意判断栈满或栈空。
(2) 队列是一种先进先出的线性表。它只允许在表的一端进行插入, 而在另一端删除元素。 队列也有两种存储表示,顺序表示(循环队列)和链式表示(链队)。队列的主要操作是进队和出 队,对于顺序的循环队列的进队和出队操作要注意判断队满或队空。凡是涉及队头或队尾指针的 修改都要将其对MAXQSIZE求模。
(3) 栈和队列是在程序设计中被广泛使用的两种数据结构,其具体的应用场景都是与其表示 方法和运算规则相互联系的。
栈 | 队列 | |
逻辑结构 | 和线性表一样,数据元素之间存在一对一的关系 | 和线性表一样,数据元素之间存在一对一的关系 |
存储结构 | 顺序存储: 存储空间预先分配,可能会导致空间闲置或栈满溢出现象;数据元素个数不能自由扩充 链式存储: 动态分配, 不会出现闲置或栈满溢出现象;数据元素个数可以自由扩充 | 顺序存储(常设计成循环队列形式): 存储空间预先分配,可能会导致空间闲置或队满溢出现象;数据元素个数不能自由扩充 链式存储: 动态分配, 不会出现闲置或队满溢出现象;数据元素个数可以自由扩充 |
运算规则 | 插入和删除在表的一端(栈顶)完成,后进先出 | 插入运算在表的一端(队尾)进行,删除运算在表的另一端(队头), 先进先出 |
————由于博主还是大三的在读生,时间有限,每天会不定时更新一些学习经验和一些32的项目,如果喜欢就点点关注吧,大佬们!!!!————