数据结构——队列

  • 概述

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种火多种特定关系的数据元素的集合。通常情况,选择合适的数据结构可以带来更高的运行或者存储效率。队列是常用的数据结构之一,是一个允许在一端进行插入操作,而在另一端进行删除操作的线性表。在嵌入式软件中主要应用于异步处理的buffer,例如,在CAN、SPI、UART等接口在中断接收到数据,存放在队列中(入队),而实际的处理解析(出队)将在其他线程或者任务中实现。另外还可以应用于FIFO的阻塞的某些特殊的应用环境,例如,报警序列。并且,在硬件的存储芯片中,有一类根据队列结构构造的芯片,那就是FIFO芯片。

  • 特征

队列是一种线性结构,允许对两端进行操作,在表的一端只能进行删除操作,此端称为队头;在表的另一端只能进行插入操作,称为队尾。若队列中没有数据,称为空队列。队列按照先进先出的原则处理结点数据,即插入的顺序和取出的顺序是相同的。如下图所示为队列示意图:

  • 分类

根据队列的实现方式,可以分为顺序队列及链表队列两种。

  1. 顺序对列:顺序队列是用数组实现的,首指针在出队的时候移动,尾指针在入队的时候移动,需要考虑队列为空和队列为满两种情况。一般情况下,会将其设置为循环队列。如下图所示:
  2. 链表队列:顾名思义,链表队列通过链表来实现,其首指针不移动始终指向头节点,尾指针在入队的时候移动,只考虑队列为空的情况(因为链表的长度在程序运行过程中可以不断增加,因此不需要考虑队列满的情况)。如下图所示:(a)空队列(b)元素x入队列(c)元素y入队列(d)元素x出队列
  • 顺序队列

顺序队列,是使用一组地址连续的内存单位依次保存队列中的数据。

  1. 入队

当向队列中添加一个元素时,将元素直接添加到队尾,不需要移动元素。

  1. 出队

队列元素的出列是在队头,即下标为0的位置,这就意味着在队头删除元素时,队列中的所有元素都得向前移动,以保证队列的队头不为空。

  1. 假溢出

由出队列可知,假设当前队列的空间为5,入队a1,a2,a3,a4,a5,出队a1,a2,但此时若接着入队的话,数组的末尾元素已经占用,再向后加就会产生数组越界的错误,但是队列下标为0和1的位置还是空闲的,这种现象叫做“假溢出”,即在顺序队列中,队尾指针已经达到数组的上界时,虽然数组中还有空位置,但是不能再有入队的操作。真溢出则是队列空间能够容纳多少,实际入队就是多少。

解决假溢出的办法就是循环队列,当队列后面满了以后,再从头开始,也即头尾相接的循环。如下图所示:

  1. 队列满/空

下面来判断队列的空及满,由队列的头指针(front)及尾指针(rear)的相对位置可知,当队列为空时,front == rear;当队列满时,front == rear。那么如何判断此时的队列究竟是满还是空呢?

方法1,设置标志变量flag,当front == rear,且flag == 0时队列为空;当front == rear,且flag == 1时队列为满。标志变量可以由队列内元素个数来判断。方法2,队列为空时,front == rear;当队列为满时,则需保留一个元素空间,也即队列满了,队列空间中还有一个空闲单元。如下图所示:

 

由图可知,由于队列首尾相接,所以rear与front尽管只相差一个位置,但是rear可能比front大,也可能比front小,所以可以利用队列空间的大小进行取模,若队列空间最大为size,则根据第二种方法,队列满的条件为(rear + 1) % size == front。

  1. C语言表示
  1. 存储结构

//----循环队列----

typedef struct

{

QElemType* base;//初始化的动态分配存储空间

int front;//头指针,若队列不空,指向队列头元素

int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置

}SqQueue;

  1. 基本操作函数原型

Status InitQueue(SqQueue& Q); //构造一个空队列Q

int QueueLength(SqQueue Q); //返回Q的元素个数,即队列的长度

Status EnQueue(SqQueue& Q, QElemType e); //入队,元素e插入队列Q

Status DeQueue(SqQueue& Q, QElemType& e); //出队,若队列不空,删除Q的队头元素,用e返回其值,并返回OK

void PrintQueue(SqQueue Q); //打印队列

  1. 基本操作函数说明

/*!

    \brief       Initialize queue

    \param[in]   queue

    \param[out] none

    \retval      Status

*/

Status InitQueue(SqQueue& Q)

{

Q.base = (QElemType*)malloc(MAXQSIZE * sizeof(QElemType));

if (!Q.base)

{

exit(OVERFLOW);//存储分配失败

}

Q.front = Q.rear = 0;

return OK;

}

/*!

    \brief       Get the number of elements in the queue

    \param[in]   queue

    \param[out] none

    \retval      Number of elements

*/

int QueueLength(SqQueue Q)

{

return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;

}

