球钟问题
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. 创建球队列和三个指示器栈和计时变量timeint 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表示失败