球钟问题

球钟问题

1. 问题背景

​ 球钟是一个利用球的移动来记录时间的简单装置。它有三个可以容纳若干个球的指示器:分钟指示器,五分钟指示器和小时指示器。

举例:若分钟指示器中有2个球,五分钟指示器中有6个球,小时指示器中有5个球,则时间为5:32

2. 工作原理

​ 每过一分钟,球钟就会从球队列的队首取出一个球放入分钟指示器,分钟指示器最多可容纳4个球。当放入第五个球时,在分钟指示器的4个球就会按照他们被放入时的相反顺序加入球队列的队尾。而第五个球就会进入五分钟指示器。

​ 按此类推,五分钟指示器最多可放11个球,小时指示器最多可放11个球。

​ 当小时指示器放入第12个球时,原来的11个球按照他们被放入时的相反顺序加入球队列的队尾,然后12个球也回到队尾。这时,三个指示器均为空,回到初始状态,从而形成一个循环。因此,该球钟表示时间的范围为0:00到11:59.

3. 问题

现设初始时球队列的球数为27,球钟的三个指示器初态均为空,问,经过多久,球队列才能恢复到原来的顺序?

4. 求解和实现

4.1 求解思路:

1. 球队列的功能是实现 <= 27 个球的头出,尾部插入: 用链式队列实现。(顺序队列要跑循环,相对没那么方便)
2. 三个指示器的功能是存球,然后反顺序输出,固定存储大小:用顺序栈实现。
3. 计时逻辑是指示器栈满向前+1,然后清空当前栈。
4. 问题判断是否恢复原来顺序。初始化队列为有序队列1到27,后续判断队列是否满足从小到大排序即可。
5. 可复用代码:linkqueue.c/h 链队列, sqstack.c/h 顺序栈。

4.2 实现代码:

main.c

#include <stdio.h>
#include "sqstack.h"
#include "linkqueue.h"

int check(linkqueue q);

int main()
{
    // 1. 创建球队列和三个指示器栈和计时变量time
    int time = 0;
    linkqueue ballqueue = linkqueue_create();
    if (ballqueue == NULL)
    {
        printf("创建球队列失败!\n");
        return	-1;
    }
    sqstack minstack = sqstack_create(4);
    sqstack fivestack = sqstack_create(11);
    sqstack hourstack = sqstack_create(11);
    if (minstack == NULL || fivestack == NULL || hourstack == NULL)
    {
        printf("创建指示器栈失败!\n");
        return	-1;
    }

    // 2. 初始球队列
    for (size_t i = 1; i <= 27; i++)
    {
        linkqueue_enqueue(ballqueue, i);
    }
    linkqueue_show(ballqueue);
    printf("球队列初始化完成!\n");
    // 3. 开始计时,并调用检查函数判断是否恢复原始球队列
    while (1)
    {
        time++;
        if (!sqstack_full(minstack))
        {
            sqstack_push(minstack, linkqueue_dequeue(ballqueue));
        }
        else
        {
            while (!sqstack_empty(minstack))
            {
                linkqueue_enqueue(ballqueue, sqstack_pop(minstack));
            }
            if	(!sqstack_full(fivestack))
            {
                sqstack_push(fivestack, linkqueue_dequeue(ballqueue));
            }
            else
            {
                while (!sqstack_empty(fivestack))
                {
                    linkqueue_enqueue(ballqueue, sqstack_pop(fivestack));
                }
                if (!sqstack_full(hourstack))
                {
                    sqstack_push(hourstack, linkqueue_dequeue(ballqueue));
                }
                else
                {
                    while (!sqstack_empty(hourstack))
                    {
                        linkqueue_enqueue(ballqueue, sqstack_pop(hourstack));
                    }
                    linkqueue_enqueue(ballqueue, linkqueue_dequeue(ballqueue));
                    if (check(ballqueue) == 1)
                    {
                        break;
                    }
                }
                
            }
        }
        
    }
    printf("***********\n");
    printf("检查球队列恢复是否原始状态!\n");
    linkqueue_show(ballqueue);
    printf("用时:%d\n",time);
    linkqueue_free(&ballqueue);
    sqstack_free(&minstack);
    sqstack_free(&fivestack);
    return 0;
}

int	check(linkqueue q)
{
    queuenode tem = q->front->next;
    int i = 0;
    while (tem->next != NULL)
    {
        if (tem->data < tem->next->data)
        {
            tem = tem->next;
            i++;
        }
        else
        {
            return 0;
        }
    }
    return 1;
}

linkqueue.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linkqueue.h"

/**
  * @name 	linkqueue_create()
  * @brief	创建链式队列
  * @param  NULL
  * @retval q 队列地址
  */
