队列是区别与栈的一种线性表。栈的核心是LIFO,即后进先出,栈最常用的是顺序实现,用一片连续 的地址存储栈。而队列是一种FIFO也就是先进先出的线性表,一个队列有且仅有一个队头和一个队尾,元素只能由队尾进入,从队头排出。而队列比较常用的是链式结构实现的队列。我们可以想一想为什么链式比顺序结构更加适合于队列。原因就在于队列是先进先出,而顺序实现申请到的地址是返回首元素地址的(malloc函数)这个首地址是不宜变动的,否则会带来一些列的麻烦,而栈干好是先进后出,第一个进入的元素出栈的可能性是最小的,只有上面所有的元素都出栈了才轮到她,而咋i队列中,第一个进入的元素或者说队头元素必然是最先出队列的,所以如果用顺序结构实现队列,申请到的首地址只要经历过一次出队列的活动后,就不再是指向队列对头的指针了,除非把队列中的元素全部向前挪动一位,而然这样做的代价是巨大了,正是由于基地址或者说首地址(队头元素的地址)变化频繁,导致顺序结构不是现实队列的好方式。而链表就成了队列实现的首选项了。
队列的实现在线性表中是比较复杂的,在普通的顺序实现的线性表中,我们只需要这样的一个结构:
typedef struct {
int *elem;
int length;
int size;
}Sqlist;
我们用elem来存申请到的空间的首地址,size来记录当前申请到的空间的大小(注意不表示线性表中元素的个数),length才是记录当前线性表中以存储的元素个数的。
而普通的链式线性表的基本结构是:
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
我们把数据存在上面的结构体结点中的data区域,而利用指针next来存下一个结点的位置。
而静态链表中,我们使用的结构是这样的:
typedef struct{
char data;
int cur;
}component,SLinkList[MAX];
我们使用一个结构体数组,用游标记录逻辑上下一个元素在结构体数组中的位置,而实际的数据存储在data中。(这是不常见的线性表,用于没有指针而又需要链表的高级程序设计语言,缺点是线性表的大小固定,一个致命的弱点,不是吗?)
然后是栈的存储结构:
typedef struct {
char *top;
char *base;
int size;
}SqStack;
我们在上面也讲到过,真是由于栈最先进入的元素出栈可能性最低,导致栈很契合于顺序实现,我们的这个结构体中,用base记录申请到的空间的首地址,top记录栈顶元素上面一个空白区域的地址(注意 top可不是指向栈顶元素的,top–才是),用size来记录当前栈含有的空间大小(注意不是栈中存储了的元素个数,那个是s->top-s->base)
而队列的存储结构就比较复杂了,或者说像上面多有实现的结构的综合体:
typedef struct QNode{
int data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
可以看到我们至少需要两个结构体(存储的元素是基本类型而非结构体类型)才能实现链式队列。我们首先来分析一下这两个结构体的作用。QNode比较容易看出来。它是链表中的结点,数据存储在它的data域,而next是它的指针域指向下一个结点。而LinkQueue结构体含有两个指向QNode结构体的指针变量,其中front指向链表的头结点,rear指向链表的尾结点,所以看起来复杂其实很简单,链式队列就是一个链表,然后把这个链表的头结点地址和尾结点地址封装在一个结构体里面,就成了链式队列。
理解了链式队列的结构后,我们就可以动手来完成这个ADT了。附上C语言实现的代码:
#include<stdio.h>
#include<stdlib.h>
#define ERROR 0
#define OK 1
typedef int Status;
typedef struct QNode{
int data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
int main(){
Status InitQueue(LinkQueue *);
Status DestroyQueue(LinkQueue *);
Status EnQueue(LinkQueue *,int );
Status DeQueue(LinkQueue *,int *);
Status ClearQueue(LinkQueue *);
Status QueueEmpty(LinkQueue *);
Status GetTop(LinkQueue *,int *);
int QueueLength(LinkQueue *q);
void visit(int );
Status QueueTraverse(LinkQueue *,void (*f)(int ));
LinkQueue q,*qp=&q;
if(InitQueue(qp)){
printf("初始化成功!\n");
}
int a=9,b,*bp=&b;
printf("常数8进入队列\n");
EnQueue(qp,8);
printf("变量9进入队列\n");
EnQueue(qp,a);
printf("当前的队列长度为:%d\n",QueueLength(qp));
printf("遍历当前队列:");
QueueTraverse(qp,visit);
GetTop(qp,bp);
printf("\n获取队列头的元素(但是不出队列):%d\n",b);
printf("遍历当前队列:");
QueueTraverse(qp,visit);
printf("\n当前的队列长度为:%d\n",QueueLength(qp));
DeQueue(qp,bp);
printf("弹出位于队列头的元素:%d\n",b);
printf("当前的队列长度为:%d\n",QueueLength(qp));
printf("遍历当前队列:");
QueueTraverse(qp,visit);
DeQueue(qp,bp);
printf("\n弹出位于队列头的元素:%d\n",b);
printf("当前的队列长度为:%d\n",QueueLength(qp));
printf("遍历当前队列:");
QueueTraverse(qp,visit);
if(QueueEmpty(qp)){
printf("\n这是一个空队列\n");
}
if(DestroyQueue(qp)){
printf("链队列销毁成功!\n");
}
return 0;
}
Status InitQueue(LinkQueue *q){
q->front=(QueuePtr)malloc(sizeof(QNode));
if(!q->front){
exit(1);
}
q->rear=q->front;
q->rear->next=NULL;
return OK;
}
Status DestroyQueue(LinkQueue *q){
while(q->front){
q->rear=q->front->next;
free(q->front);
q->front=q->rear;
}
return OK;
}
Status EnQueue(LinkQueue *q,int e){
QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
if(!p){
exit(1);
}
p->data=e;
p->next=NULL;
q->rear->next=p;
q->rear=p;
return OK;
}
Status DeQueue(LinkQueue *q,int *e){
if(q->front==q->rear){
return ERROR;
}
QueuePtr p=q->front->next;
*e=p->data;
q->front->next=p->next;
if(q->rear==p){
q->rear=q->front;
}
free(p);
return OK;
}
Status ClearQueue(LinkQueue *q){
q->rear=q->front;
return OK;
}
Status QueueEmpty(LinkQueue *q){
if(q->front==q->rear){
return 1;
}else{
return 0;
}
}
Status GetTop(LinkQueue *q,int *e){
if(q->front==q->rear){
return ERROR;
}
QueuePtr p=q->front->next;
*e=p->data;
return OK;
}
int QueueLength(LinkQueue *q){
QueuePtr p=q->front;
int n=0;
while(p!=q->rear){
n++;
p=p->next;
}
return n;
}
void visit(int a){
printf("%d\t",a);
}
Status QueueTraverse(LinkQueue *q,void (*f)(int )){
if(q->front==q->rear){
return ERROR;
}
QueuePtr p=q->front->next;
while(p!=q->rear){
f(p->data);
p=p->next;
}
f(q->rear->data);
return OK;
}
运行结果:
初始化成功!
常数8进入队列
变量9进入队列
当前的队列长度为:2
遍历当前队列:8 9
获取队列头的元素(但是不出队列):8
遍历当前队列:8 9
当前的队列长度为:2
弹出位于队列头的元素:8
当前的队列长度为:1
遍历当前队列:9
弹出位于队列头的元素:9
当前的队列长度为:0
遍历当前队列:
这是一个空队列
链队列销毁成功!
--------------------------------
Process exited after 0.1394 seconds with return value 0
请按任意键继续. . .
为了简化,我这里的链式队列存储的int型。