【十三】队列的链式存储结构

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值