linkqueue linkqueue_create()
{
    // 1. 申请队列头尾指针内存和队列节点内存
    linkqueue p = (linkqueue)malloc(sizeof(linkqueue_struct));
    if (p ==  NULL)
    {
        printf("linkqueue malloc error\n");
        return NULL;
    }
    queuenode newnode = (queuenode)malloc(sizeof(queuenode_struct));
    if (newnode == NULL)
    {
        printf("linknode malloc error\n");
        free(p);
        return NULL;
    }
    // 2. 初始化队列头尾指针和队列节点
    p->front = p->rear = newnode;
    newnode->next = NULL;
    newnode->data = -1;

    return p;
}

/**
  * @name 	linkqueue_create()
  * @brief	p判断链式队列是否为空
  * @param  q 队列地址
  * @retval 0 队列不为空,1 队列为空,-1 队列不存在
  */
int linkqueue_empty(linkqueue q)
{
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 判断队列是否为空
    return q->front == q->rear ? 1 : 0;
}

/**
  * @name 	linkqueue_enqueue()
  * @brief	入队
  * @param  q 队列地址
  * @retval 0 入队成功,-1 入队失败
  */
int linkqueue_enqueue(linkqueue q, data_t x)
{
    // queuenode tem;
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 申请队列节点内存
    queuenode newnode = (queuenode)malloc(sizeof(queuenode_struct));
    if (newnode == NULL)
    {
        printf("queuenode malloc error\n");
        return -1;
    }
    newnode->data = x;
    newnode->next = NULL;
    // 3. 将新节点插入到队列尾结点之后,并尾指针指向新节点
    // tem = q->front;
    // while (tem->next != NULL)
    // {
    //     tem = tem->next;
    // }
    // tem->next = newnode;
    q->rear->next = newnode;
    q->rear = newnode;
    return 0;
}
/**
  * @name 	linkqueue_dequeue()
  * @brief	出队
  * @param  q 队列地址
  * @retval 出队元素
  */
data_t linkqueue_dequeue(linkqueue q)
{
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }

    // 2. 判断队列是否为空
    if (q->front == q->rear)
    {
        printf("linkqueue is empty\n");
        return -1; 
    }
    // 3. 出队操作,释放队头结点,并头指针指向头结点下一个结点
    queuenode tem = q->front->next;
    data_t x = tem->data;

    q->front->next = tem->next;
    if (q->rear == tem)
    {
        q->rear = q->front;
    }
    free(tem);
    return x;
}
/**
  * @name 	linkqueue_show()
  * @brief	遍历队列,输出队列元素
  * @param  q 队列地址
  * @retval -1 遍历失败,0 遍历成功
  */
int linkqueue_show(linkqueue q)
{
    // 1. 判断队列是否存在以及队列是否为空
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    if (q->front == q->rear)
    {
        printf("linkqueue is empty\n");
        return -1; 
    }
    // 2. 遍历队列,输出队列元素
    queuenode tem = q->front->next;
    while (tem != NULL)
    {
        printf("%d \n", tem->data);
        tem = tem->next;
    }
    return 0;
}

/**
  * @name 	linkqueue_free()
  * @brief	遍历队列,输出队列元素
  * @param  q 队列地址
  * @retval -1 释放失败,0 释放成功
  */
int linkqueue_free(linkqueue *q)
{
    // 1. 判断队列是否存在
    if (*q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 释放队列
    queuenode tem = (*q)->front;
    while (tem != NULL)
    {
        queuenode next = tem->next;
        free(tem);
        tem = next;
    }
    free(*q);
    *q = NULL;
    return 0;
}

linkqueue.h

typedef int data_t;

typedef struct node_t
{
    data_t data;                 // 数据域
    struct node_t *next;        // 指针域
} queuenode_struct, *queuenode; // 队列结点结构体类型定义

typedef struct
{
    queuenode front, rear;
} linkqueue_struct,*linkqueue;  // 队列结构体类型定义

linkqueue linkqueue_create();                     // 创建队列
int linkqueue_empty(linkqueue q);                 // 判断队列是否为空
int linkqueue_enqueue(linkqueue q, data_t x);     // 入队
data_t linkqueue_dequeue(linkqueue q);            // 出队
int linkqueue_show(linkqueue q);                  // 遍历队列
int linkqueue_free(linkqueue *q);                 // 释放队列

sqstack.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for memset
#include "sqstack.h"

/**
  * @name 	sqstack_create()
  * @brief	创建顺序栈
  * @param  length	栈长
  * @retval 栈地址
  */
sqstack sqstack_create(int length)
{
    sqstack s;
    // 1. 检查栈长参数是否合理
    if (length < 0)
    {
        printf("length is invalid\n");
        return NULL;
    }
    // 2. 申请栈空间,先申请栈结构体,然后申请data空间
    if ((s = (sqstack)malloc(sizeof(sqstack_struct))) == NULL)
    {
        printf("malloc sqstack failed\n");
        return NULL;
    }
    if ((s->data = (data_t *)malloc(length*sizeof(data_t))) == NULL)
    {
        printf("malloc sqstack->data failed\n");
        free(s);
        return NULL;
    }

    // 3. 初始化栈
    memset(s->data, 0, length*sizeof(data_t));
    s->maxlen = length;
    s->top = -1;

    return s; 
}

/**
  * @name 	sqstack_push()
  * @brief	顺序栈入栈
  * @param  s 栈地址,x 入栈元素
  * @retval 0 入栈成功,-1 入栈失败
  */
int sqstack_push(sqstack s, data_t x)
{
    // 1. 检查栈是否存在以及栈是否满
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
        return -1;
    }
    if (s->top == s->maxlen - 1)
    {
        printf("sqstack is full\n");
        return -1;
    }
    
    // 2. 入栈
    s->top++;
    s->data[s->top] = x;
    return 0;
}

