队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们简称为链队列。我们将队头指针指向链队列的头结点,而队尾指针指向终端节点。
头结点 队头 队尾
front a1 an NULL
rear
空队列时,front和rear都指向头结点。
头结点
front NULL
rear
4.13.4 循环队列和链队列
时间上 基本操作都是常数时间,即都为0(1)的,不过循环队列是事先申请好的 空间,使用期间不释放。 而对于链队列,每次申请和释放结点也会存在一些 时间开销。
空间上 循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的 问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上 的开销。空间上,链队列更加灵活。
总之,在可以确定队列长度最大值的情况下,建议用循环队列
无法预估队列的长度时,则用链队列。
链队列程序如下所示:
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
//定义结点数据类型
typedef struct Node
{
int data;
struct Node *next;
} node,*pnode;
//队列的链表结构
typedef struct Queue
{
pnode front;
pnode rear; //队头队尾指针
} queue,*pqueue;
//函数声明
//构造空队列
void init_queue(pqueue list);
//入队
void en_queue(pqueue list,int val);
//遍历
void traverse(pqueue list);
//出队
bool de_queue(pqueue list,int *val);
//队列为空
bool empty_queue(pqueue list);
int main(void)
{
queue list;
int val;
init_queue(&list);
en_queue(&list,1);
en_queue(&list,2);
en_queue(&list,3);
en_queue(&list,4);
en_queue(&list,5);
en_queue(&list,6);
traverse(&list);
if(de_queue(&list,&val))
{
printf("出队成功,出队的元素是:%d.\n",val);
}
else
{
printf("出队失败!\n");
}
traverse(&list);
return 0;
}
//构造空队列
void init_queue(pqueue list)
{
list->front = (pnode)malloc(sizeof(node));
if(list->front == NULL)
{
printf("分配内存失败!\n");
exit(-1);
}
else
{
list->rear = list->front;
list->front->next = NULL;
list->rear->next = NULL;
}
}
//插入元素val为新的队尾元素 入队
/**********************************************************
front
头结点 a1 a2 a3 a4.............an s
e NULL
rear
**********************************************************/
void en_queue(pqueue list,int val)
{
pnode p;
p = (pnode)malloc(sizeof(node));
if(p == NULL)
{
printf("分配内存失败!\n");
exit(-1);
}
p->data = val;
p->next = NULL;
list->rear->next = p; //把拥有元素e新结点p赋值给原队尾结点的后继
list->rear = p; //把当前的p设置为队尾结点,rear指向p
return ;
}
//遍历输出
void traverse(pqueue list)
{
pnode p;
p = list->front->next;
while(p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return ;
}
//队列为空
bool empty_queue(pqueue list)
{
if(list->front == list->rear)
{
return true;
}
else
{
return false;
}
}
//出队
/**************************************************************
出队操作时,就是头结点的后继结点出队,将头结点的后继改为它后面的结点
若链表除过头结点外只剩一个元素时,则需要将rear指向头结点。
**************************************************************/
bool de_queue(pqueue list,int *val)
{
if(empty_queue(list))
{
return false;
}
else
{
pnode p;
p = list->front->next; //头结点不能丢,将欲删除的队头结点暂存给p
*val = p->data; //将欲删除的队头结点的值赋值给e
list->front->next = p->next; //将原队头结点后继p->next赋值给头结点后继
if(list->rear == p) //若队头是队尾,则删除后将rear指向头结点。
{
list->rear = list->front;
}
free(p);
return true;
}
}