/*!

    \brief       Write element to queue

    \param[in]   queue

element

    \param[out] none

    \retval      Status

*/

Status EnQueue(SqQueue& Q, QElemType e)

{

if ((Q.rear + 1) % MAXQSIZE == Q.front)

{

return ERROR;

}

Q.base[Q.rear] = e;

Q.rear = (Q.rear + 1) % MAXQSIZE;

return OK;

}

/*!

    \brief       Remove element from queue

    \param[in]   queue

element

    \param[out] none

    \retval      Status

*/

Status DeQueue(SqQueue& Q, QElemType& e)

{

if (Q.front == Q.rear)

{

return ERROR;

}

e = Q.base[Q.front];

Q.front = (Q.front + 1) % MAXQSIZE;

return OK;

}

/*!

    \brief       Printf queue element

    \param[in]   queue

    \param[out] none

    \retval      none

*/

void PrintQueue(SqQueue Q)

{

printf("队列为:");

int n = Q.front;

for (int i = 0; i < QueueLength(Q); i++)//循环次数

{

printf("%d ", Q.base[n++ % MAXQSIZE]);

}

printf("\n");

}

  • 链表队列

链表队列即使用链表来保存队列中各元素的值,为了操作方便,将队列头指针front指向链表的头结点,将队列尾指针rear指向链表的终端结点。如下图所示:

 

  1. 入队

链表队列在入队操作时,队尾指针指向下一存储空间的头结点,如下图所示:

 

  1. 出队

链表队列在出队操作时,队头指针指向第二个元素的头结点,如下图所示:

 

  1. 队列空

当队列为空时,头指针与尾指针都指向同一个结点,如下图所示:

 

  1. C语言表示
  1. 存储结构

//单链队列----队列的链式存储结构

typedef struct QNode

{

QElemType data;

struct QNode* next;

}QNode, * QueuePtr;

typedef struct

{

QueuePtr front;//队头指针

QueuePtr rear;//队尾指针

}LinkQueue;

  1. 基本操作函数原型

Status InitQueue(LinkQueue& Q); //构造一个空队列Q

Status DestroyQueue(LinkQueue& Q); //销毁队列Q,将Q清为空队列

Status GetHead(LinkQueue Q, QElemType& e); //用e返回Q的队头元素

Status EnQueue(LinkQueue& Q, QElemType e); //入队,元素e插入队列Q

Status DeQueue(LinkQueue& Q, QElemType& e); //出队,若队列不空,删除Q的队头元素,用e返回其值,并返回OK

void PrintQueue(LinkQueue Q); //打印队列

  1. 基本操作函数说明

/*!

    \brief       Initialize queue

    \param[in]   queue

    \param[out] none

    \retval      Status

*/

Status InitQueue(LinkQueue& Q)

{

//构造一个空队列Q

Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));

if (!Q.front)

{

exit(OVERFLOW);//分配存储失败

}

Q.front->next = NULL;

return OK;

}

/*!

    \brief       Destroy queue

    \param[in]   queue

    \param[out] none

    \retval      Status

*/

Status DestroyQueue(LinkQueue& Q)

{

//销毁队列Q

while (Q.front)

{

Q.rear = Q.front->next;

free(Q.front);

Q.front = Q.rear;

}

return OK;

}

/*!

    \brief       Get queue header address

    \param[in]   queue

element

    \param[out] none

    \retval      Status

*/

Status GetHead(LinkQueue Q, QElemType& e)

{

if (Q.front == Q.rear)

{

return ERROR;

}

e = Q.front->next->data;

return OK;

}

/*!

    \brief       Write element to queue

    \param[in]   queue

element

    \param[out] none

    \retval      Status

*/

Status EnQueue(LinkQueue& Q, QElemType e)

{

//插入元素e为Q的新的队尾元素

QueuePtr p = (QueuePtr)malloc(sizeof(QNode));

if (!p)

{

exit(OVERFLOW);

}

p->data = e;

p->next = NULL;

Q.rear->next = p;

Q.rear = p;

return OK;

}

/*!

    \brief       Remove element from queue

    \param[in]   queue

element

    \param[out] none

    \retval      Status

*/

Status DeQueue(LinkQueue& Q, QElemType& e)

{

//若队列不空,则删除Q的对头元素,用e返回其值,并返回OK;否则返回ERROR

if (Q.front == Q.rear)

return ERROR;

QueuePtr p = Q.front->next;

e = p->data;

Q.front->next = p->next;

if (Q.rear == p)

{

Q.rear = Q.front;

}

free(p);

return OK;

}

/*!

    \brief       Printf queue element

    \param[in]   queue

    \param[out] none

    \retval      none

*/

void PrintQueue(LinkQueue Q)

{

printf("队列值为:");

QueuePtr p = Q.front;

while (p->next != NULL)

{

printf("%d ",p->next->data);

p = p->next;

}

printf("\n");

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值