• 球钟问题既栈和队列的结合实例


    1 问题描述

    问题.png
    相当于有27个数,然后有三个栈,一个队列,数最开始的时候按顺序存储在队列中;开始时三个栈为空,这三个栈的分别是能容纳11个数的5分钟栈和小时栈,以及一个能容纳4个数的一分钟栈。

    1.开始时27个数都在队列中,依次将出队,入一分钟栈,当一分钟栈满时,然后将一分钟栈中的元素依次出栈入队,然后再出队,入5分钟栈;
    2.当5分钟栈满时,且一分钟栈也满时,先将一分钟栈中的元素出栈入队,再将5分钟栈的元素出栈入队,最后出队入小时栈;
    3.当一分钟栈满,5分钟栈也满,且小时栈也满时,将依次将一分钟栈、5分钟栈和小时栈中的元素出栈入队,再将队头的元素出队入队,再回到 1 。
    问:经过多少次出队,队列中的元素的顺序和最开始的元素顺序相同。

    2 思路

    我们只需要按照其要求进行模拟即可。
    我们可以选择使用循环队列或者链式队列,循环队列比较难一点,使用链式队列的话在最后判断顺序的时候只需要判断对应位置的元素是否和初始元素相同;使用循环队列的话,元素的相对顺序判断比较麻烦,因为最后队头元素的绝对位置可能发生改变。

    3 代码

    3.1 队列

    //定义循环队列的结构
    #define K 27 //能够存放的数据的个数
    typedef struct {
        int data[K + 1]; //存27个数,空一个数用判断
        int front, rear;
    } loopqueue_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.1.1 初始化队列

    loopqueue_t* LoopQueueCreate(void){
        loopqueue_t *h;
        if((h = malloc(sizeof *h))== NULL){
            printf("malloc memory error\n");
            return NULL;
        }
        memset(h, 0, sizeof(h->data));
        //相当于给小球打编号,方便最后判断小球的顺序
        for(int i = 0; i < 27; i++)
            h->data[i] = i + 1;
        h->front = 0;  //队头
        h->rear = 27;  //队尾,其实是27个小球的下一个位置,方便判满、判空
        return h;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.1.2 出队

    int LoopQueueDeQueue(loopqueue_t* h){
        //使用判空,防止操作,也方便调试
       if(LoopQueueIsEmpty(h)){
            printf("队空,删除失败\n");
            return (int)-1;
        }
        int date = h->data[h->front];
        h->data[h->front] = 100000;     //销毁原来队列中存储的数据,为了安全安全,当发现出现了队中不应该出现的值,提示我们出错了
        h->front = (h->front + 1) % (K + 1);    //队头后移
        return date; //返回出队元素的值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.1.3 入队

    int LoopQueueEnQueue(loopqueue_t* h, int data){
        //使用判空,防止操作,也方便调试
        if(LoopQueueIsFull(h)){
            printf("队满,入队失败\n");
            return -1;
        }
        //因为rear是最后一个元素的下一位置,所以一定是先入队再移动
        h->data[h->rear] = data;    //入队
        h->rear = (h->rear + 1) % (K + 1);  //队尾后移
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.1.4 判队满

    循环队列使用数组实现,当rear的下一个位置和front指向是同一个位置时就是数组满了

    int LoopQueueIsFull(loopqueue_t* h){
        return (h->rear + 1) % (K + 1) == h->front ? 1 : 0;
    }
    
    • 1
    • 2
    • 3

    3.1.5 判队空

    当read和front指向的是同一个位置时,队列就空了

    int LoopQueueIsEmpty(loopqueue_t* h){
        return h->rear == h->front ? 1 : 0;
    }
    
    • 1
    • 2
    • 3

    3.2 栈

    栈也使用顺序栈
    因为需要大小不同的栈,所以我直接定义了两种不同的栈,下面的各种函数我也根据栈的大小实现了两次;
    方便的方法是传参,根据参数进行操作

    #define N 4
    #define M 11
    
    typedef struct {
        int data[N];
        int top;
    } seqstack_t;
    
    typedef struct {
        int data[M];
        int top;
    } stack_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.2.1 栈的初始化

    seqstack_t* SeqStackCreate(void)
    {
        seqstack_t* h;
        if ((h = malloc(sizeof *h)) == NULL) {
            printf("malloc memory error\n");
            return NULL;
        }
        h->top = -1;
        return h;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    stack_t* StackCreate(void)
    {
        stack_t* h;
        if ((h = malloc(sizeof *h)) == NULL) {
            printf("malloc memory error\n");
            return NULL;
        }
        h->top = -1;
        return h;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.2.2 入栈

    int SeqStackPush(seqstack_t* h, int data){
        if(SeqStackIsFull(h)){
            return 1;
        }
        h->data[++(h->top)] = data;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    int StackPush(stack_t* h, int data){
        if(StackIsFull(h)){
            return -1;
        }
        h->data[++(h->top)] = data;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.3 出栈

    直接移动栈顶指针进行覆盖

    int SeqStackPop(seqstack_t* h){
        return h->data[h->top--];
    }
    
    • 1
    • 2
    • 3
    int StackPop(stack_t* h){
        return h->data[h->top--];
    }
    
    • 1
    • 2
    • 3

    3.2.4 栈判满

    top指向栈顶元素,所以只需要判断top是指向的栈的最大下标

    int SeqStackIsFull(seqstack_t* h)
    {
        return h->top == N - 1 ? 1 : 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    int StackIsFull(stack_t* h)
    {
        return h->top == M - 1 ? 1 : 0;
    }
    
    • 1
    • 2
    • 3
    • 4

    3.3 main函数

    只有小时指示器,5分钟指示器,一分钟指示器都空的时候小球才全在队列中,此时才有可能出现队列中元素和初始元素相同

    
    int main(int argc, const char* argv[])
    {
        loopqueue_t* ball; //球队列
        stack_t *h, *min_five; //小时和五分钟指示器
        seqstack_t* min; //分钟指示器
        int day = 0;
        int date;
        int n = 50;
    
        //球钟初始化
        if ((ball = LoopQueueCreate()) == NULL) {
            printf("ball error\n");
            return -1;
        }
        if ((h = StackCreate()) == NULL) {
            printf("h error\n");
            return -1;
        }
        if ((min_five = StackCreate()) == NULL) {
            printf("min_five error\n");
            return -1;
        }
        if ((min = SeqStackCreate()) == NULL) {
            printf("min error\n");
            return -1;
        }
    
        while (1) {
            date = LoopQueueDeQueue(ball);
            if (SeqStackPush(min, date)) { //如果分钟指示器满将指示器中的小球加入ball队列中
                for (int i = 0; i < 4; i++) //加入ball队列,4次出队
                    LoopQueueEnQueue(ball, SeqStackPop(min));
                if (StackPush(min_five, date)) { //如果5分钟指示器满则将指示器中的小球加入ball队列中
                    for (int i = 0; i < 11; i++) //加入ball队列,11次出队
                        LoopQueueEnQueue(ball, StackPop(min_five));
                    if (StackPush(h, date)) { //如果小时指示器满则将指示器中的小球加入ball队列中
                        for (int i = 0; i < 11; i++) //加入ball队列,11次出队
                            LoopQueueEnQueue(ball, StackPop(h));
                        LoopQueueEnQueue(ball, date);
                        day++;
    
                        int flag = 0;
                        for (int i = 0; i < 27; i++) {
                                if (ball->data[(i + ball->front) % (K + 1)] != i + 1) {
                                flag = 1;
                                break;
                            }
                        }
                        if (!flag)
                            break;
                    }
                }
            }
        }
        printf("%d\n", day);
        return 0;
    }
    
    
    • 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
  • 相关阅读:
    vue管理系统列表行按钮过多, 封装更多组件
    [SWPU2019]Web1
    扬帆际海:东南亚为何成为跨境消费天堂?
    传统算法与神经网络算法,进化算法优化神经网络
    python正则表达式
    【学习笔记】redux(阮一峰教程)
    map格式和string格式转化为json格式
    JavaScript 进阶03
    koa2+better-sqlite3实现增删改查
    Spark学习(3)-Spark环境搭建-Standalone
  • 原文地址:https://blog.csdn.net/qq_41555003/article/details/126919915