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


    总目录

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2 初始化

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

    2.3 判空

    // 判断队列是否为空
    bool QueueEmpty(SeqQueue Q) {
        // 队空条件
        // 即队头指针和队尾指针都指向第一个位置
        if(Q.rear == Q.front) {
            return true;
        } else {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.6 获取队头元素的值

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

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.完整代码

    #include
    
    #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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    4.运行结果

    在这里插入图片描述

  • 相关阅读:
    LeetCode 1624. 两个相同字符之间的最长子字符串
    这MATLAB代码哪里有错啊
    大数据挖掘决策树计算过程
    腾讯云轻量服务器地域选择教程,2024最新地域选择攻略
    数据结构(超详细讲解!!)第二十三节 树型结构
    [Android][DevTips]DHCP流程与ARP相关简介
    vulnhub靶机ha:wordy
    【论文解读】Co-attention network with label embedding for text classification
    COM组件IDispatch操作
    1.9 面试经典150题 除自身以外数组的乘积
  • 原文地址:https://blog.csdn.net/treesorshining/article/details/125798537