/**
  * @name 	sqstack_empty()
  * @brief	顺序栈判断是否为空
  * @param  s 栈地址
  * @retval 1 栈为空,0 栈不为空
  */
int sqstack_empty(sqstack s)
{
    // 1. 检查栈是否存在
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
        return -1;
    }
    // 2. 通过栈顶判断栈是否为空
    return(s->top == -1 ? 1 : 0);    
}

/**
  * @name 	sqstack_full()
  * @brief	顺序栈判断是否满
  * @param  s 栈地址
  * @retval 1 栈为满,0 栈不满
  */
int sqstack_full(sqstack s)
{
    // 1. 检查栈是否存在
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
        return -1;
    }
    // 2. 通过栈顶判断栈是否为满
    return(s->top == s->maxlen - 1 ? 1 : 0);
}

/**
  * @name 	sqstack_pop()
  * @brief	顺序栈出栈
  * @param  s 栈地址
  * @retval 出栈元素
  */
data_t sqstack_pop(sqstack s)
{
    // 1. 检查栈是否存在
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
    }
    // 2. 出栈
    s->top--;
    return s->data[s->top+1];
}

/**
  * @name 	sqstack_top()
  * @brief	顺序栈获取栈顶元素
  * @param  s 栈地址
  * @retval 出栈元素
  */
data_t sqstack_top(sqstack s)
{
    // 1. 检查栈是否存在
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
    }
    // 2. 返回栈顶元素
    return s->data[s->top];
}

/**
  * @name 	sqstack_clear()
  * @brief	顺序栈清空
  * @param  s 栈地址
  * @retval 0 清空成功,-1 清空失败
  */
int sqstack_clear(sqstack s)  
{
    // 1. 检查栈是否存在
    if (s == NULL)
    {
        printf("sqstack is invalid\n");
        return -1;
    }
    // 2. 清空栈。直接将栈顶置为-1,后续的入栈操作会覆盖原来的值。
    s->top = -1;
    return 0;
}

/**
  * @name 	sqstack_free()
  * @brief	顺序栈出栈
  * @param  s 栈地址
  * @retval 0 释放成功,-1 释放失败
  */
int sqstack_free(sqstack *s)
{
    if (*s == NULL)
    {
        printf("sqstack is invalid\n");
        return -1;
    }
    if ((*s)->data != NULL)
    {
        free((*s)->data);
    }
    free(*s);
    *s = NULL;
    return 0;
}

sqstack.h

typedef int data_t;     // 定义数据类型  

typedef struct 
{
    data_t *data;
    int maxlen;           // 栈最大长度
    int top;             // 栈顶-1标准栈为空
} sqstack_struct,*sqstack;

sqstack sqstack_create(int length);       // 创建一个长度为length的栈,返回值为栈的指针
int sqstack_push(sqstack s, data_t x);     // 元素x入栈,返回值为 0表示成功,-1表示失败
int sqstack_empty(sqstack s);              // 判断栈是否为空,返回值为 1表示空,0表示非空
int sqstack_full(sqstack s);               // 判断栈是否为满,返回值为 1表示满,0表示非满
data_t sqstack_pop(sqstack s);             // 栈顶元素出栈,返回值为栈顶元素
data_t sqstack_top(sqstack s);             // 返回栈顶元素,栈顶元素不变
int sqstack_clear(sqstack s);              // 清空栈,返回值为 0表示成功,-1表示失败
int sqstack_free(sqstack *s);               // 释放栈,返回值为 0表示成功,-1表示失败

5 结果:

image-20250202233400654

posted @ 2025-02-02 23:37  Arsun  阅读(49)  评论(0)    收藏  举报