转载:https://www.cnblogs.com/muzijie/p/5655228.html
1 链队列的存储结构
将对头指针front指向链队列的头结点,队尾指针rear指向终端结点。
空队列时,头指针front和尾指针rear都指向头结点。
链队列的存储结构为:
typedef int QElemType;
typedef struct QNode { //结点结构
QElemType data;
struct QNode *next;
}QNode;
typedef struct QNode * QueuePtr;
typedef struct { //队列的链表结构
QueuePtr rear;
QueuePtr front;
}LinkQueue;
2 入队操作
//插入元素e为Q的新的队尾结点
Status EnQueue(QueuePtr Q, QElemType e) {
QueuePtr q = (QueuePtr)malloc(sizeof(QNode));
if (!q) { //存储分配失败
exit(OVERFLOW);
}
q->data = e;
q->next = NULL;
Q->rear->next = q;
Q->rear = q;
return OK;
}
3 出队操作
出队操作,就是头结点的后继结点出队,将头结点的后继改为它后面的结点。
若链表除头结点外只剩一个元素时,则需将rear指针指向头结点。
//若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR。
Status DeQueue(QueuePtr Q, QElemType *e) {
QueuePtr q;
if (Q->rear == Q->front) { //空队列
return ERROR;
}
q = Q->front->next; //q指向第一个结点
*e = q->data;
Q->front->next = q->next;
if (Q->rear == p) { //若队头就是队尾,删除后,需要将rear指针指向头结点
Q->rear = Q->front;
}
free(q);
return OK;
}
4 循环队列与链队列的比较
从时间上考虑,循环队列和链队列的基本操作都是O(1),不过循环队列是事先已申请好空间,使用期间不会释放。而对于链队列,每次申请和释放结点也会存在一些时间开销。如果入队和出队频繁,两者还是有细微差异的。
从空间来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但是是可以接受的。所以从空间上说,链队列更加灵活。
总的来说,在可以确定链队列最大长度的情况下,建议使用循环队列。如果无法预估队列的长度,则使用链队列。
转载:https://www.cnblogs.com/hughdong/archive/2017/05/11/6841970.html
(作者说的挺有意思的话:You know something and I know nothing.)
和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队头到队尾的元素外,尚需敷设两个指针front和rear分别指示队列头元素位置和队列尾元素的位置。
如果使用顺序表作为队列的话,当处于右图状态则不能继续插入新的队尾元素,否则会因为数组越界而导致程序代码被破坏。
由此产生了由链表实现的循环队列,只有队列未满时才可以插入新的队尾元素。
下面内容,转载:https://www.cnblogs.com/chenliyang/p/6554141.html
1.图中有两个指针(其实就是两个整数型变量,因为在这里有指示作用,所以这里理解为指针)front、rear,一个指示队头,一个指示队尾。
2.rear和front互相追赶着,这个追赶过程就是队列添加和删除的过程,如果rear追到head说明队列满了,如果front追到rear说明队列。
说明:令队列空间中的一个单元闲置,使得队列非空时,Q.rear与Q.front之间至少间隔一个空闲单。(思考为什么空一格)
3.我们把它掰弯,用的是求余,这样两个值就不会跑出最大范围,并且可以实现弯曲的效果,所以说对于循环队列我们必须给定最大值MAXQSIZE。
这其实是我们臆想的,反正我们要做的就是利用循环来解决空间浪费的问题。
☆当添加一个元素时,(rear+1)%MAXQSIZE; //理解为什么求余?
☆当删除一个元素时,(front+1)%MAXQSIZE;//理解为什么求余?
☆当rear=front的时候,队列可能是满,也可能是空。(这也是为什么空一格的原因)
因为存在满和空两种情况,我们需要分别判断:
☆满:当队列添加元素到rear的下一个元素是head的时候,也就是转圈子要碰头了,我们就认为队列满了。(Q.rear+1)%MAXSIZE=Q.front
☆空:当队列删除元素到head=rear的时候,我们认为队列空了。Q.rear==Q.front,不一定为0
上面这一段要好好理解,在其他编程的地方,也会用到类似的思想。
下面的代码思想很重要。
2.1对节点的定义
#define MAXQSIZE 100
typedef int QElemtype;
typedef int status;
typedef struct{
QElemtype *base;
int front;
int rear;
}SqQueue;
2.2初始化队列
SqQueue* InitQueue()
{
SqQueue *q;
q=new SqQueue;
q->base=new int[MAXQSIZE];
q->rear=q->front=0;
return q;
}
2.3添加操作
status EnQueue(SqQueue *q,QElemtype e)
{
//插入到队尾
if((q->rear+1)%MAXQSIZE==q->front)
return 0;
q->base[q->rear]=e;
q->rear=(q->rear+1)%MAXQSIZE;
return 1;
}
2.4删除操作
status DeQueue(SqQueue *q)
{
if(q->front==q->rear)
return 0;
printf("%d",q->base[q->front]);
q->front =(q->front+1)%MAXQSIZE;
return 1;
}
备注,这插入和删除的操作,类似于标记。(这也很重要)
2.5获取队列长度
int QueueLength(SqQueue *q)
{
return (q->rear-q->front+MAXQSIZE)%MAXQSIZE;
}
补:还有些其他的队列,比如优先队列,双端队列。(用的时候可以查STL)
队列练习:
团体队列
并行程序模拟