问题描述:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下1只猴子时,这个猴子就是猴王,编程要求输入n,m后,输出最后猴王的编号。
输入:每行连续的两个整数n,m,用一个空格分开,以0 0结束输入
输出:每行一个整数,代表猴王的编号
一,链表法
方法:1.链表的创建(尾插法)
2.链表的删除
易错点:
1. 因为连续的输入,所以每次开始创建新链表的时候都要定义rear=head;
2. 删除到只剩下两个有效结点的时候,如果删除第一个结点会使链表不完整连贯,所以结尾要加上head->next=p,把链表连起来
3.记得free(p)最后一个猴王的链表结点,还有头指针free(head);
完整代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} Node, *Link;
int main() {
int n, m;
int answer[100];//储存猴王的编号
int count = 0;
Link head, rear; //尾插法做准备
Link p, q;
head = (Link)malloc(sizeof(Node));
head->next = NULL;
rear = head;
while (1) { //保证连续的输入数据
scanf("%d %d", &n, &m);
if (n == 0 || m == 0) {
free(head);
break;//循环退出的条件
} else {
rear = head; //每次都要先让rear先指向head
for (int i = 0; i < n; i++) { //循环链表的创建
Link node;
node = (Link)malloc(sizeof(Node));
node->next = head->next;
node->data = i + 1; //猴子的编号
rear->next = node;
rear = node;
}
int k = 1; //记录猴子的报数
p = rear; //rear->next就是head->next,即q
q = head->next;
while (p != q) { //只剩下一个序号的时候,p,q指针重合
if (k == m) { //删除该猴子
p->next = q->next;
free(q);
k = 1;
q = p->next;
}//移动q到下一个有效结点上
else {
p = q;
q = q->next;
k++;
}
}
}
//删除到只剩下两个有效结点的时候,如果删除第一个结点会使链表不完整连贯
head->next = q;
answer[count] = q->data;
free(q);
count++;
}
for (int i = 0; i < count; i++) {
printf("%d\n", answer[i]);
}
free(head);
return 0;
}
二,用数组模拟链表法
(这里是 懒猫老师《数据结构》课程的该题讲解视频的截图,讲的非常清晰!!)
方法:
1.数组的下标就是猴子的编号-1,数组里对应存的内容就是 连接关系(相当于指针域)
2.关键就是将数组末尾的数据设置为0,相当于建成了一个循环链表
完整代码:
#include <stdio.h>
int main() {
int n, m;
while (1) {
scanf("%d %d", &n, &m);
if (n == 0 || m == 0) {
return 0;
} else {
int pos = 0;
int prior = n - 1;
int number = n;
int monkey[n];
int i = 0;
for (i = 0; i < n - 1; i++) { //相当于创建数组链表
monkey[i] = i + 1;
}
monkey[i] = 0; //循环链表
int k = 1; //记录猴子报数
while (number > 1) { //或者写成pos!=prior
if (k == m) {
monkey[prior] = monkey[pos];
monkey[pos] = -1;
pos = monkey[prior]; //pos移动到下一个位置
number--;
k = 1;
} else {
prior = pos;
pos = monkey[pos];
k++;
}
}
for (int i = 0; i < n; i++) {
if (monkey[i] != -1)
printf("%d\n", i + 1);
}
}
}
return 0;
}
三,普通的数组法
方法:思路与第二个方法不同,在普通的数组里构建出循环的效果,我们可以使用以下式子来代替pos++;
pos=(pos+1)%n (n是总共的数字个数)
易错点:要给monkey数组大小设置成题目n能给到的最大值,因为要输入的数据的组数是未知的,即每组n(猴子的个数)都可能是不同的。
#include <stdio.h>
int main() {
int n, m;
while (1) {
scanf("%d %d", &n, &m);
if (m == 0 || n == 0)
return 0;
int monkey[301] = {0};
for (int i = 0; i < n; i++) {
monkey[i] = i + 1;
}
int count = 1;
int number = n;
int pos = 0;
while (number > 1) {
if (monkey[pos] > 0) {//判断该位置有没有数据
if (count == m) {
monkey[pos] = 0;
count = 1;
number--;
pos = (pos + 1) % n; //i++;
} else {
pos = (pos + 1) % n; //i++;
count++;
}
} else//没有数据就++
pos = (pos + 1) % n;
}
for (int i = 0; i < n; i++) {
if (monkey[i] != 0)
printf("%d\n", monkey[i]);
}
}
return 0;
}