前言
本文在完成过程中,得到许多朋友的帮助,在此谢谢诸位。本人欢乐无限,技术有限,如有任何问题,欢迎各位不吝赐教。
1 队列是什么
队列是在表的一端进行插入,而在另一端进行删除的线性表。队列具有先进先出(FIFO, First In, First Out)特性。
在队列中,元素之间的位置关系主要有两个:
- 队头(front):进行删除的一端。
- 队尾(rear):进行插入的一端。
\
队列的基本操作是入队(enqueue)和出队(dequeue)。入队是在表的后端插入一个元素,程序中用enqueue(x, Q)实现。出队删除表的前端的元素,程序中用dequeue(Q)实现。
至此,大家应该对队列有了一个初步的认识。下面是一个队列模型,队列大小(指队列中元素的个数)为6。队头元素为a1,队尾元素为a6。
2 队列的实现
队列也是表,因此任何表的实现方法都可以用来实现队列。
2.1 队列的数组实现
对于使用数组实现的队列的数据结构,可以用下面的图来直观的说明:
从上面的图可以看到,队列的数据结构包括队列的结构(图中间的矩形)和队列的数组(图右边的矩形)两部分组成。
其中队列的结构包含5个成员:
- capacity:int变量,队列的容量;
- front:int变量,指示队头的位置;
- rear:int变量,指示队尾的位置;
- size: int变量,队列的大小;
- array:指针,指向队列的数组。
\
特别指出的是front初始化为1,rear初始化为0。这使得队列不为空时,front指示队头而rear指示队尾。(注:有些程序员可能采用了不同的策略:rear指示队尾的下一位置)
队列的数组是一个动态数组,用来存储队列的数据元素。由于在程序运行时,队列的插入和删除随时都可能在进行,为了充分利用数组空间,这里数组的使用方法有点特别,就是当front或rear到达数组尾端的时候,如果再执行出队或入队操作,它们又绕回到数组的开头,这种数组叫做循环数组(circular array)。采用这种数组实现的队列也就成为循环队列。
下面是C实现的队列,首先是Queue.h的头文件:
typedef int ElementType;
#ifndef QUEUE_H
#define QUEUE_H
struct QueueRecord;
typedef struct QueueRecord *Queue;
int isEmpty( Queue Q );
int isFull( Queue Q );
Queue createQueue( int maxElements );
void disposeQueue( Queue Q );
void makeEmpty( Queue Q );
void enqueue( ElementType x, Queue Q );
ElementType front( Queue Q );
void dequeue( Queue Q );
ElementType frontAndDequeue( Queue Q );
#endif
下面是Fatal.h的头文件:
#include <stdio.h>
#include <stdlib.h>
#define Error( Str ) FatalError( Str )
#define FatalError( Str ) fprintf( stderr, "%s\n", Str ), exit( 1 )
下面是Queue.c的源文件:
#include <stdlib.h>
#include "Queue.h"
#include "Fatal.h"
#define MinQueueSize ( 5 )
struct QueueRecord{
int capacity;
int front;
int rear;
int size;
ElementType *array;
};
int isEmpty( Queue Q )
{
return Q->size == 0;
}
int isFull( Queue Q )
{
return Q->size == Q->capacity;
}
Queue createQueue( int maxElements )
{
Queue Q;
if( maxElements < MinQueueSize )
Error( "Queue size is too small" );
Q = malloc( sizeof( struct QueueRecord ) );
if( Q == NULL )
FatalError( "Out of space!!!" );
Q->array = malloc( sizeof( ElementType ) * maxElements );
if( Q->array == NULL )
FatalError( "Out of space!!!" );
Q->capacity = maxElements;
makeEmpty( Q );
return Q;
}
void makeEmpty( Queue Q )
{
Q->size = 0;
Q->front = 1;
Q->rear = 0;
}
void disposeQueue( Queue Q )
{
if( Q != NULL ) {
free( Q->array );
free( Q );
}
}
void enqueue( ElementType x, Queue Q )
{
if( isFull( Q ) )
Error( "Full queue" );
else {
Q->size++;
Q->rear = (Q->rear + 1) % Q->capacity;
Q->array[ Q->rear ] = x;
}
}
ElementType front( Queue Q )
{
if( isEmpty( Q ) )
Error( "Empty queue" );
return Q->array[ Q->front ];
}
void dequeue( Queue Q )
{
if( isEmpty( Q ) )
Error( "Empty queue" );
else {
Q->size--;
Q->front = (Q->front + 1) % Q->capacity;
}
}
ElementType frontAndDequeue( Queue Q )
{
ElementType x = 0;
if( isEmpty( Q ) )
Error( "Empty queue" );
else {
Q->size--;
x = Q->array[ Q->front ];
Q->front = (Q->front + 1) % Q->capacity;
}
return x;
}
测试程序main.c
#include <stdio.h>
#include <stdlib.h>
#include "Queue.h"
int main(int argc, char *argv[])
{
Queue Q;
Q = createQueue( 5 );
for(int i = 0; i < 10; i++ ){
enqueue( i, Q );
printf("%d ", isFull(Q));
}
printf("\n");
while( !isEmpty( Q ) ) {
printf( "%d\n", front( Q ) );
dequeue( Q );
}
for(int i = 0; i < 20; i++ )
enqueue( i, Q );
while( !isEmpty( Q ) )
printf( "%d\n", frontAndDequeue( Q ) );
disposeQueue( Q );
return 0;
}
2.2 队列的链表实现
队列也可以使用单链表实现。其数据结构包含了队列的大小size,指针front和rear。其中front指向头结点(注意不是队头,队头可以看做是头结点的后继),rear指向队尾。其初始状态可用如下模型表示:
下面是一个大小为n的队列,队头元素为A1,队尾元素为An。
我们通过在表的头结点后面插入来实现enqueue,在队尾后面插入来实现dequeue。
首先是Queue.h
typedef int ElementType;
#ifndef QUEUE_H
#define QUEUE_H
struct ListNode;
typedef struct ListNode *PtrToNode;
struct QueueRecord;
typedef struct QueueRecord *Queue;
int isEmpty(Queue Q);
Queue createQueue();
void disposeQueue(Queue Q);
void makeEmpty(Queue Q);
void enqueue();
ElementType front(Queue Q);
void dequeue(Queue Q);
#endif
Fatal.h
#include <stdio.h>
#include <stdlib.h>
#define Error(str) FatalError(str)
#define FatalError(str) fprintf(stderr, "%s\n", str), exit(1)
Queue.c
#include <stdlib.h>
#include "Queue.h"
#include "Fatal.h"
struct ListNode{
ElementType element;
PtrToNode next;
};
struct QueueRecord{
int size;
PtrToNode front;
PtrToNode rear;
};
int isEmpty(Queue Q)
{
return Q->size == 0;
}
Queue createQueue()
{
Queue Q = malloc(sizeof(struct QueueRecord));
if(Q == NULL)
FatalError("Out of space!!!");
Q->size = 0;
Q->front = Q->rear = malloc(sizeof(struct ListNode));
if(Q->front == NULL)
FatalError("Out of space!!!");
Q->front->next = NULL;
return Q;
}
void disposeQueue(Queue Q)
{
makeEmpty(Q);
free(Q);
}
void makeEmpty(Queue Q)
{
while(!isEmpty(Q))
dequeue(Q);
}
void enqueue(ElementType x, Queue Q)
{
PtrToNode p = malloc(sizeof(struct ListNode));
if(p == NULL)
FatalError("Out of space!!!");
p->element = x;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
Q->size++;
}
ElementType front(Queue Q)
{
return Q->front->next->element;
}
void dequeue(Queue Q)
{
PtrToNode p = Q->front->next;
Q->front->next = p->next;
if(Q->front->next == NULL) //队列为空
Q->rear = Q->front;
free(p);
Q->size--;
}
测试程序main.c:
#include <stdio.h>
#include <stdlib.h>
#include "Queue.h"
int main(int argc, char *argv[])
{
Queue Q = createQueue( );
for(int i = 0; i < 10; i++)
enqueue( i, Q );
while(!isEmpty( Q ) ) {
printf( "%d\n", front( Q ) );
dequeue( Q );
}
for(int i = 0; i < 10; i++)
enqueue( i, Q );
while(!isEmpty( Q ) ) {
printf( "%d\n", front( Q ) );
dequeue( Q );
}
disposeQueue( Q );
return 0;
}
参考文献
[1] Mark Allen Weiss,数据结构与算法分析:C语言描述(原书第2版)
[2] Thomas H.Cormen,Charles E.Leiserson,Ronald L.Rivest,Clifford Stein,算法导论