题目要求:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。
从编号为k的人开始报数,数到m的那个人自杀;他的下一个人又从1开始报数,
数到m的那个人又出列;依此规律重复下去,请问:最后一个存活的人编号是多少。
输入格式: 3个数n k m(含义如上题)
输出格式: 最后一个出列的人编号。
样例输入:
6 1 2
样例输出:
5
算法思路:
约瑟夫环是一道经典的循环链表问题,每当报数到指定数字,那个人就出局,接下来的一个人重新开始报数,相当于链表回到了开头,循环反复,直到只剩下一个人。
- 我们需要先创建一个带有头节点的循环链表,然后用把所有人一次插入到循环链表当中,并赋值他们相对应的报数;
- 接下来开始处理从第几个人开始报数的问题:循环遍历链表,直到到指定的那个人,退出循环,返回节点,则为开始报数的人;
- 最后解决循环链表删除问题,向函数中传入指定删除的报数,循环遍历链表,每到之指定报数,退出循环进行删除操作,外面大循环继续往下走,直到结束。
具体实现如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct node{
int data;
struct node *next;
}Node,*Link;
Link create(int n,int k); //创建一个带有头节点的循环链表,并控制从k开始报数
void Josephus(Link p,int m); //传入链表和报数
int main()
{
Link p = NULL;
int n,k,m;
scanf("%d %d %d",&n,&k,&m); //输入总人数,开始的报数,自杀报数
p = create(n,k);
Josephus(p,m);
return 0;
}
Link create(int n,int k)
{
Link head = NULL,p = NULL, pr = NULL; //pr是新节点p的前一个结点
int i;
for(i = 1;i <= n;i++)
{
p = (Link)malloc(sizeof(Node));
if(p == NULL)
{
printf("动态内存分配失败");
return NULL;
}
p->data = i; //给每个人赋上报数的值
if(head == NULL) //如果头节点指向空,直接放
{
head = p;
}
else
{
pr->next = p;
}
pr = p;
}
p->next = head; //循环结束就回到头节点,形成完整的循环链表
p = head;
//开始处理从第几个人开始报数
for(i = 1;i < k;i++)
{
pr = p;
p = p->next; //一直遍历,到了k就出循环
}
return p; //返回指定的第一人
}
void Josephus(Link p,int m)
{
Link pr = NULL;
int i;
while(p->next != p) //循环链表首尾未连接
{
for(i = 1;i < m;i++)
{
pr = p;
p = p->next;
} //循环结束,就是要自杀的人
pr->next = p->next; //删除操作
free(p);
p = pr->next; //重新开始,p指向新的出发点
}
printf("%d",p->data); //输出最后存活的人
return;
}