链表实现Josephus约瑟夫环问题如下,输出每轮杀掉的人的编号,并且输出最后剩下的一名幸运者。
头文件如下
#ifndef JOSEPHLIST_H
#define JOSEPHLIST_H
#include <stdio.h>
typedef struct node *link;
struct node{
int item;
link next;
};
link make_node(int item);
void free_node(link p);
void make_joseph_circle(int m);
int joseph_number(int m, int n);
void insert(link p);
void delete_node(link p);
void traverse(void (*visit)(link));
void destroy(void);
#endif
实现如下
#include <stdlib.h>
#include "josephlist.h"
static link head = NULL;
static link tail = NULL;
link make_node(int item)
{
link p = new node;
p->item=item;
p->next=NULL;
return p;
}
void free_node(link p)
{
if(p!=NULL)
free(p);
}
void insert(link p)
{
if(head == NULL && tail ==NULL){
head = p;
tail = p;
p->next=head;
}else{
tail->next = p;
tail = p;
tail->next = head;
}
}
void delete_node(link p)
{
link pre;
if(head==tail&&head==p) {
head = NULL;
tail = NULL;
return;
}else if(head==tail&&head!=p) {
printf("impossible\n");
return;
}else if(p==head){
head = head->next;
tail ->next = head; // IMPORTANT O(∩_∩)O
return;
}
for(pre=head;pre;pre=pre->next) {
if(pre->next == p) {
pre->next = p->next;
if(p==tail)
tail=pre;
return;
}
}
}
void traverse(void (*visit)(link))
{
link p;
for(p=head;p!=tail;p=p->next)
visit(p);
visit(tail);
}
void destroy(void)
{
link p= head;
link q= head;
while(p!=tail) {
q = p;
p = p->next;
free_node(q);
}
if(tail!=NULL)
free_node(tail);
head = NULL;
tail = NULL;
return;
}
void make_joseph_circle(int m)
{
if(head!=NULL)
destroy();
for(int i=1;i<=m;i++){
link p = make_node(i);
insert(p);
}
return;
}
int joseph_number(int m, int n)
{
make_joseph_circle(m);
link p=head;
link temp_next=p;
int count=1;
while(count<m) {
for(int i=0;i<n-1;i++) {
p = p->next;
}
printf("%d\t%d\n",count,p->item);
temp_next = p->next;
delete_node(p);
free_node(p);
p = temp_next;
count++;
}
printf("LUCKY Joseph:%d\n\n",p->item);
int result = p->item;
if(p!=NULL)
free_node(p);
head=NULL;
tail=NULL;
return result;
}
main函数如下
#include <stdio.h>
#include "josephlist.h"
void print_item(link p)
{
printf("%d\n",p->item);
}
int main()
{
printf("joseph(6,5)\n");
joseph_number(6,5);
printf("joseph(6,3)\n");
joseph_number(6,3);
printf("joseph(6,4)\n");
joseph_number(6,4);
return 0;
}
运行结果如下:
现在来探索一下每次得到的约瑟夫数的规律,从最简单的开始。假设每次报数的时候,都1,2,1,2这样的报数,报数为2的在此轮出列。那么,总人数变化时,约瑟夫数如何变化呢?为了简单,现在假设人数从1增长到25,每次报数为2时的变化情况:
把main函数改为如下:
#include <stdio.h>
#include "josephlist.h"
void print_item(link p)
{
printf("%d\n",p->item);
}
int main()
{
int total=25;
int result_people[26];
for (int i=1;i<=total;i++) {
result_people[i]=joseph_number(i,2);
}
for(int i=1;i<=total;i++)
{
printf("%d\t",i);
}
printf("\n");
for(int i=1;i<=total;i++)
{
printf("%d\t",result_people[i]);
}
printf("\n");
return 0;
}
最终结果如下,可以看到,结果是有规律的:
约瑟夫数的结果构成了一个这样的数列
1,
1,3
1,3,5,7
1,3,5,7,9,11,13,15,
...
那么,当每次报数为2时,如何从数学上推导约瑟夫数随着总人数变化的递推公式呢?又如何把报数从2推广到N呢?还没想出来。。。