【上机实验】03:循环队列与扩容

循环队列与扩容


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

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值