一群小孩围成一圈,每个小孩都会带有一个随机的密码。然后设定一个数m,从第一个小孩数起,数到第m个的时候,该小孩离开。小孩离开时,其携带的密码将更新这个m值,顺序往下数的第m个小孩会继续出列。依次这样数下去,最后一个小孩是胜利者,问:胜利者是第几个小孩?
这就是大家所熟知的约瑟夫环。
单循环链表天然地很适合解决这个问题,下面用C语言实现了一下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #define OK 1; 5 #define ERROR 0; 6 #define OVERFLOW -2; 7 8 typedef int ElemType; 9 typedef struct Node 10 { 11 ElemType num; 12 ElemType password; 13 struct Node *next; 14 }Node; 15 typedef struct Node CLinkList; /* 定义LinkList */ 16 17 void main(){ 18 int num=10; 19 int m=10; 20 CLinkList *L; 21 /* 输入约瑟夫环的总结点数*/ 22 printf("有多少个小孩参与游戏(设定小于100):"); 23 scanf("%d",&num); 24 if(num<=0||num>=100){ 25 /* 如果输入的数字超出范围,就默认总结点数为10 */ 26 printf("你的输入不符合,下面采用默认数字10\n"); 27 num=10; 28 } 29 /* 输入第一个需要出列的数字 */ 30 printf("第一个报到哪个数必须出列 (设定小于100):"); 31 scanf("%d",&m); 32 if(m<=0||m>=100){ 33 printf("你的输入不符合,下面采用默认数字10\n"); 34 m=10; 35 } 36 InitList(L); 37 Creat_Joseph(L,num); 38 ListTraverse(L,num); 39 Joseph_Run(L,m); 40 } 41 42 /* 释放删除的结点 */ 43 void FreeNode(CLinkList *p){ 44 CLinkList *temp=p; 45 while(temp->next!=p) 46 temp=temp->next; 47 temp->next=p->next; 48 printf("编号%3d的小孩离队了,他携带的密码是%3d\n",p->num, p->password); 49 free(p); 50 } 51 52 /* 初始化结点 */ 53 int InitList(CLinkList *l){ 54 l=(CLinkList *) malloc (sizeof(CLinkList)); 55 if(!l) 56 return OVERFLOW; 57 return OK; 58 } 59 /* 创建每个结点携带的密码 */ 60 void Creat_Joseph(CLinkList *l,int length){ 61 int i; 62 CLinkList *p=l; 63 l->next=l; 64 l->num=1; 65 srand( (unsigned)time(NULL)); 66 l->password=rand()%(length+3)+1; 67 printf("以下小孩(约瑟夫环结点)携带的密码随机生成的:\n"); 68 /* 打印约瑟夫环的第一个结点 */ 69 printf("小孩编号: %3d 带的密码: %3d\n",l->num,l->password); 70 for(i=1;i<length;i++){ 71 CLinkList* temp=(CLinkList*)malloc (sizeof(CLinkList)); 72 p->next=temp; 73 temp->next=l; 74 temp->num=i+1; 75 temp->password=rand()%(2*length)+1; 76 /* 打印除第一个结点外的其它结点 */ 77 printf("小孩编号: %3d 带的密码: %3d\n",temp->num,temp->password); 78 p=p->next; 79 } 80 } 81 82 void ListTraverse(CLinkList *L,int length) 83 { 84 int i; 85 CLinkList *p=L->next; 86 for(i = 0; i < length; i++) 87 { 88 printf("%d:%d ",p->num, p->password); 89 p=p->next; 90 } 91 printf("\n"); 92 return OK; 93 } 94 95 /* 根据结点携带的密码释放下一个结点 */ 96 void Joseph_Run(CLinkList *l, int m){ 97 int i,k; 98 CLinkList *temp,*p; 99 if(l->next==l) 100 /* 打印最后一个结点 */ 101 printf("最后编号 %d 的小孩获胜!",l->num); 102 else{ 103 temp = l; 104 for(i=1; i<m; i++) 105 { 106 printf("编号%3d的小孩安全\n",temp->num); 107 temp=temp->next; 108 } 109 p=temp->next; 110 k=temp->password; 111 /* 释放中招的结点 */ 112 FreeNode(temp); 113 /* 递归 */ 114 Joseph_Run(p,k); 115 } 116 }
程序运行结果:
有多少个小孩参与游戏(设定小于100):6 第一个报到哪个数必须出列 (设定小于100):8 以下小孩(约瑟夫环结点)携带的密码随机生成的: 小孩编号: 1 带的密码: 5 小孩编号: 2 带的密码: 9 小孩编号: 3 带的密码: 12 小孩编号: 4 带的密码: 2 小孩编号: 5 带的密码: 5 小孩编号: 6 带的密码: 2 2:9 3:12 4:2 5:5 6:2 1:5 编号 1的小孩安全 编号 2的小孩安全 编号 3的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 6的小孩安全 编号 1的小孩安全 编号 2的小孩离队了,他携带的密码是 9 编号 3的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 6的小孩安全 编号 1的小孩安全 编号 3的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 6的小孩离队了,他携带的密码是 2 编号 1的小孩安全 编号 3的小孩离队了,他携带的密码是 12 编号 4的小孩安全 编号 5的小孩安全 编号 1的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 1的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 1的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 1的小孩离队了,他携带的密码是 5 编号 4的小孩安全 编号 5的小孩安全 编号 4的小孩安全 编号 5的小孩安全 编号 4的小孩离队了,他携带的密码是 2 最后编号 5 的小孩获胜! Process returned 23 (0x17) execution time : 8.506 s Press any key to continue.