【数据结构】【王道】【栈与队列】循环队列的实现及基本操作(使用顺序队列)(可直接运行)

总目录

1.说明

此处选择了略过一般队列而直接实现循环队列。一般队列如果以队尾指针rear==MaxSize作为队满条件,可能会产生一个问题:“假溢出”。队列是一种操作受限的线性表,入队是在队尾入队,出队是在队头出队,在不出队的情况下,如果出现rear==Maxsize,即表示已经队满,但是如果在此时进行出队,由于出队是在队头操作,即使队尾满了,进行出队操作之后,队头前会空置出大量的空间,直至最后一个元素,此时队头指针、队尾指针都指向一个位置,这也是判空的条件,队尾指针等于MaxSize,表示队已经满了。但事实上,队列还有一个元素未出队,且线性表中还有大量的空间未曾使用,此种溢出并不是真正的溢出,而是“假溢出”。

对于此种情况,可使用循环队列来解决,当队尾指针指向线性表的最后时,不会令rear==MaxSize,可以通过取模运算,使rear重新指向队头,当然,此前需要进行判定。此种循环队列一般有以下三种思路:

  • 第一种就是将队满条件设为(rear+1)%MaxSize==front,此种方法的优点是rear==front此条件仍然是表示队空的条件,且不需要额外的存储空间;缺点就是会浪费一个单元的存储空间,因为rear是指向下一个应该插入的位置,队满时其实rear指向的存储单元中并没有数据。
  • 第二种方法就是额外增加一个数据成员size,用于记录队中元素个数,这样即使是rear==front也能够轻易判断,但会占据额外的空间。
  • 第三种方法同样是增加一个数据成员tag,类似于一个标签,可以设置其等于0时rear==front表示队空,其等于1时rear==front表示队满。

此处实现的是第一种,默认队头指针front指向第一个元素,队尾指针rear指向下一个应该插入的位置。

2.基本操作

2.1 结构体构造

typedef struct {
    int data[MaxSize];  // 用静态数组存放队列元素
    // 队头指针与队尾指针
    // 队头指针指向队头元素,队尾指针指向队尾元素的后一个位置
    // 即队尾指针应指向下一个应该插入的位置
    int front, rear;
} SeqQueue;

2.2 初始化

// 初始化
void InitQueue(SeqQueue &Q) {
    // 初始时,队头队尾指针指向0
    // 队头指针指向第一个,队尾指针指向下一个应该插入的位置,也即是0
    Q.rear = Q.front = 0;
}

2.3 判空

// 判断队列是否为空
bool QueueEmpty(SeqQueue Q) {
    // 队空条件
    // 即队头指针和队尾指针都指向第一个位置
    if(Q.rear == Q.front) {
        return true;
    } else {
        return false;
    }
}

2.4 入队

