顺序循环队列
一 顺序表循环队列
1.1 顺序循环队列定义
队列是一种运算受限的先进先出线性表,仅允许在队尾插入(入队),在队首删除(出队)。新元素入队后成为新的队尾元素,元素出队后其后继元素就成为队首元素。
队列的顺序存储结构使用一个数组和两个整型变量实现,其结构如下:
1 struct Queue{ 2 ElemType elem[MaxSize]; 3 int head, tail; 4 };
即利用数组elem顺序存储队列中的所有元素,利用整型变量head和tail分别存储队首元素和队尾(或队尾下一个)元素的下标位置,分别称为队首指针和队尾指针。MaxSize指定顺序存储队列的最大长度,即队列中最多能够存储的元素个数。当队列的顺序存储空间静态分配时,MaxSize通常为宏定义;若动态分配时,还可为全局或局部变量。
单向顺序队列存在“假溢出”问题,即多次入队和出队操作后,队首空余许多位置而队尾指针指向队列中最后一个元素位置,从而无法使新的数据元素入队。假溢出本质在于队首和队尾指针值不能由所定义数组下界值自动转为数组上界值,解决办法是将顺序队列构造成一个逻辑上首尾相连的循环表。当队列的第MaxSize-1个位置被占用后,只要队首还有可用空间,则把新的数据元素插入队列第0号位置。因此,顺序队列通常都采用顺序循环队列结构。
下图所示为MaxSize=4的循环队列三种状态图:
其中,为区分队空与队满,在入队时少用一个数据(图中下标为3的位置)。本文约定队首指针head指向队首元素,队尾指针tail指向队尾元素的下一个元素,以队尾指针加1等于队首指针判断队满,即:
- 初始化:head = tail = 0;
- 队列空:head == tail;
- 队列满:(tail + 1) % MaxSize == head;
- 元素数:num = (tail - head + MaxSize) % MaxSize
%为取模运算符,循环队列利用数学上的取模运算将首尾巧妙相连。
当约定head指向队首前一个元素,tail指向队尾元素时,元素数目仍满足上面的公式。当约定head指向队首元素,tail指向队尾元素时,元素数目为(tail - head + 1 + MaxSize) % MaxSize。
此外,也可另设一个标志以区别队空和队满。该标志可为布尔型空满标志位,也可为队列中元素个数。
通过队首元素位置和队列中元素个数,可计算出队尾元素所在位置。亦即,可用队列中元素个数代替队尾指针(或队首指针)。此时,队列满足:
- 队列空:num == 0;
- 队列满:num == MaxSize;
- 队尾位置:tail = (head + num + MaxSize) % MaxSize
代码实现如下:
circularQueue.c
/*初始化:head = tail = 0; * 队列空:head == tail; * 队列满:(tail + 1) % MaxSize == head; * 元素数:num = (tail - head + MaxSize) % MaxSize * */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include "circularQueue.h" //判断循环队列是否为空 int IsQueEmpty(P_CIRCULAR_QUEUE cQue) { return cQue->cqHead == cQue->cqTail; } //判断循环队列是否满 int IsQueFull(P_CIRCULAR_QUEUE cQue) { //为了区分队空的情况和队满的情况,使用+1来空出一个数据 return (cQue->cqTail + 1) % cQue->maxSize == cQue->cqHead; } //获取循环队列的大小 int getQueueSize(P_CIRCULAR_QUEUE cQue) { cQue->size = (cQue->cqTail - cQue->cqHead + cQue->maxSize) % cQue->maxSize; printf("cqTail[%d], cqHead[%d], size[%d]\n", cQue->cqTail, cQue->cqHead, cQue->size); return cQue->size; } //获取循环队列队首的位置 int getQueueHead(P_CIRCULAR_QUEUE cQue) { return cQue->cqHead; } //获取循环队列队首元素 int getQueueHeadData(P_CIRCULAR_QUEUE cQue) { return cQue->data[cQue->cqHead]; } //获取循环队列队尾的位置 int getQueueTail(P_CIRCULAR_QUEUE cQue) { return cQue->cqTail; } //获取循环队列队首元素 int getQueueTailData(P_CIRCULAR_QUEUE cQue) { return cQue->data[cQue->cqTail]; } //初始化循环队列 void InitCircularQue(P_CIRCULAR_QUEUE cQue, int maxsize) { printf("cque size =%zu\n", sizeof(*cQue)); cQue->data = (int*)malloc(sizeof(int)*maxsize); //memset(cQue, 0, sizeof(*cQue)); cQue->cqTail = 0; cQue->cqHead = 0; cQue->size = 0; cQue->maxSize = maxsize; printf("cqHead[%d], cqTail[%d], maxSize[%d]\n", cQue->cqHead, cQue->cqTail, cQue->maxSize); } //向循环队列中插入元素 void enterCircularQue(P_CIRCULAR_QUEUE cQue, int elem) { if(IsQueFull(cQue)) { printf("Elem %d can't push to CircularQueue %p (Full)!\n", elem, cQue); return; } //cQue->data[cQue->cqTail] = elem; int *p = cQue->data; p[cQue->cqTail] = elem; cQue->cqTail = (cQue->cqTail + 1)%cQue->maxSize; printf("cqTail ==%d \n", cQue->cqTail); } //从循环队列中取数据 int leaveCircularQue(P_CIRCULAR_QUEUE cQue) { if(IsQueEmpty(cQue)) { printf("Queue %p is Empty! \n", cQue); return -1; } int elem = cQue->data[cQue->cqHead]; cQue->cqHead = (cQue->cqHead + 1)%cQue->maxSize; printf("cqHead == %d \n", cQue->cqHead); return elem; } //显示队列中的所有元素 void ShowQue(P_CIRCULAR_QUEUE cQue) { if(IsQueEmpty(cQue)) { printf("Queue %p is Empty! \n", cQue); return ; } printf("CircularQueue Element: "); int elemIdx = cQue->cqHead; while((elemIdx % cQue->maxSize) != cQue->cqTail) printf("%d ", cQue->data[(elemIdx++) % cQue->maxSize]); printf("\n"); } void delQue(P_CIRCULAR_QUEUE cQue) { cQue->cqTail = cQue->cqHead = 0; FREE(cQue->data); }
circularQueue.h
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define FREE(p) \ if (p != NULL) {\ free(p);\ p = NULL;\ } typedef struct{ //int data[QUEUE_SIZE]; //队列中的元素 int *data; int cqHead; //指向队首元素 int cqTail; //指向队尾元素 int size; //当前队列的大小 int maxSize; //可以容纳的最大大小 }CIRCULAR_QUEUE, *P_CIRCULAR_QUEUE; int IsQueEmpty(P_CIRCULAR_QUEUE cQue); int IsQueFull(P_CIRCULAR_QUEUE cQue); int getQueueSize(P_CIRCULAR_QUEUE cQue); int getQueueHead(P_CIRCULAR_QUEUE cQue); int getQueueHeadData(P_CIRCULAR_QUEUE cQue); int getQueueTail(P_CIRCULAR_QUEUE cQue); int getQueueTailData(P_CIRCULAR_QUEUE cQue); //队列是先进先出FIFO void InitCircularQue(P_CIRCULAR_QUEUE cQue, int maxsize); void enterCircularQue(P_CIRCULAR_QUEUE cQue, int elem); int leaveCircularQue(P_CIRCULAR_QUEUE cQue); void ShowQue(P_CIRCULAR_QUEUE cQue); void delQue(P_CIRCULAR_QUEUE cQue);
main.c
#include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<string.h> #include"circularQueue.h" #define QUEUE_SIZE 5 //队列中最大容纳QUEUE_SIZE-1个元素. int main() { CIRCULAR_QUEUE gcQue; InitCircularQue(&gcQue, QUEUE_SIZE); printf("Push circularQueue 1,2,3,4,5,6,7..\n"); enterCircularQue(&gcQue, 1); enterCircularQue(&gcQue, 2); enterCircularQue(&gcQue, 3); enterCircularQue(&gcQue, 4); enterCircularQue(&gcQue, 5); enterCircularQue(&gcQue, 6); enterCircularQue(&gcQue, 7); printf("CircularQueue Elem Num %d\n", getQueueSize(&gcQue)); ShowQue(&gcQue); printf("\nPop Queue...\n"); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); delQue(&gcQue); printf("\n Push 6, 7 \n"); enterCircularQue(&gcQue, 6); enterCircularQue(&gcQue, 7); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); printf("Pop %d \n", leaveCircularQue(&gcQue)); ShowQue(&gcQue); return 0; }
测试结果如下:
cque size =36 Push circularQueue 1,2,3,4,5,6,7.. cqTail ==1 cqTail ==2 cqTail ==3 cqTail ==4 Elem 5 can't push to CircularQueue 0x7fff579c15a0 (Full)! Elem 6 can't push to CircularQueue 0x7fff579c15a0 (Full)! Elem 7 can't push to CircularQueue 0x7fff579c15a0 (Full)! cqTail[4], cqHead[0], size[4] CircularQueue Elem Num 4 CircularQueue Element: 1 2 3 4 Pop Queue... Pop 1 CircularQueue Element: 2 3 4 Pop 2 CircularQueue Element: 3 4 Pop 3 CircularQueue Element: 4 Push 6, 7 cqTail ==0 cqTail ==1 Pop 4 CircularQueue Element: 6 7 Pop 6 CircularQueue Element: 7 Pop 7 Queue 0x7fff579c15a0 is Empty!