数据结构与算法(9)—链队
2、链队
2.1、链队的存储结构
队列的链接实现称为链队,链队实际上是一个同时带有头指针和尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点即单链表的最后一个结点。
队列以链表形式出现,链首结点为队头,链尾结点为队尾。
队头指针为LQ->front,队尾指针为LQ->rear,队头元素的引用为Q->front->data,队尾元素的引用为LQ->rear->data.
初始化时,设置LQ->front=LQ->rear=NULL.
进队操作,与链表中链尾插入操作一样:出队操作,与链表中链首删除操作一样。
队空的条件为LQ->FRONT==NULL.
2.3、链队列基本函数算法描述
注:以下程序在VC6.0+WIN98下测试通过。
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct QNode{
ElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LQueue;
/*队列初始化*/
void InitQueue(LQueue *LQ)
{
LQ->rear=LQ->front=NULL;
}
/*入队*/
void InQueue(LQueue *LQ,ElemType e)
{
QueuePtr s;
s=(QueuePtr)malloc(sizeof(QNode));
s->data=e;
s->next=NULL;
if(LQ->front==NULL && LQ->rear==NULL)/*队空*/
LQ->rear=LQ->front=s;
else
{
LQ->rear->next=s;
LQ->rear=s;
}
}
/*出队*/
int OutQueue(LQueue *LQ,ElemType *e)
{
QueuePtr s;
if(LQ->front==NULL && LQ->rear==NULL)/*队空*/
{
printf("Queue is empty./n");
return 0;
}
s=LQ->front;
*e=s->data;
if(LQ->rear==LQ->front)/*原队列中仅有一个结点,删除后队列变空*/
LQ->rear=LQ->front=NULL;
else
LQ->front=LQ->front->next;
free(s);
return 1;
}
/*判空*/
int QueueEmpty(LQueue *LQ)
{
if(LQ->front==NULL && LQ->rear==NULL)
return 1;
else
return 0;
}
/*显示队列中的所有元素*/
void ShowQueue(LQueue LQ)
{
int i=0;
while (LQ.front)
{
printf("队列中的第%d个元素为%d/n",++i,(LQ.front)->data);
LQ.front=LQ.front->next;
}
}
/*取队首元素*/
GetQueue(LQueue *LQ,ElemType *e)
{
if(LQ->front==NULL && LQ->rear==NULL)
return 0;
else
{
*e=LQ->front->data;
return 1;
}
}
/*清队列*/
void ClearQueue(LQueue *LQ)
{
QueuePtr s,t;
s=LQ->front;
while(s)
{
t=s;
s=s->next;
free(t);
}
LQ->rear=LQ->front=NULL;
}
int main()
{
LQueue *LQ;
ElemType e;
InitQueue(LQ);
if (QueueEmpty(LQ))
printf("队列为空/n");
printf("请输入数字:");
scanf("%d",&e);
InQueue(LQ,e);
printf("请输入数字:");
scanf("%d",&e);
InQueue(LQ,e);
printf("请输入数字:");
scanf("%d",&e);
InQueue(LQ,e);
ShowQueue(*LQ);
OutQueue(LQ,&e);
printf("删除的元素为%d/n",e);
ShowQueue(*LQ);
if (QueueEmpty(LQ))
printf("队列为空/n");
GetQueue(LQ,&e);
printf("队首元素为%d/n",e);
ClearQueue(LQ);
if (QueueEmpty(LQ))
printf("队列为空/n");
return 0;
}
2.4、循环队列的优点是:其可以克服顺序队列的“假上溢”现象,能够使存储队列的向量空间得到充分的利用。判别循环队列的“空”或“满”不能以头尾指针是否相等来确定,一般是通过以下几种方法:一是另设变量来区别队列的空和满;二是少用一个元素的空间。每次入队前测试入队后头尾指针是不会重合,如果会重合就认为队列已满;三是设置一计数器记录队列中元素总数,不仅可以判别空或满,还可以得到队列中元素的个数。
2.5、队列的顺序存储结构和链式存储结构的区别
队列的顺序存储结构是利用一组地址连续的存储单元依次存放队列中的数据元素。一般情况下,用一维数组来作为队列的顺序存储空间,并且两个指示器front和rear分别指向队头元素位置和队尾元素位置。
队列的链接实现是一个同时带有头指针和尾指针的受限单链表。头指针指向队头结点,尾指针指向队尾结点即单链表的最后一个结点。
2.6、双端队列是限定插入和删除操作在线性表的两端进行,可将其看成是栈底在一起的两个栈,但其与两个栈共享存储空间是不同的。共享存储空间中的两个栈的栈顶指针是向两端扩展的,因而每个栈只需一个指针;而双端队列允许两端进行插入和删除元素因而每个端点必须设立两个指针。在实际应用中,可对双端队列的输出进行限制(即一个端点允许插入和删除,另一个端点允许插入),也可对双端队列的输入进行限制(即一个端点允许插入和删除,另一个端点只允许删除)。可见,采用双端队列可增加应用中的灵活性。
2.7、队列的应用非常广泛,如键盘的输入缓冲区的存储结构(循环队列)及作业队列等。队列也可用于递归的非递归转化。例如:若有一个分时系统,其中一台计算机连接着四个终端,即允许四个用户同时使用这一台计算机。则计算机系统必须设立一个队列,用以管理各终端用户使用CPU的请求。当某个用户要求使用CPU时,相应的终端代号就入队,而队头的终端用户则是CPU当前服务的对象。一种比较简单的情况是:对于当前用户(队首),系统每次分配一个时间片的时间间隔,在一个时间片内,如果当前用户的作业没有结束,则该终端用户的代号出队后重新入队,插入队尾,等待下一次CPU服务。如果某个用户的作业运行结束,则先退出,出队后不再入队,整个运行过程就是各终端代号不断地入队、出队,CPU轮流地为n(n<=4)个终端用户服务。由于计算机的运行速度极快,所以,对于每个终端用户来说,似乎计算机是单独在为其服务。