// 入队操作
// 此循环队列队列元素计算方法:(rear + MaxSize - front) % MaxSize
bool EnQueue(SeqQueue &Q, int x) {
    // 判断队列是否已经满了
    // 注意:即使rear==MaxSize也不一定能代表队列已经满了
    // 因为出队是从队头出队,可能队头有元素出队,导致线性表前部分空置下来
    // 虽然以下操作可能会导致浪费一个存储单元,但是如果Q.rear==Q.front,就表示队列空置,难以判定
    // 所以不能让Q.rear==Q.front
    if((Q.rear + 1) % MaxSize == Q.front) {
        return false;
    }
    // 由于队尾指针指向下一个要插入的位置
    // 所以直接把要插入的数据插入队尾指针位置即可,即队尾
    Q.data[Q.rear] = x;
    // 队尾指针后移
    // 此处使用了取模运算,将无限的整数域映射到了有限的整数集合上,即{0, 1, 2, ..., MaxSize - 1}
    // 此运算将存储空间从逻辑上变为了环状
    // 即在rear指向队尾后,即rear == MaxSize时,会取模使其再次指向0
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

2.5 出队

// 出队操作
// 由于队列的操作受限性,所以删除只能在队头进行
bool DeQueue(SeqQueue &Q, int &x) {
    // 判断队列是否为空
    if(Q.rear == Q.front) {
        return false;
    }
    // 将队头元素的值赋给x
    x = Q.data[Q.front];
    // 队头元素后移
    // 类似于之前队尾元素后移,形成了环状循环
    Q.front = (Q.front + 1) % MaxSize;
    return true;
}

2.6 获取队头元素的值

// 获取队头元素的值
// 类似于出队操作,不过队头指针位置不变
bool GetHead(SeqQueue Q, int &x) {
    if(Q.rear == Q.front) {
        return false;
    }
    x = Q.data[Q.front];
    return true;
}

2.7 输出队列中所有元素

// 输出队列中所有元素
// 由于队列只能在队头出队,所以只能将所有元素出队输出
void PrintQueue(SeqQueue Q) {
    if(QueueEmpty(Q)) {
        printf("the queue is empty!\n");
    }
    while(!QueueEmpty(Q)) {
        int x;
        DeQueue(Q, x);
        printf("dequeue:%d\n", x);
    }
}

3.完整代码

#include<stdio.h>

#define MaxSize 10  // 定义队列中元素最大个数

typedef struct {
    int data[MaxSize];  // 用静态数组存放队列元素
    // 队头指针与队尾指针
    // 队头指针指向队头元素,队尾指针指向队尾元素的后一个位置
    // 即队尾指针应指向下一个应该插入的位置
    int front, rear;
} SeqQueue;

// 初始化
void InitQueue(SeqQueue &Q) {
    // 初始时,队头队尾指针指向0
    // 队头指针指向第一个,队尾指针指向下一个应该插入的位置,也即是0
    Q.rear = Q.front = 0;
}

// 判断队列是否为空
bool QueueEmpty(SeqQueue Q) {
    // 队空条件
    // 即队头指针和队尾指针都指向第一个位置
    if(Q.rear == Q.front) {
        return true;
    } else {
        return false;
    }
}

// 入队操作
// 此循环队列队列元素计算方法:(rear + MaxSize - front) % MaxSize
bool EnQueue(SeqQueue &Q, int x) {
    // 判断队列是否已经满了
    // 注意:即使rear==MaxSize也不一定能代表队列已经满了
    // 因为出队是从队头出队,可能队头有元素出队,导致线性表前部分空置下来
    // 虽然以下操作可能会导致浪费一个存储单元,但是如果Q.rear==Q.front,就表示队列空置,难以判定
    // 所以不能让Q.rear==Q.front
    if((Q.rear + 1) % MaxSize == Q.front) {
        return false;
    }
    // 由于队尾指针指向下一个要插入的位置
    // 所以直接把要插入的数据插入队尾指针位置即可,即队尾
    Q.data[Q.rear] = x;
    // 队尾指针后移
    // 此处使用了取模运算,将无限的整数域映射到了有限的整数集合上,即{0, 1, 2, ..., MaxSize - 1}
    // 此运算将存储空间从逻辑上变为了环状
    // 即在rear指向队尾后,即rear == MaxSize时,会取模使其再次指向0
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

// 出队操作
// 由于队列的操作受限性,所以删除只能在队头进行
bool DeQueue(SeqQueue &Q, int &x) {
    // 判断队列是否为空
    if(Q.rear == Q.front) {
        return false;
    }
    // 将队头元素的值赋给x
    x = Q.data[Q.front];
    // 队头元素后移
    // 类似于之前队尾元素后移,形成了环状循环
    Q.front = (Q.front + 1) % MaxSize;
    return true;
}

// 获取队头元素的值
// 类似于出队操作,不过队头指针位置不变
bool GetHead(SeqQueue Q, int &x) {
    if(Q.rear == Q.front) {
        return false;
    }
    x = Q.data[Q.front];
    return true;
}

// 输出队列中所有元素
// 由于队列只能在队头出队,所以只能将所有元素出队输出
void PrintQueue(SeqQueue Q) {
    if(QueueEmpty(Q)) {
        printf("the queue is empty!\n");
    }
    while(!QueueEmpty(Q)) {
        int x;
        DeQueue(Q, x);
        printf("dequeue:%d\n", x);
    }
}

int main() {
    SeqQueue Q;
    InitQueue(Q);
    PrintQueue(Q);
    int x;
    EnQueue(Q, 3);
    GetHead(Q, x);
    printf("enqueue:%d\n", x);
    x = 5;
    EnQueue(Q, x);
    printf("enqueue:%d\n", x);
    x = 7;
    EnQueue(Q, x);
    printf("enqueue:%d\n", x);
    PrintQueue(Q);
}

4.运行结果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Silver Star

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值