目录
1. 前言
1.1 定义
队列:一种先进先出(first in first out,缩写 FIFO)的线性表。
允许插入的一端(rear)称为 队尾 ,允许删除的一端 (front)称为 队头。
和其他线性表一样,分为链式存储和线性存储。
用链表标识的队列简称 链队列,本文的代码就是实现队列的链式存储。
1.2 队列示意图
2. 链队列存储结构和函数说明
2.1 队列结点
typedef struct QNode{
QElemType data;
struct QNode * next;
}QNode; //定义队列的结点
2.2 队列类型
typedef struct{
QNode * front; //队头指针
QNode * rear; //队尾指针
int length;
}LinkQueue;
2.3 函数原型声明
一共有9个基本的操作函数
Status InitQueue(LinkQueue * queue); /*初始化队列,申请一个头结点的内存*/
void DestroyQueue(LinkQueue *queue); /*销毁队列*/
Status ClearQueue(LinkQueue * queue); //将队列queue清空
Status QueueEmpty(LinkQueue queue); //判断队列是否为空
Status GetHead(LinkQueue queue ,QElemType * e); //获取队列第一个元素
int QueueLength(LinkQueue queue); //返回队列长度
Status EnQueue(LinkQueue * queue, QElemType e); //元素e 插入队列queue
Status DeQueue(LinkQueue * queue ,QElemType * e); //若队列queue不空,则删除Q的队头元素,用e返回其值,并返回 OK;否则返回ERROR
Status QueueTraverse(LinkQueue queue,void (*visit)())//遍历队列,对队列的每个元素调用Visit函数
2.4 初始化队列 InitQueue
原型 :Status InitQueue(LinkQueue * queue);
功能 :初始化队列
说明:申请头结点的内存地址
头指针、尾指针、队列变量初始化
/*初始化队列,申请一个头结点的内存*/
Status InitQueue(LinkQueue * queue)
{
queue->front = (QNode*) malloc(sizeof(QNode)); //申请一个队列结点作为头结点的内存地址给 队头指针;
if(queue->front == NULL)
return FALSE;
queue->front->next = NULL;
queue->rear=queue->front;
queue->length=0;
return TRUE;
}
2.5 销毁队列 DestroyQueue
原型 :void DestroyQueue(LinkQueue *queue)
功能 :销毁队列,释放内存空间
/*销毁队列*/
void DestroyQueue(LinkQueue *queue)
{
ClearQueue(queue);
free(queue->front);
queue->front = queue->rear=NULL;
queue->length=0;
}
2.6 清空队列 ClearQueue
原型 :Status ClearQueue(LinkQueue * queue)
功能 :清空队列中的结点,
说明 : 和DestroyQueue的区别在于,清空队列是保留头结点指向的地址的,清空以后,队列还是可以照常使用的。
//将队列queue清空
Status ClearQueue(LinkQueue * queue)
{
QNode * curNode;
while((curNode = queue->front->next) != NULL)
{
queue->front->next=curNode->next;
free(curNode);
}
queue->rear = queue->front;
queue->length = 0;
return OK;
}
2.7 判断队列是否为空 QueueEmpty
原型 :Status QueueEmpty(LinkQueue queue)
功能 :判断队列是否为空
说明 :判断队列是否为空,只需要判断头指针和尾指针是否相等,相等,队列即为空
//判断队列是否为空
Status QueueEmpty(LinkQueue queue)
{
return queue.front == queue.rear? TRUE:FALSE;
}
2.8 获取队列第一个元素 QueueEmpty
原型 :Status GetHead(LinkQueue queue ,QElemType * e)
功能 :获取队列第一个元素
说明 :获取队列第一个元素,不是删除第一个元素。获得元素存放在参数 e 中,如果获取成功,返回TRUE ;失败,返回FALSE;
//获取队列第一个元素
Status GetHead(LinkQueue queue ,QElemType * e)
{
if(queue.length == 0)
return FALSE;
*e=queue.front->next->data;
return TRUE;
}
2.9 获取队列长度 QueueLength
原型 :int QueueLength(LinkQueue queue)
功能 :获取队列长度
//返回队列长度
int QueueLength(LinkQueue queue)
{
return queue.length;
}
2.10 插入操作 EnQueue
原型 :Status EnQueue(LinkQueue * queue, QElemType e)
功能 :元素 e 插入队列
说明 :插入操作,都是在队列尾部进行;插入成功,返回 TRUE;插入失败,返回 FALSE;
//元素 e 插入队列queue
Status EnQueue(LinkQueue * queue, QElemType e)
{
QNode * curNode=(QNode*) malloc(sizeof(QNode));
if(!curNode)
return FALSE;
curNode->data=e;
curNode->next=NULL;
queue->rear->next = curNode;
queue->rear =curNode;
queue->length++;
return TRUE;
}
2.11 删除操作 DeQueue
原型 :Status DeQueue(LinkQueue * queue ,QElemType * e)
功能 :删除队列第一个元素,
说明 :删除操作,只能在队头执行。 删除成功,返回 TRUE;删除失败,返回 FALSE;
//若队列queue不空,则删除Q的队头元素,用e返回其值,并返回 OK;否则返回ERROR
Status DeQueue(LinkQueue * queue ,QElemType * e)
{
QNode * curNode;
if(queue->length == 0)
return FALSE;
curNode = queue->front->next;
*e=curNode->data;
queue->front->next=curNode->next;
free(curNode);
queue->length--;
return TRUE;
}
2.11 遍历队列 QueueTraverse
原型 :Status QueueTraverse(LinkQueue queue,void (*visit)())
功能 :遍历队列
说明 :队列的每个元素调用Visit函数,这里的visit是函数指针,实际应用可以根据需要修改Visit函数。
//遍历队列,对队列的每个元素调用Visit函数
Status QueueTraverse(LinkQueue queue,void (*visit)())
{
QNode * curNode=queue.front->next;
while(curNode)
{
visit(curNode->data);
curNode=curNode->next;
}
}
3. 完整代码
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define MAXSIZE 30
typedef int Status;
typedef int QElemType; //定义元素类型为整型
typedef struct QNode{
QElemType data;
struct QNode * next;
}QNode; //定义队列的结点
typedef struct{
QNode * front; //队头指针
QNode * rear; //队尾指针
int length;
}LinkQueue; //定义一个队列类型
Status InitQueue(LinkQueue * queue); /*初始化队列,申请一个头结点的内存*/
void DestroyQueue(LinkQueue *queue); /*销毁队列*/
Status ClearQueue(LinkQueue * queue); //将队列queue清空
Status QueueEmpty(LinkQueue queue); //判断队列是否为空
Status GetHead(LinkQueue queue ,QElemType * e); //获取队列第一个元素
int QueueLength(LinkQueue queue); //返回队列长度
Status EnQueue(LinkQueue * queue, QElemType e); //元素e 插入队列queue
Status DeQueue(LinkQueue * queue ,QElemType * e); //若队列queue不空,则删除Q的队头元素,用e返回其值,并返回 OK;否则返回ERROR
Status QueueTraverse(LinkQueue queue,void (*visit)());//遍历队列,对队列的每个元素调用Visit函数
/*初始化队列,申请一个头结点的内存*/
Status InitQueue(LinkQueue * queue)
{
queue->front = (QNode*) malloc(sizeof(QNode)); //申请一个队列结点作为头结点的内存地址给 队头指针;
if(queue->front == NULL)
return FALSE;
queue->front->next = NULL;
queue->rear=queue->front;
queue->length=0;
return TRUE;
}
/*销毁队列*/
void DestroyQueue(LinkQueue *queue)
{
ClearQueue(queue);
free(queue->front);
queue->front = queue->rear=NULL;
queue->length=0;
}
//将队列queue清空
Status ClearQueue(LinkQueue * queue)
{
QNode * curNode;
while((curNode = queue->front->next) != NULL)
{
queue->front->next=curNode->next;
free(curNode);
}
queue->rear = queue->front;
queue->length = 0;
return OK;
}
//判断队列是否为空
Status QueueEmpty(LinkQueue queue)
{
return queue.front == queue.rear? TRUE:FALSE;
}
//获取队列第一个元素
Status GetHead(LinkQueue queue ,QElemType * e)
{
if(queue.length == 0)
return FALSE;
*e=queue.front->next->data;
return TRUE;
}
//返回队列长度
int QueueLength(LinkQueue queue)
{
return queue.length;
}
//元素e 插入队列queue
Status EnQueue(LinkQueue * queue, QElemType e)
{
QNode * curNode=(QNode*) malloc(sizeof(QNode));
if(!curNode)
return FALSE;
curNode->data=e;
curNode->next=NULL;
queue->rear->next = curNode;
queue->rear =curNode;
queue->length++;
return TRUE;
}
//若队列queue不空,则删除Q的队头元素,用e返回其值,并返回 OK;否则返回ERROR
Status DeQueue(LinkQueue * queue ,QElemType * e)
{
QNode * curNode;
if(queue->length == 0)
return FALSE;
curNode = queue->front->next;
*e=curNode->data;
queue->front->next=curNode->next;
free(curNode);
queue->length--;
return TRUE;
}
void Visit(QElemType e)
{
printf("%3d",e);
}
//遍历队列,对队列的每个元素调用Visit函数
Status QueueTraverse(LinkQueue queue,void (*visit)())
{
QNode * curNode=queue.front->next;
while(curNode)
{
visit(curNode->data);
curNode=curNode->next;
}
}
int main()
{
QElemType e;
LinkQueue queue;
InitQueue(&queue);
printf("队头分别插入数字3、4、5后:");
EnQueue(&queue,3);
EnQueue(&queue,4);
EnQueue(&queue,5);
QueueTraverse(queue,Visit);
printf("\n删除队头数字后:");
DeQueue(&queue,&e);
QueueTraverse(queue,Visit);
printf("\n清空队列");
ClearQueue(&queue);
printf("\n队列长度:%d\n",QueueLength(queue));
DestroyQueue(&queue);
getchar();
return 0;
}
4. 运行结果
5. 优缺点
这里的优缺点,是相对于顺序存储队列而言
优点:
- 链队列可以动态分配空间,当不确定队列大小的时候,内存空间需要多少就申请多少,不浪费。
- 不会出现数组溢出等问题。
缺点:
当确定队列大小的时候,占用的空间比顺序存储大,因为每个结点都有一个额外的指针指向下一个结点。