赞
踩
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
基本结构如下图:
最左边是对头,最右边是队尾
当数据传入队列的时候,数据会直接放在队尾的位置,此过程为入队列。
当取数据时,只能从对头的数据依次往后取,此过程为出队列。
队列结构导致了队列中的数据会遵从先进先出FIFO(First In First Out)
的原则。
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
本文采用链表结构来实现队列。
队列结构说明
队列主要通过一个头地址 front 和一个尾地址 rear 来控制队列中数据的位置信息。
队列中数据则是由多个链表块数据构成,如下图蓝色方块。
当队列为空时,front 和 rear 均为空,如下图(a).
当有数据进入链表中时,front 和 rear 会分别指向队头和队尾的位置,数据块会通过链表的结构链接起来,结构如下图(b)(c)(d)。
基于队列结构的特殊性(包含链表数据块和对头头尾指针)以及操作的简便性,创建以下两个结构体对队列进行管理。
typedef int MyQDataType;//宏定义数据类型,便于不同数据的放入 //链表数据块结构的创建 typedef struct QueueNode { MyQDataType data;//存放数据 struct QueueNode* next;//指向下一个数块地址 }QNode; //队列结构的创建 typedef struct QueueStack { QNode* phead;//指向队头位置 QNode* ptail;//指向队尾位置 int size;//记录数据个数 }Queue;
void QueueInit(Queue* pq)
{
assert(pq);//断言,队列不存在时报错
pq->phead = NULL;//队头置空
pq->ptail = NULL;//队尾置空
pq->size = 0;//数据个数置零
}
当size 等于0 的时候,队列中数据为空。
bool QueueEmpty(Queue* pq)//布尔判断,为真时返回ture,为假时返回false。
{
assert(pq);//断言,队列不存在时报错
return pq->size == 0;//当size 为0时,满足条件,返回ture,否在返回false
//等价于以下代码
/*if(pq->size==0)
return ture;
else
retrun false;*/
}
入队列要考虑队列是否为空以及头尾指针的位置摆放。
void QueuePush(Queue* pq, MyQDataType x) { assert(pq);//断言,队列不存在时报错 //创建新的链表数据块,并放入数据 QNode* newnode = (QNode*)malloc(sizeof(QNode)); if(newnode == NULL) { perror("malloc fail"); return; } newnode->data = x; newnode->next = NULL; //队尾指针不为空,即队列中已经存在数据时,数据直接放队尾 if (pq->ptail != NULL) { pq->ptail->next = newnode; pq->ptail = newnode; } //队尾指针为空,即队列中无数据时,头指针和尾指针同时指向第一块数据 else { pq->ptail = newnode; pq->phead = newnode; } pq->size++;//插入数据,数据个数加一 }
出队列要考虑空间的释放以及头尾指针的位置
void QueuePop(Queue* pq) { assert(pq);//断言,队列不存在时报错 assert(!QueueEmpty(pq));//断言,队列为空时报错 //单个数据节点 if (pq->phead->next == NULL) { free(pq->phead);//直接释放空间 pq->phead = pq->ptail = NULL;//置空 } //多个数据节点 else { Queue* cur = pq->phead->next;//定位下一个节点位置 free(pq->phead);//释放当前节点空间 pq->phead = cur;//头指针后移 } pq->size--;//数据个数减一 }
需要队头数据的返回类型
MyQDataType QueueFront(Queue* pq)
{
assert(pq);//断言,队列不存在时报错
assert(!QueueEmpty(pq));//断言,队列为空时报错
return pq->phead->data;//返回头指针指向数据块中的数据内容
}
需要队尾数据的返回类型
MyQDataType QueueBack(Queue* pq)
{
assert(pq);//断言,队列不存在时报错
assert(!QueueEmpty(pq));//断言,队列为空时报错
return pq->ptail->data;//返回尾指针指向数据块中的数据内容
}
队列数据个数由size 在负责记录,只需要返回size 的大小即可。
int QueueSize(Queue* pq)
{
assert(pq);//断言,队列不存在时报错
assert(!QueueEmpty(pq));//断言,队列为空时报错
return pq->size;//返回数据个数
}
void test1() { Queue pq;//创建队列 QueueInit(&pq);//队列初始化 //插入数据 1 ~ 6 QueuePush(&pq, 1); QueuePush(&pq, 2); QueuePush(&pq, 3); QueuePush(&pq, 4); QueuePush(&pq, 5); QueuePush(&pq, 6); //打印插入的数据 while (!QueueEmpty(&pq)) { printf("%5d\n", QueueFront(&pq));//打印队头数据 QueuePop(&pq);//使队头数据出队列 } QueueDestroy(&pq);//队列的销毁 } int main() { test1(); return; }
以上代码执行结果:
从结果上来看,实现了先入先出的原则。
一、操作系统调度
在操作系统中,队列被广泛应用于进程调度。操作系统需要根据进程的优先级、执行时间等因素来对进程进行调度。这时,可以使用队列来管理等待调度的进程。新到达的进程会排在队列的末尾,而正在执行的进程会从队列的头部取出。这种方式能够保证每个进程都能按照一定的顺序被执行,公平地利用系统资源。
二、网络通信
在网络通信中,队列可以用来处理请求和响应。例如,在Web服务器中,当有多个客户端请求访问网站时,服务器会将这些请求按照先后顺序放入队列中。服务器会从队列的头部取出请求,并进行处理和响应。通过队列的方式,能够有效地处理大量并发请求,保证服务器的稳定性和响应速度。
三、图像处理
在图像处理中,队列可以用来实现图像的滤波操作。滤波操作是图像处理中常用的一种操作,它可以对图像进行平滑、锐化、边缘检测等处理。一种常见的滤波操作是均值滤波,它使用一个滑动窗口在图像上移动,并计算窗口内像素的平均值,然后将平均值赋给滑动窗口的中心像素。这个滑动窗口的移动可以使用队列来实现,新的像素进入队列,旧的像素从队列中移出,从而实现滤波操作。
四、任务调度
在任务调度中,队列可以用来管理待执行的任务。例如,在生产线上,有多个任务需要按照一定的顺序执行,那么可以使用队列来管理这些任务。新的任务进入队列的末尾,而正在执行的任务从队列的头部取出。这样可以确保任务按照一定的顺序执行,提高生产效率。
五、消息传递
在分布式系统中,队列常常被用作消息传递的中间件。当多个系统之间需要传递消息时,可以使用队列来实现。发送者将消息放入队列中,接收者从队列中取出消息进行处理。队列可以保证消息的顺序和可靠性,确保消息的正确传递。
六、打印队列
在打印机管理中,队列可以用来管理打印任务。当多个用户同时提交打印任务时,这些任务可以按照先后顺序排队进入打印队列。打印机从队列中取出任务进行打印,直到队列为空。通过队列的方式,能够有效管理打印任务,保证打印机的高效利用。
队列结构是一种变相的链表结构,相比于单独的链表,它多了一个包含队头指针和队尾指针的结构体,但其实质上,仍旧属于链表结构。只要掌握了链表结构,队列结构的实现会变得比较简单。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。