1、链式存储的定义
上一篇介绍了队列及其顺序存储,基于上一篇,简介队列的链式存储结构的实现!
2、基于链表的实现
队列是一种特殊的顺序表,所以可以基于链表来实现队列的链式存储结构!
数据结构的定义
//因为内部使用的是顺序链表,所以有如下的定义
typedef struct _struct_linkqueue
{
LinklistNode node;
LinkQueueNode *item;
}TLinkQueueNode;
部分代码:
/*
创建一个队列
如果成功,返回一个队列指针,否则返回NULL
*/
LinkQueue *LinkQueue_Create()
{
return List_Create();
}
/*
销毁一个队列
*/
void LinkQueue_Destroy(LinkQueue *queue)
{
LinkQueue_Clear(queue);
List_Destroy(queue);
}
/*
清空队列中的元素
*/
void LinkQueue_Clear(LinkQueue *queue)
{
while(LinkQueue_Length(queue) > 0)
{
LinkQueue_Retrieve(queue);
}
}
/*
在队尾添加一个元素
如果成功,返回1,失败返回0
*/
int LinkQueue_Append(LinkQueue *queue, LinkQueueNode *item) //O(n)
{
int iret = 1;
//分配内存,存储数据item
TLinkQueueNode *tnode = (TLinkQueueNode*)malloc(sizeof(TLinkQueueNode));
iret = iret && (queue != NULL) && (item != NULL) && (tnode != NULL);
if(iret)
{
//保存真正要存储的数据
tnode->item = item;
//链表尾部作为队列尾部
iret = List_Insert(queue,(LinklistNode*)tnode,List_Length(queue));
}
//如果插入没有成功,则释放tnode
if (!iret)
{
free(tnode);
}
return iret;
}
/*
在队头移出一个元素
如果成功,返回被移出的元素指针,否则,返回NULL
*/
LinkQueueNode *LinkQueue_Retrieve(LinkQueue *queue) //O(1)
{
LinkQueueNode *ret = NULL;
TLinkQueueNode * tnode = (TLinkQueueNode*)List_Delete(queue,0);
if(tnode != NULL)
{
ret = tnode->item;
free(tnode);
}
return ret;
}
/*
获取队头元素
如果成功,返回队头元素指针,否则,返回NULL
*/
LinkQueueNode *LinkQueue_Header(LinkQueue *queue) //O(1)
{
LinkQueueNode *ret = NULL;
TLinkQueueNode *tnode = (TLinkQueueNode*)List_Get(queue,0);
if(tnode != NULL)
{
ret = tnode->item;
}
return ret;
}
链式队列的瓶颈
- 链式队列
- 线性表的第一个元素作为队头
- 线性表的最后一个元素作为队尾
- 入队的新元素是在线性表的最后,时间复杂度为O( n)
- 出队的元素即链表的第一个元素,时间复杂度O( 1)
问题:
如何将入队操作的时间复杂度降低到O(1)?
上述算法中LinkQueue_Append();操作的时间复杂度为O(n),为了优化它,有了如下的改进方法!
3、链式队列的优化方案
- 定义rear指针始终指向链表中的最后一个元素
- 入队时将新元素通过rear插入队尾,且将rear指向新元素
- 定义一个front指针,始终指向链表中的第一个元素
- 出队时,通过front指针获取出队的元素,并让它指向下一个元素
数据结构的定义
//linkqueue.h
//为了保持对外的一致性
typedef void LinkQueue;
typedef void LinkQueueNode;
//linkqueue.c
//TLinkQueueNode 定义了两个指针,一个指向下一个Node,一个是真正的要被存储的数据的地址
typedef struct _struct_linkqueuenode TLinkQueueNode;
struct _struct_linkqueuenode
{
TLinkQueueNode *next;
LinkQueueNode *item;
};
//新增加了front和rear指针,分别指向队头和队尾
typedef struct _struct_linkqueue
{
TLinkQueueNode *front;
TLinkQueueNode *rear;
int length;
}TLinkQueue;
部分代码:
/*
创建一个队列
如果成功,返回一个队列指针,否则返回NULL
*/
LinkQueue *LinkQueue_Create()
{
TLinkQueue *tqueue = (TLinkQueue*)malloc(sizeof(TLinkQueue));
if(tqueue != NULL)
{
tqueue->front = NULL;
tqueue->rear = NULL;
tqueue->length = 0;
}
return tqueue;
}
/*
销毁一个队列
*/
void LinkQueue_Destroy(LinkQueue *queue)
{
if(queue != NULL)
{
LinkQueue_Clear(queue);
free(queue);
}
}
/*
清空队列中的元素
*/
void LinkQueue_Clear(LinkQueue *queue)
{
if(queue != NULL)
{
while(LinkQueue_Length(queue) > 0)
{
LinkQueue_Retrieve(queue);
}
}
}
/*
在队尾添加一个元素
如果成功,返回1,失败返回0
*/
int LinkQueue_Append(LinkQueue *queue, LinkQueueNode *item)
{
int iret = 1;
TLinkQueue *tqueue = (TLinkQueue*)queue;
//申请内存空间,存放真正被保存的数据
TLinkQueueNode *tnode = (TLinkQueueNode*)malloc(sizeof(TLinkQueueNode));
iret = iret && (tqueue != NULL) && (item != NULL) && (tnode != NULL);
if(iret)
{
//存放真正被保存的数据,实际上是一个地址item
tnode->item = item;
/*
如果插入的是第一个元素,则要让front指针指向该元素;
否则,只需在rear标记的队尾插入该元素即可
*/
if(tqueue->length > 0)
{
tqueue->rear->next = tnode;
tqueue->rear = tnode;
tnode->next = NULL;
}
else
{
tqueue->front = tnode;
tqueue->rear = tnode;
tnode->next = NULL;
}
tqueue->length++;
}
//如果插入没有成功,则释放tnode
if (!iret)
{
free(tnode);
}
return iret;
}
/*
在队头移出一个元素
如果成功,返回被移出的元素指针,否则,返回NULL
*/
LinkQueueNode *LinkQueue_Retrieve(LinkQueue *queue)
{
LinkQueueNode *ret = NULL;
TLinkQueueNode * tnode = NULL;
TLinkQueue *tqueue = (TLinkQueue*)queue;
if(tqueue != NULL)
{
//判断是够还有元素可以出队
if(tqueue->length > 0)
{
tnode = tqueue->front;
tqueue->front = tqueue->front->next;
ret = tnode->item;
free(tnode);
tqueue->length--;
//如果经过上述的出队后,没有元素了,那么让front和rear指向NULL
if(tqueue->length <= 0)
{
tqueue->front = NULL;
tqueue->rear = NULL;
}
}
}
return ret;
}
/*
获取队头元素
如果成功,返回队头元素指针,否则,返回NULL
*/
LinkQueueNode *LinkQueue_Header(LinkQueue *queue)
{
LinkQueueNode *ret = NULL;
TLinkQueueNode *tnode = NULL;
TLinkQueue *tqueue = (TLinkQueue*)queue;
if(tqueue != NULL)
{
if(tqueue->length > 0)
{
tnode = tqueue->front;
ret = tnode->item;
}
}
return ret;
}
/*
获取队列元素个数
如果失败,返回-1
*/
int LinkQueue_Length(LinkQueue *queue)
{
int iret = -1;
TLinkQueue *tqueue = (TLinkQueue*)queue;
if(tqueue != NULL)
{
iret = tqueue->length;
}
return iret;
}
4、完整源码下载
文件名:linkqueue-1.0.tar.gz
说明:使用顺序链表实现的链式存储队列
链接: http://pan.baidu.com/s/1o6wu8uI 密码: 5rrx
文件名:linkqueue-1.1.tar.gz
链接: http://pan.baidu.com/s/1hqnd4sG 密码: hu3r
说明:修复了由linklist.c中的List_Get()函数所引起的BUG,详情见链接!
文件名:linkqueue-2.0.tar.gz
说明:没有使用顺序链表,采用改进的算法实现的链式存储队列
链接: http://pan.baidu.com/s/1kTkp1ZL 密码: sexb
编译步骤:
0.1 解压缩:tar -zxvf linkqueue-1.0.tar.gz/linkqueue-2.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./LinkQueue