约瑟夫问题
假设有n个人围成一圈,然后对每个人按顺序编号1,2,3,…,n,规定从1号按顺序开始报数,报到k的人出局,之后下一个人再从1开始报数,报到k的人在出局,一直进行下去,问:最后一个出局者为几号?
样例
假设有8个人,每次报数报到4的人出局,则过程如下。
四号出局
八号出局
五号出局
二号出局
一号出局
三号出局
七号出局
所以最后一个出局者为6号。
用c语言实现循环链表解决问题
可以创建一个循环链表来解决像此类的有关循环的问题。
循环链表结构示意图
循环链表的创建
/* 节点 */
typedef struct node {
unsigned int number; // 存放编号
struct node * next;
}* pt,node;
/* 循环链表创建 */
void creat (pt * head, int numb) // head为 node **型,值传递
{ // numb 为数量
pt current, prev;
* head = (pt) malloc (sizeof(node));
prev = * head;
for(int i = 1; numb--; i++) // 创建numb个节点
{
current = (pt) malloc (sizeof(node));
current->number = i; // 按顺序编号
prev->next = current;
prev = current ;
}
current->next = (*head)->next; //尾节点指回头结点
}
具体解决的函数
/* 解决约瑟夫问题 */
int yue(int m,int n) // m为总人数,n为报数的次数
{
pt head, current, prev;
creat(&head,m); // 创建循环链表,m个节点
current = head->next;
for (int i = 1; i < m; i++) // 循环 m - 1次, 剩下最后一个人
{
for (int j = 0; j < n - 1; j++) // current指到报道n的人
{
current = current->next;
}
/* 删除当前编号 */
prev = current->next;
current->number = current->next->number;
current->next = current->next->next;
free(prev);
}
return current->number; // 返回最后一个人的编号
}
这里删除用到了一种特殊的方法。
prev = current->next; //记录下一个节点
/* 将下一个节点的所有数据搬过来 */
current->number = current->next->number;
current->next = current->next->next;
free(prev); // 释放下一个节点
如果要删除一个已知的节点,不知道其前一个节点, 但是不能遍历链表,即要求时间复杂度为O(1),则可以使用这种方法。
这种方法无法删除最后一个节点, 但循环链不存在该问题.
具体测试
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
unsigned int number;
struct node * next;
}* pt,node;
void creat(pt * c, int a);
int yue(int m, int n);
int main ()
{
int m, n;
while(scanf("%d %d", &m, &n) != EOF)
{
printf("最后一个出局的人为%d号\n", yue(m, n%m));
}
}
void creat (pt * c, int a)
{
pt current, prev;
* c = (pt) malloc (sizeof(node));
prev = * c;
for(int i = 1; a--; i++)
{
current = (pt) malloc (sizeof(node));
current->number = i;
prev->next = current;
prev = current ;
}
current->next = (*c)->next;
}
int yue(int m,int n)
{
pt head, current, prev;
creat(&head,m);
current = head->next;
for (int i = 1; i < m; i++)
{
for (int j = 0; j < n - 1; j++)
{
current = current->next;
}
prev = current->next;
current->number = current->next->number;
current->next = current->next->next;
free(prev);
}
return current->number;
}
结果: