数据结构(十三)队列的顺序存储结构(循环队列)
一、队列:队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
二、队列在程序设计中的应用:键盘输入、操作系统多个程序因需要通过一个管道输出而按先后次序排队等待等。
三、队列顺序存储的不足:把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即是队头。入队列操作就是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为O(1)。但是,与栈不同的是,队列元素的出列是在队头,即下标为0的位置,那也就意味着,队列中的所有元素都得向前移动,以保证队列的队头,也就是下标为0的位置不为空,此时时间复杂度为O(n)。
四、如果不去限制队列的元素必须存储在数组的前n个单元这一条件,出队的性能就会大大增加,也就是,队头不需要一定在下标为0的位置。
五、为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,所以引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear时,次队列不是还剩一个元素,而是空队列。
六、“假溢出”现象就是:数组前面的位置是空闲的,而数组后面的位置已经不够了。解决假溢出的办法就是后面满了,就再重头开始,也就是头尾相接的循环。把队列的这种头尾相接的顺序存储结构称为循环队列。
七、不循环时,当front等于rear时,队列是空队列。但是,使用循环后,front等于rear时,队列是可能是满的。为了解决这个问题,采用这个方法:当队列空时,front=rear;当队列满是,修改条件,保留一个元素空间,也就是说,队列满时,数组中还有一个空闲单元。尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。
八、假设队列的最大尺寸为QueueSize,那么队列满的条件是(rear+1) % QueueSize == front。
九、由于rear > front时,队列长度等于rear - front;当rear < front时,队列长度等于(0+rear) + (QueueSize-front)。因此,通用的队列长度计算公式为:(rear-front+QueueSize)% QueueSize。
十、循环队列的C语言代码实现:
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ /* 循环队列的顺序存储结构 */ typedef struct { QElemType data[MAXSIZE]; int front; /* 头指针 */ int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */ }SqQueue; Status visit(QElemType c) { printf("%d ",c); return OK; } /* 初始化一个空队列Q */ Status InitQueue(SqQueue *Q) { Q->front=0; Q->rear=0; return OK; } /* 将Q清为空队列 */ Status ClearQueue(SqQueue *Q) { Q->front=Q->rear=0; return OK; } /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */ Status QueueEmpty(SqQueue Q) { if(Q.front==Q.rear) /* 队列空的标志 */ return TRUE; else return FALSE; } /* 返回Q的元素个数,也就是队列的当前长度 */ int QueueLength(SqQueue Q) { return (Q.rear-Q.front+MAXSIZE)%MAXSIZE; } /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ Status GetHead(SqQueue Q,QElemType *e) { if(Q.front==Q.rear) /* 队列空 */ return ERROR; *e=Q.data[Q.front]; return OK; } /* 若队列未满,则插入元素e为Q新的队尾元素 */ Status EnQueue(SqQueue *Q,QElemType e) { if ((Q->rear+1)%MAXSIZE == Q->front) /* 队列满的判断 */ return ERROR; Q->data[Q->rear]=e; /* 将元素e赋值给队尾 */ Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */ /* 若到最后则转到数组头部 */ return OK; } /* 若队列不空,则删除Q中队头元素,用e返回其值 */ Status DeQueue(SqQueue *Q,QElemType *e) { if (Q->front == Q->rear) /* 队列空的判断 */ return ERROR; *e=Q->data[Q->front]; /* 将队头元素赋值给e */ Q->front=(Q->front+1)%MAXSIZE; /* front指针向后移一位置, */ /* 若到最后则转到数组头部 */ return OK; } /* 从队头到队尾依次对队列Q中每个元素输出 */ Status QueueTraverse(SqQueue Q) { int i; i=Q.front; for(i = Q.front; i != Q.rear; i = (i + 1) % MAXSIZE){ visit(Q.data[i]); } /* while((i+Q.front)!=Q.rear) { visit(Q.data[i]); i=(i+1)%MAXSIZE; } */ printf("\n"); return OK; } int main() { Status j; int i=0,l; QElemType d; SqQueue Q; InitQueue(&Q); printf("初始化队列后,队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ",MAXSIZE-1); do { /* scanf("%d",&d); */ d=i+100; if(d==-1) break; i++; EnQueue(&Q,d); }while(i<MAXSIZE-1); printf("队列长度为: %d\n",QueueLength(Q)); printf("现在队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); printf("连续%d次由队头删除元素,队尾插入元素:\n",MAXSIZE); for(l=1;l<=MAXSIZE;l++) { DeQueue(&Q,&d); printf("删除的元素是%d,插入的元素:%d \n",d,l+1000); /* scanf("%d",&d); */ d=l+1000; EnQueue(&Q,d); } l=QueueLength(Q); printf("现在队列中的元素为: \n"); QueueTraverse(Q); printf("共向队尾插入了%d个元素\n",i+MAXSIZE); if(l-2>0) printf("现在由队头删除%d个元素:\n",l-2); while(QueueLength(Q)>2) { DeQueue(&Q,&d); printf("删除的元素值为%d\n",d); } printf("现在队列中的元素为: \n"); QueueTraverse(Q); j=GetHead(Q,&d); if(j) printf("现在队头元素为: %d\n",d); ClearQueue(&Q); printf("清空队列后, 队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); return 0; } 输出为: 初始化队列后,队列空否?1(1:空 0:否) 请输入整型队列元素(不超过19个),-1为提前结束符: 队列长度为: 19 现在队列空否?0(1:空 0:否) 连续20次由队头删除元素,队尾插入元素: 删除的元素是100,插入的元素:1001 删除的元素是101,插入的元素:1002 删除的元素是102,插入的元素:1003 删除的元素是103,插入的元素:1004 删除的元素是104,插入的元素:1005 删除的元素是105,插入的元素:1006 删除的元素是106,插入的元素:1007 删除的元素是107,插入的元素:1008 删除的元素是108,插入的元素:1009 删除的元素是109,插入的元素:1010 删除的元素是110,插入的元素:1011 删除的元素是111,插入的元素:1012 删除的元素是112,插入的元素:1013 删除的元素是113,插入的元素:1014 删除的元素是114,插入的元素:1015 删除的元素是115,插入的元素:1016 删除的元素是116,插入的元素:1017 删除的元素是117,插入的元素:1018 删除的元素是118,插入的元素:1019 删除的元素是1001,插入的元素:1020 现在队列中的元素为: 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 共向队尾插入了39个元素 现在由队头删除17个元素: 删除的元素值为1002 删除的元素值为1003 删除的元素值为1004 删除的元素值为1005 删除的元素值为1006 删除的元素值为1007 删除的元素值为1008 删除的元素值为1009 删除的元素值为1010 删除的元素值为1011 删除的元素值为1012 删除的元素值为1013 删除的元素值为1014 删除的元素值为1015 删除的元素值为1016 删除的元素值为1017 删除的元素值为1018 现在队列中的元素为: 1019 1020 现在队头元素为: 1019 清空队列后, 队列空否?1(1:空 0:否)
十一、循环队列的Java语言代码实现:
- 接口类:
package bigjun.iplab.sequenceQueue; public interface QueueINF { // 判断顺序队列是否为空 public boolean isqueueEmpty(); // 将一个已经存在的顺序队列置成空表 public void queueClear(); // 求顺序队列的长度 public int queueLength(); // 读取顺序队列的队列队头元素 public int getHeadElem() throws Exception; // 在顺序队列的队尾插入元素e public void queueEnter(int e) throws Exception; // 删除顺序队列队头元素 public void queueDel() throws Exception ; // 输出顺序队列中的所有元素 public void queueTraverse() throws Exception; }
- 实现类:
package bigjun.iplab.sequenceQueue; public class SequenceQueue implements QueueINF { private final static int MAXSIZE = 20; private int[] queueElem; private int front; private int rear; public SequenceQueue() { queueElem = new int[MAXSIZE]; front = rear = 0; } @Override public boolean isqueueEmpty() { return front == rear; } public void queueClear() { front=rear=0; } public int queueLength() { return (rear - front + MAXSIZE) % MAXSIZE; } public int getHeadElem() throws Exception { if (front == rear) throw new Exception("顺序队列为空,无法获取队头元素"); return queueElem[front]; } public void queueEnter(int e) throws Exception { if ((rear + 1) % MAXSIZE == front) throw new Exception("队列为满的,无法实现入队列操作"); queueElem[rear] = e; rear = (rear + 1) % MAXSIZE; } public void queueDel() throws Exception { if (front == rear) throw new Exception("顺序队列为空,无法删除队头元素"); front = (front + 1 ) % MAXSIZE; } public void queueTraverse() throws Exception { if (front == rear) throw new Exception("顺序队列为空,无法获取队头元素"); System.out.print("此时顺序队列的元素为: "); for (int i = front; i != rear; i = (i + 1 ) % MAXSIZE) { System.out.print(queueElem[i] + " "); } System.out.println(); } public static void main(String[] args) throws Exception { int i = 0; SequenceQueue sQueue = new SequenceQueue(); System.out.println("队列是否为空: " + sQueue.isqueueEmpty()); do { int d = i + 100; i++; sQueue.queueEnter(d); } while (i < MAXSIZE - 1); // 这里留一个位置为空,注意一下。 System.out.println("队列是否为空: " + sQueue.isqueueEmpty()); System.out.println("队列长度为: " + sQueue.queueLength()); sQueue.queueTraverse(); for (int j = 1; j <= MAXSIZE; j++) { sQueue.queueDel(); int f = j + 1000; sQueue.queueEnter(f); } sQueue.queueTraverse(); for (int l = 1; l <= 17 ; l++) { sQueue.queueDel(); } System.out.println("队列长度为: " + sQueue.queueLength()); sQueue.queueTraverse(); System.out.println("队列的队头为: " + sQueue.getHeadElem()); sQueue.queueDel(); System.out.println("队列的队头为: " + sQueue.getHeadElem()); } }
- 输出:
队列是否为空: true 队列是否为空: false 队列长度为: 19 此时顺序队列的元素为: 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 此时顺序队列的元素为: 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 队列长度为: 2 此时顺序队列的元素为: 1019 1020 队列的队头为: 1019 队列的队头为: 1020