浅谈数据结构之链队列(六)
前面我们讲了队列的顺序存储结构,现在我们来看看队列的链式存储结构。队列的链式存储其实就是线性表的单链表结构,只不过它是尾进头出而已,通常我们把它简称为链队列。为了操作上的方便,我们将队头指针front指向链队列的头结点,而队尾指针rear则指向终端结点。注意:当队列为空时,指针front和rear都指向头结点。
在这里,我们再介绍一下循环队列。循环队列是为了避免数组插入与删除数据时需要移动数据而引入的,我们一般把队列的这种头尾相接的顺序存储结构称为循环队列。对于循环队列和链队列相比较来说,循环队列使得队头和队尾在数组中循环变化,解决了移动数据时所需要的时间损耗,因此它的时间复杂度为0(1);而链队列事先并不需要预估队列的长度,也没有存储元素个数和空间浪费的问题,所以在空间上,链队列更加灵活,方便。
链队列的入队操作其实就是在链表尾插入结点,而出队操作则是将头结点的后继结点出队,同时将头结点的后继改为它后面的结点;若链表除头结点外只剩下一个元素,则需将指针rear指向头结点,具体操作源程序代码如下所示:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 5 #define OK 1 6 #define ERROR 0 7 #define TRUE 1 8 #define FALSE 0 9 10 #define MAXSIZE 20 /* 存储空间初始分配量 */ 11 12 typedef int Status; 13 typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ 14 15 typedef struct QNode /* 结点结构 */ 16 { 17 QElemType data; 18 struct QNode *next; 19 }QNode,*QueuePtr; 20 21 typedef struct /* 队列的链表结构 */ 22 { 23 QueuePtr front,rear; /* 队头、队尾指针 */ 24 }LinkQueue; 25 26 Status visit(QElemType c) 27 { 28 printf("%d ",c); 29 return OK; 30 } 31 32 /* 构造一个空队列Q */ 33 Status InitQueue(LinkQueue *Q) 34 { 35 Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode)); 36 if(!Q->front) 37 exit(OVERFLOW); 38 Q->front->next=NULL; 39 return OK; 40 } 41 42 /* 将Q清为空队列 */ 43 Status ClearQueue(LinkQueue *Q) 44 { 45 QueuePtr p,q; 46 Q->rear=Q->front; 47 p=Q->front->next; 48 Q->front->next=NULL; 49 while(p) 50 { 51 q=p; 52 p=p->next; 53 free(q); 54 } 55 return OK; 56 } 57 58 /* 求队列的长度 */ 59 int QueueLength(LinkQueue Q) 60 { 61 int i=0; 62 QueuePtr p; 63 p=Q.front; 64 while(Q.rear!=p) 65 { 66 i++; 67 p=p->next; 68 } 69 return i; 70 } 71 72 /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ 73 Status GetHead(LinkQueue Q,QElemType *e) 74 { 75 QueuePtr p; 76 if(Q.front==Q.rear) 77 return ERROR; 78 p=Q.front->next; 79 *e=p->data; 80 return OK; 81 } 82 83 84 /* 插入元素e为Q的新的队尾元素 */ 85 Status EnQueue(LinkQueue *Q,QElemType e) 86 { 87 QueuePtr s=(QueuePtr)malloc(sizeof(QNode)); 88 if(!s) /* 存储分配失败 */ 89 exit(OVERFLOW); 90 s->data=e; 91 s->next=NULL; 92 Q->rear->next=s; /* 把拥有元素e的新结点s赋值给原队尾结点的后继 */ 93 Q->rear=s; /* 把当前的s设置为队尾结点,rear指向s */ 94 return OK; 95 } 96 97 /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */ 98 Status DeQueue(LinkQueue *Q,QElemType *e) 99 { 100 QueuePtr p; 101 if(Q->front==Q->rear) 102 return ERROR; 103 p=Q->front->next; /* 将欲删除的队头结点暂存给p */ 104 *e=p->data; /* 将欲删除的队头结点的值赋值给e */ 105 Q->front->next=p->next; /* 将原队头结点的后继p->next赋值给头结点后继 */ 106 if(Q->rear==p) /* 若队头就是队尾,则删除后将rear指向头结点 */ 107 Q->rear=Q->front; 108 free(p); 109 return OK; 110 } 111 112 /* 从队头到队尾依次对队列Q中每个元素输出 */ 113 Status QueueTraverse(LinkQueue Q) 114 { 115 QueuePtr p; 116 p=Q.front->next; 117 while(p) 118 { 119 visit(p->data); 120 p=p->next; 121 } 122 printf("\n"); 123 return OK; 124 } 125 126 int main() 127 { 128 int i; 129 QElemType e; 130 LinkQueue q; 131 132 i=InitQueue(&q); 133 printf("1.初始化后队列后,队列的长度:Q.length=%d\n",QueueLength(q)); 134 135 EnQueue(&q,-5); 136 EnQueue(&q,3); 137 EnQueue(&q,8); 138 EnQueue(&q,10); 139 EnQueue(&q,16); 140 EnQueue(&q,36); 141 printf("2.插入6个元素后,队列的长度:Q.length=%d\n",QueueLength(q)); 142 printf("3.队列的元素输出依次为:"); 143 QueueTraverse(q); 144 145 i=GetHead(q,&e); 146 if(i==OK) 147 printf("4.此时队列的队头元素是:%d\n",e); 148 149 DeQueue(&q,&e); 150 i=GetHead(q,&e); 151 if(i==OK) 152 printf("5.删除队列的队头元素后,新的队头元素是:%d\n",e); 153 154 DeQueue(&q,&e); 155 printf("6.继续删除队头元素后,队列的长度:Q.length=%d\n",QueueLength(q)); 156 printf("7.队列的元素输出依次为:"); 157 QueueTraverse(q); 158 159 ClearQueue(&q); 160 printf("8.清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next); 161 162 return 0; 163 }