循环队列与扩容
1.顺序队列操作
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct{
int *data;
int front, rear;
int length;
}SqQueue;
//1.队列初始化
SqQueue *InitQueue(int n){
SqQueue *sq = (SqQueue *)malloc(sizeof(SqQueue));
sq->data = (int *)malloc(sizeof(int) * n);
sq->length = n;
sq->front = sq->rear = 0;
return sq;
}
//2.队列销毁
void DestroyQueue(SqQueue *sq){
if (sq == NULL) return;
free(sq->data);
free(sq);
return;
}
//3.队列判空
int QueueEmpty(SqQueue *sq){
return sq->front == sq->rear;
}
//4.获取队列队首元素
int GetHead(SqQueue *sq){
if(sq->front != sq->rear) return sq->data[sq->front];
}
//5.队列入队
int Push(SqQueue *sq, int val){
if(sq == NULL) return 0;
if(sq->rear == sq->length) return 0;
sq->data[sq->rear] = val;
sq->rear++;
return 1;
}
//6.队列出队
int Pop(SqQueue *sq){
if(sq == NULL) return 0;
if(QueueEmpty(sq)) return 0;
sq->front += 1;
return 1;
}
void print(SqQueue *sq){
printf("Queue : [");
for(int i = sq->front; i < sq->rear; ++i){
i != sq->front && printf(", ");
printf("%d", sq->data[i]);
}
printf("]\n");
return ;
}
int main(){
srand(time(0));
#define MAX_OP 20
SqQueue *sq = InitQueue(10);
for(int i = 0; i < MAX_OP; ++i){
int op = rand() % 4;
int val = rand() % 100;
switch(op){
case 0:
case 1:
case 2:{
printf("push %d to the Queue = %d\n", val, Push(sq, val));
}break;
case 3:{
printf("pop %d from the Queue = ", GetHead(sq));
printf("%d\n", Pop(sq));
}break;
}
print(sq);
}
#undef MAX_OP
DestroyQueue(sq);
return 0;
}
普通队列存在假溢出现象,注意到最后5次push操作全部失败,但是queue队列中的元素却并没有达到设置的最大容量值10
2.循环顺序队列:
为了处理顺序队列的假溢出问题,
将普通队列修改为循环队列,程序中主要修改三个部分:
- 对于SqQueue结构体:增加一个count变量用于记录循环队列中实际存储的元素个数
typedef struct{
int *data;
int front, rear;
int length;//队列容量
int count;//实际存储的元素个数
}SqQueue;
- 对于push操作的修改:当尾指针达到队尾时,再添加元素将其重新调到队头
if(sq->rear == sq->length) sq->rear = 0;//尾指针置0
sq->count++;
- 对于pop操作的修改:当头指针达到队尾时,再删除元素则将其重新调到队头
if(sq->front == sq->length) sq->front = 0;//头指针置0
sq->count--;
循环顺序队列的实现:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct{
int *data;
int front, rear;
int length;
int count;
}SqQueue;
//1.队列初始化
SqQueue *InitQueue(int n){
SqQueue *sq = (SqQueue *)malloc(sizeof(SqQueue));
sq->data = (int *)malloc(sizeof(int) * n);
sq->length = n;
sq->front = sq->rear = 0;
sq->count = 0;
return sq;
}
//2.队列销毁
void DestroyQueue(SqQueue *sq){
if (sq == NULL) return;
free(sq->data);
free(sq);
return;
}
//3.队列判空
int QueueEmpty(SqQueue *sq){
return sq->count == 0;
}
//4.获取队列队首元素
int GetHead(SqQueue *sq){
if(sq->front != sq->rear) return sq->data[sq->front];
}
//5.队列入队
int Push(SqQueue *sq, int val){
if(sq == NULL) return 0;
if(sq->count == sq->length) return 0;
sq->data[sq->rear++] = val;
if(sq->rear == sq->length) sq->rear = 0;//尾指针置0
sq->count++;
return 1;
}
//6.队列出队
int Pop(SqQueue *sq){
if(sq == NULL) return 0;
if(QueueEmpty(sq)) return 0;
sq->front++;
if(sq->front == sq->length) sq->front = 0;//头指针置0
sq->count--;
return 1;
}
void print(SqQueue *sq){
printf("Queue(%d) : [", sq->count);
for(int i = 0; i < sq->count; ++i){
i && printf(", ");
printf("%d", sq->data[(i + sq->front) % sq->length]);
}
printf("]\n");
return ;
}
int main(){
srand(time(0));
#define MAX_OP 20
SqQueue *sq = InitQueue(10);
for(int i = 0; i < MAX_OP; ++i){
int op = rand() % 4;
int val = rand() % 100;
switch(op){
case 0:
case 1:
case 2:{
printf("push %d to the Queue = %d\n", val, Push(sq, val));
}break;
case 3:{
printf("pop %d from the Queue = ", GetHead(sq));
printf("%d\n", Pop(sq));
}break;
}
print(sq);
}
#undef MAX_OP
DestroyQueue(sq);
return 0;
}
3.循环顺序队列扩容:
当循环顺序队列真的达到最大容量时(真溢出),可以对其进行扩容操作:
注意:注意:如果使用realloc进行扩容,则队列中的元素很可能会出现顺序错乱,故应该使用malloc或者calloc实现扩容操作
关键代码:
//SqQueue扩容操作
int expand(SqQueue *sq){
int extend_size = sq->length;//扩容一倍
int *p;
//(1)开辟新的空间
while(extend_size){
p = (int *)malloc(sizeof(int) * (sq->length + extend_size));
if(p != NULL) break;//malloc扩容成功
extend_size >>= 1;//malloc扩容失败则将扩容容量extend缩小,再次尝试扩容
}
if(p == NULL) return 0;//循环扩容失败
//(2)将原队列数据转移到新队列中
for(int i = 0; i < sq->count; ++i){
p[i] = sq->data[(i + sq->front) % sq->length];//逐个将队列中的元素进行转移
}
//手动释放原队列空间
free(sq->data);
sq->data = p;
//更改新队列基本信息
sq->front = 0, sq->rear = sq->count;
sq->length += extend_size;
return 1;
}
//5.队列入队
int Push(SqQueue *sq, int val){
if(sq == NULL) return 0;
if(sq->count == sq->length){
//循环顺序队列已满需要扩容
if(!expand(sq)){
printf(RED("fail to expand !\n"));
return 0;
}
printf(GREEN("success to expand! the new length = %d\n"), sq->length);
}
sq->data[sq->rear++] = val;
if(sq->rear == sq->length) sq->rear = 0;//尾指针置0
sq->count++;
return 1;
}
完整程序:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define COLOR(a, b) "\033[" #b "m" a "\033[0m"
#define RED(a) COLOR(a, 34)
#define GREEN(a) COLOR(a, 32)
typedef struct{
int *data;
int front, rear;
int length;//容量
int count;//元素个数
}SqQueue;
//1.队列初始化
SqQueue *InitQueue(int n){
SqQueue *sq = (SqQueue *)malloc(sizeof(SqQueue));
sq->data = (int *)malloc(sizeof(int) * n);
sq->length = n;
sq->front = sq->rear = 0;
sq->count = 0;
return sq;
}
//2.队列销毁
void DestroyQueue(SqQueue *sq){
if (sq == NULL) return;
free(sq->data);
free(sq);
return;
}
//3.队列判空
int QueueEmpty(SqQueue *sq){
return sq->count == 0;
}
//4.获取队列队首元素
int GetHead(SqQueue *sq){
if(sq->front != sq->rear) return sq->data[sq->front];
}
//SqQueue扩容操作
int expand(SqQueue *sq){
int extend_size = sq->length;//扩容一倍
int *p;
//(1)开辟新的空间
while(extend_size){
p = (int *)malloc(sizeof(int) * (sq->length + extend_size));
if(p != NULL) break;//malloc扩容成功
extend_size >>= 1;//malloc扩容失败则将扩容容量extend缩小,再次尝试扩容
}
if(p == NULL) return 0;//循环扩容失败
//(2)将原队列数据转移到新队列中
for(int i = 0; i < sq->count; ++i){
p[i] = sq->data[(i + sq->front) % sq->length];//逐个将队列中的元素进行转移
}
//手动释放原队列空间
free(sq->data);
sq->data = p;
//更改新队列基本信息
sq->front = 0, sq->rear = sq->count;
sq->length += extend_size;
return 1;
}
//5.队列入队
int Push(SqQueue *sq, int val){
if(sq == NULL) return 0;
if(sq->count == sq->length){
//循环顺序队列已满需要扩容
if(!expand(sq)){
printf(RED("fail to expand !\n"));
return 0;
}
printf(GREEN("success to expand! the new length = %d\n"), sq->length);
}
sq->data[sq->rear++] = val;
if(sq->rear == sq->length) sq->rear = 0;//尾指针置0
sq->count++;
return 1;
}
//6.队列出队
int Pop(SqQueue *sq){
if(sq == NULL) return 0;
if(QueueEmpty(sq)) return 0;
sq->front++;
if(sq->front == sq->length) sq->front = 0;//头指针置0
sq->count--;
return 1;
}
void print(SqQueue *sq){
printf("Queue(%d) : [", sq->count);
for(int i = 0; i < sq->count; ++i){
i && printf(", ");
printf("%d", sq->data[(i + sq->front) % sq->length]);
}
printf("]\n");
return ;
}
int main(){
srand(time(0));
#define MAX_OP 20
SqQueue *sq = InitQueue(1);
for(int i = 0; i < MAX_OP; ++i){
int op = rand() % 4;
int val = rand() % 100;
switch(op){
case 0:
case 1:
case 2:{
printf("push %d to the Queue = %d\n", val, Push(sq, val));
}break;
case 3:{
printf("pop %d from the Queue = ", GetHead(sq));
printf("%d\n", Pop(sq));
}break;
}
print(sq);
}
#undef MAX_OP
DestroyQueue(sq);
return 0;
}
4.链队列操作:
对比于顺序队列,链队列不会出现假溢出(循环顺序队列解决)和真溢出(循环顺序队列扩容解决)的情况,实现较为简单:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct LinkQueue {
Node *front, *rear;
int length;
} LinkQueue;
Node *InitNode(int val) {
Node *p = (Node *)malloc(sizeof(Node));
p->data = val;
p->next = NULL;
return p;
}
LinkQueue *InitLinkQueue() {
LinkQueue *lq = (LinkQueue *)malloc(sizeof(LinkQueue));
lq->front->next = NULL;
lq->rear = lq->front;
lq->length = 0;
return lq;
}
void DestroyNode(Node *node) {
if (node == NULL) return;
free(node);
return;
}
void DestroyQueue(LinkQueue *lq) {
if (lq == NULL) return;
Node *p = lq->front->next, *temp;
while (p != NULL) {
temp = p->next;
DestroyNode(p);
p = temp;
}
free(lq);
return;
}
int Empty(LinkQueue *lq) {
return lq->length == 0;
}
int Push(LinkQueue *lq, int val) {
if (lq == NULL) return 0;
Node *temp = InitNode(val);
lq->rear->next = temp;
lq->rear = temp;
lq->length += 1;
return 1;
}
int Pop(LinkQueue *lq) {
if (lq == NULL) return 0;
if (Empty(lq)) return 0;
Node *temp = lq->front->next;
lq->front->next = lq->front->next->next;
DestroyNode(temp);
lq->length -= 1;
if (lq->length == 0) lq->rear = lq->front;
return 1;
}
int GetHead(LinkQueue *lq) {
return lq->front->next->data;
}
void print(LinkQueue *lq) {
if (lq == NULL) return;
printf("Queue(%d) : [", lq->length);
for (Node *p = lq->front->next; p != NULL; p = p->next) {
p != lq->front->next && printf(", ");
printf("%d", p->data);
}
printf("]\n");
return;
}
int main(){
srand(time(0));
LinkQueue *lq = InitLinkQueue();
#define MAX_OP 20
for (int i = 0; i < MAX_OP; ++i) {
int op = rand() % 4;
int val = rand() % 100;
switch (op) {
case 0:
case 1:
case 2: {
printf("push %d to the Queue = %d\n", val, Push(lq, val));
} break;
case 3: {
if (!Empty(lq)) {
printf("pop %d from the Queue = ", GetHead(lq));
printf("%d\n", Pop(lq));
}
} break;
}
print(lq);
}
#undef MAX_OP
DestroyQueue(lq);
return 0;
}
报错原因分析:初始化链队列时出现segment fault访问越界错误
在调用InitLinkQueue()
方法时,lq->front->next = NULL;
访问越界第一个node结点并不存在,无法访问其next域。
设置虚拟头结点处理:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct LinkQueue {
Node front, *rear;//虚拟头结点
int length;
} LinkQueue;
Node *InitNode(int val) {
Node *p = (Node *)malloc(sizeof(Node));
p->data = val;
p->next = NULL;
return p;
}
LinkQueue *InitLinkQueue() {
LinkQueue *lq = (LinkQueue *)malloc(sizeof(LinkQueue));
lq->front.next = NULL;
lq->rear = &(lq->front);
lq->length = 0;
return lq;
}
void DestroyNode(Node *node) {
if (node == NULL) return;
free(node);
return;
}
void DestroyQueue(LinkQueue *lq) {
if (lq == NULL) return;
Node *p = lq->front.next, *temp;
while (p != NULL) {
temp = p->next;
DestroyNode(p);
p = temp;
}
free(lq);
return;
}
int Empty(LinkQueue *lq) {
//return lq->rear == NULL;
return lq->length == 0;
}
int Push(LinkQueue *lq, int val) {
if (lq == NULL) return 0;
Node *temp = InitNode(val);
lq->rear->next = temp;
lq->rear = temp;
lq->length += 1;
return 1;
}
int Pop(LinkQueue *lq) {
if (lq == NULL) return 0;
if (Empty(lq)) return 0;
Node *temp = lq->front.next;
lq->front.next = lq->front.next->next;
DestroyNode(temp);
lq->length -= 1;
if (lq->length == 0) lq->rear = &(lq->front);
return 1;
}
int GetHead(LinkQueue *lq) {
return lq->front.next->data;
}
void print(LinkQueue *lq) {
if (lq == NULL) return;
printf("Queue(%d) : [", lq->length);
for (Node *p = lq->front.next; p != NULL; p = p->next) {
p != lq->front.next && printf(", ");
printf("%d", p->data);
}
printf("]\n");
return;
}
int main(){
srand(time(0));
LinkQueue *lq = InitLinkQueue();
#define MAX_OP 20
for (int i = 0; i < MAX_OP; ++i) {
int op = rand() % 4;
int val = rand() % 100;
switch (op) {
case 0:
case 1:
case 2: {
printf("push %d to the Queue = %d\n", val, Push(lq, val));
} break;
case 3: {
if (!Empty(lq)) {
printf("pop %d from the Queue = ", GetHead(lq));
printf("%d\n", Pop(lq));
}
} break;
}
print(lq);
}
#undef MAX_OP
DestroyQueue(lq);
return 0;
}
报错成功解决
报错最后修改于2022.5.3