约瑟夫问题:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
通俗的说一下规则:
- 所有的人(包括你自己)围成一个圈并编号
- 从第一个人开始顺时针报数,报到数字k的人被杀掉并移除圈
- 从被杀死的下一个人继续报数,报到数字k的人再被杀掉并移除
- 直到剩下最后一个人
这个问题的解决方法有很多种,可以用递归,用数学推导,用数组完成,下面介绍一种用链表模拟的方法解决这个问题。
提供两段实现的代码供参考:
第一段代码:
///在一个主函数中实现 #include<stdio.h> #include<malloc.h> typedef struct List { int data; struct List *next; }LinkList; int main() { LinkList *L,*r,*s; L = (LinkList *)malloc(sizeof(LinkList)); r =L; int n,i; int k; printf("Please input the number of people:\n"); scanf("%d",&n); printf("The number to count each round:\n"); scanf("%d",&k); for(i = 1;i<=n;i++) ///尾插法建立循环链表 { s = (LinkList *)malloc(sizeof(LinkList)); s->data = i; r->next = s; r= s; } r->next =L->next; //让最后一个链表的下一个节点指向开头 LinkList *p,*q; p = L->next; while(p->next != p) //开始模拟(判断条件要注意:因为最后肯定剩下一个人, 所以最后一个数据的next还是他本身) { for(i = 1;i<k-1;i++) { p = p->next; //从第一个人开始数k-2次 } q = p->next; p->next = p->next->next; //将该节点从链表上删除,第k个人死去 free(q); p = p->next; //p指针指向死去的下一个人 } printf("The last one :%d",p->data); return 0; }
结果展示:
第二段代码:
///用函数实现 #include <stdio.h> #include <malloc.h> #define LEN sizeof (LinkList) //定义struct List这个类型的长度 typedef struct List //定义结构体 { int num; struct List *next; }LinkList; LinkList *create(int m) //创建循环链表 { LinkList *head,*p1,*p2; int i; head=p1=(LinkList*)malloc(LEN); head->num=1; for(i=1,p1->num=1;i<m;i++) { p2=(LinkList*)malloc(LEN); p2->num=i+1; p1->next=p2; p1=p2; } p2->next=head; return head; } LinkList *findout(LinkList *start,int n) //找到需要删除结点的前一个结点 { int i; LinkList *p; p=start; for(i=1;i<n-1;i++) p=p->next; return p; } LinkList *letout(LinkList *last) //删除下一个结点并返回下下个结点 { LinkList *out,*next; out=last->next; last->next=out->next; next=out->next; printf("%d-->",out->num); //打印死亡顺序 free(out); return next; } int main() { int m,n,i,king; LinkList *p; printf("Please input the number of people:\n"); scanf("%d",&m); printf("The number to count each round:\n"); scanf("%d",&n); if(n==1) { king=m; } else { p=create(m); for(i=1;i<m;i++) { p=findout(p,n); p=letout(p); } king=p->num; free(p); } printf("\n"); printf("The last one is:%d\n",king); return 0; }
结果展示: