我们在操作系统中会遇到疑似死机后,你的鼠标随便点也没什么作用,但过一会后,就会把你的鼠标操作都执行一遍,在这个过程中,就使用了队列这种数据结构的排序功能。
队列( queue )是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。允许插入的一端称为队尾,允许删除的一端称为队头。
一、链式队列
链式队列,类似于链表的一种队列,从空间上来说链式队列没有固定长度,比起顺序储存的循环队列来说,多了个指针域而已。
定义
需要定义两个结构体,一个是结点,一个是首尾指针
typedef int datatype;
typedef struct queue_node
{
datatype data;
queue_node* next; //下一个结点(结构体指针)
}node,*queueptr; //节点结构的指针
struct linked_queue //链式队列的结构
{
queueptr front, rear;
};
队列初始化
在队列初始化中,仅仅需要给队列结构体进行初始化,至于结点则在插入时申请。
int initqueue(linked_queue &q) //初始化链式队列结构,引用传递
{
q.front = q.rear = (queueptr)(malloc(sizeof(node)));
if (!q.front)
{
cout << "初始化失败" << endl;
return 0;
}
q.front->next = NULL; //此处一定要置空
cout << q.front->next << endl;
return 1;
}
插入操作
值得注意的一点是,如何把第一个元素的地址赋给头部结点的next ?
首先在初始化时,把头尾front,rear相等,而后赋值时使用rear.next=(节点地址),则可以解决。
int insert_queue(linked_queue &q, datatype x)//将x插入队列
{
queueptr s = (queueptr)malloc(sizeof(node));
if (!s)
{
cout << "申请失败" << endl;
return 0;
}
else
{
s->data = x;
s->next = NULL; //生成新的结点
q.rear->next = s;
//插入尾部,刚开始第一步时,因为头尾指针相同,所以头指针被赋予了第一个元素的地址
q.rear = s; //修改队尾指针
return 1;
}
}
弹出元素
在弹出时需要判断,是否队列为空?是否是首元素弹出?如果是首元素弹出还需要让头尾指针再次相等。
int pop_queue(linked_queue &q, datatype& e)//用e返回其值
{
queueptr p;
if (q.front == q.rear)
{
cout << "队列为空" << endl;
return 0;
}
p = q.front->next; //暂存头元素指针
e = p->data;
q.front = p->next;
if (q.rear = p) //若队头是队尾,删除后队尾指向队头
q.rear = q.front;
free(p);
return 1;
}
至于显示,计算队列中元素数量和链表中的一致。
二、循环队列
首先我们先来看一个问题,假设是长度为5的数组,初始状态为空队列,front 与rear指针均指向下标为0的位置。然后入队a1、a2,a3,a4,front指针依旧指向下标为0位置,rear指向4的位置。
出队a1、a2,则front指针指向下标为2的位置,rear 不变,再入队a5,此时front 指针不变,rear 指针移动到数组之外。可此时我们在0,1位置上还是空余的,这种现象称为假溢出。
所以解决这个问题的办法,就是后面满了,就再从头开始。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
在写代码时,我们需要考虑以下几个问题。
什么时候队列为空呢 ?很显然时front==rear的时候,为了防止两者相等时不知道是为空还是为满。我们修改规则,当队列满时,数组还有一个空余单位。
队列满的情况 :我们定义队列最大最大尺寸位queuesize,那队列满的条件就是(rear+1)%queuesize==front
。
队列的长度 :当rear<front时,长度为rear-front+queuesize,但当rear>front时,长度为rear-front,于是通用公式为 (rear-front+queuesize)%queuesize
定义及基本操作
typedef int datatype;
#define maxsize 1024
typedef struct circular_queue
{
datatype data[maxsize];
int front;
int rear;
}queue;
int initqueue(queue *q) //初始化
{
q->front = q->rear = 0;
return 1;
}
int length(queue* q) //求长度
{
return (q->rear - q->front + maxsize) % maxsize;
}
插入
int insert_queue(queue* q, datatype e)
{
if ((q->rear + 1) % maxsize == q->front)
{
cout << "循环队列为满" << endl;
return 0;
}
q->data[q->rear] = e;
q->rear = (q->rear + 1) % maxsize;
return 1;
}
删除
int pop_queue(queue* q, datatype* x)//弹出元素放到地址x中
{
if (q->front == q->rear)
{
cout << "循环队列为空" << endl;
return 0;
}
*x = q->data[q->front];
q->front = (q->front + 1) % maxsize;
return 1;
}
显示所有元素
int show_queue(queue* q)
{
if (q->front == q->rear)
{
cout << "循环队列为空" << endl;
return 0;
}
int i = q->front;
while ( i% maxsize != q->rear) //从头到尾
{
cout << q->data[i] << endl;
i++;
}
}
三、STL中的queue
首先是基本的队列,操作是十分简单的,毕竟轮子造好了。
#include<queue>//头文件
queue<typename>name;
back()返回最后一个元素
empty()如果队列空则返回真
front()返回第一个元素
pop()删除第一个元素
push()在末尾加入一个元素
size()返回队列中元素的个数
优先队列的基本操作
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。操作函数与上类似。
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。priority_queue<>的可支持的容器必须是用数组实现的容器
priority_queue<int> a;
//等同于 priority_queue<int, vector<int>, less<int> > a;
priority_queue<int, vector<int>, greater<int> > b;
//这样就是小顶堆
堆:通俗来讲利用完全二叉树的结构来维护的一维数组