一群小孩围成一圈,每个小孩都会带有一个随机的密码。然后设定一个数m,从第一个小孩数起,数到第m个的时候,该小孩离开。小孩离开时,其携带的密码将更新这个m值,顺序往下数的第m个小孩会继续出列。依次这样数下去,最后一个小孩是胜利者,问:胜利者是第几个小孩?
这就是大家所熟知的约瑟夫环。
单循环链表天然地很适合解决这个问题,下面用C语言实现了一下:
001 | #include <stdio.h> |
002 | #include <stdlib.h> |
003 | #include <time.h> |
004 | #define OK 1; |
005 | #define ERROR 0; |
006 | #define OVERFLOW -2; |
007 |
008 | typedef int ElemType; |
009 | typedef struct Node |
010 | { |
011 | ElemType num; |
012 | ElemType password; |
013 | struct Node *next; |
014 | }Node; |
015 | typedef struct Node CLinkList; /* 定义LinkList */ |
016 |
017 | void main(){ |
018 | int num=10; |
019 | int m=10; |
020 | CLinkList *L; |
021 | /* 输入约瑟夫环的总结点数*/ |
022 | printf ( "有多少个小孩参与游戏(设定小于100):" ); |
023 | scanf ( "%d" ,&num); |
024 | if (num<=0||num>=100){ |
025 | /* 如果输入的数字超出范围,就默认总结点数为10 */ |
026 | printf ( "你的输入不符合,下面采用默认数字10\n" ); |
027 | num=10; |
028 | } |
029 | /* 输入第一个需要出列的数字 */ |
030 | printf ( "第一个报到哪个数必须出列 (设定小于100):" ); |
031 | scanf ( "%d" ,&m); |
032 | if (m<=0||m>=100){ |
033 | printf ( "你的输入不符合,下面采用默认数字10\n" ); |
034 | m=10; |
035 | } |
036 | InitList(L); |
037 | Creat_Joseph(L,num); |
038 | ListTraverse(L,num); |
039 | Joseph_Run(L,m); |
040 | } |
041 |
042 | /* 释放删除的结点 */ |
043 | void FreeNode(CLinkList *p){ |
044 | CLinkList *temp=p; |
045 | while (temp->next!=p) |
046 | temp=temp->next; |
047 | temp->next=p->next; |
048 | printf ( "编号%3d的小孩离队了,他携带的密码是%3d\n" ,p->num, p->password); |
049 | free (p); |
050 | } |
051 |
052 | /* 初始化结点 */ |
053 | int InitList(CLinkList *l){ |
054 | l=(CLinkList *) malloc ( sizeof (CLinkList)); |
055 | if (!l) |
056 | return OVERFLOW; |
057 | return OK; |
058 | } |
059 | /* 创建每个结点携带的密码 */ |
060 | void Creat_Joseph(CLinkList *l, int length){ |
061 | int i; |
062 | CLinkList *p=l; |
063 | l->next=l; |
064 | l->num=1; |
065 | srand ( (unsigned) time (NULL)); |
066 | l->password= rand ()%(length+3)+1; |
067 | printf ( "以下小孩(约瑟夫环结点)携带的密码随机生成的:\n" ); |
068 | /* 打印约瑟夫环的第一个结点 */ |
069 | printf ( "小孩编号: %3d 带的密码: %3d\n" ,l->num,l->password); |
070 | for (i=1;i<length;i++){ |
071 | CLinkList* temp=(CLinkList*) malloc ( sizeof (CLinkList)); |
072 | p->next=temp; |
073 | temp->next=l; |
074 | temp->num=i+1; |
075 | temp->password= rand ()%(2*length)+1; |
076 | /* 打印除第一个结点外的其它结点 */ |
077 | printf ( "小孩编号: %3d 带的密码: %3d\n" ,temp->num,temp->password); |
078 | p=p->next; |
079 | } |
080 | } |
081 |
082 | void ListTraverse(CLinkList *L, int length) |
083 | { |
084 | int i; |
085 | CLinkList *p=L->next; |
086 | for (i = 0; i < length; i++) |
087 | { |
088 | printf ( "%d:%d " ,p->num, p->password); |
089 | p=p->next; |
090 | } |
091 | printf ( "\n" ); |
092 | return OK; |
093 | } |
094 |
095 | /* 根据结点携带的密码释放下一个结点 */ |
096 | void Joseph_Run(CLinkList *l, int m){ |
097 | int i,k; |
098 | CLinkList *temp,*p; |
099 | 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 | } |
程序运行结果:
01 | 有多少个小孩参与游戏(设定小于100):6 |
02 | 第一个报到哪个数必须出列 (设定小于100):8 |
03 | 以下小孩(约瑟夫环结点)携带的密码随机生成的: |
04 | 小孩编号: 1 带的密码: 5 |
05 | 小孩编号: 2 带的密码: 9 |
06 | 小孩编号: 3 带的密码: 12 |
07 | 小孩编号: 4 带的密码: 2 |
08 | 小孩编号: 5 带的密码: 5 |
09 | 小孩编号: 6 带的密码: 2 |
10 | 2:9 3:12 4:2 5:5 6:2 1:5 |
11 | 编号 1的小孩安全 |
12 | 编号 2的小孩安全 |
13 | 编号 3的小孩安全 |
14 | 编号 4的小孩安全 |
15 | 编号 5的小孩安全 |
16 | 编号 6的小孩安全 |
17 | 编号 1的小孩安全 |
18 | 编号 2的小孩离队了,他携带的密码是 9 |
19 | 编号 3的小孩安全 |
20 | 编号 4的小孩安全 |
21 | 编号 5的小孩安全 |
22 | 编号 6的小孩安全 |
23 | 编号 1的小孩安全 |
24 | 编号 3的小孩安全 |
25 | 编号 4的小孩安全 |
26 | 编号 5的小孩安全 |
27 | 编号 6的小孩离队了,他携带的密码是 2 |
28 | 编号 1的小孩安全 |
29 | 编号 3的小孩离队了,他携带的密码是 12 |
30 | 编号 4的小孩安全 |
31 | 编号 5的小孩安全 |
32 | 编号 1的小孩安全 |
33 | 编号 4的小孩安全 |
34 | 编号 5的小孩安全 |
35 | 编号 1的小孩安全 |
36 | 编号 4的小孩安全 |
37 | 编号 5的小孩安全 |
38 | 编号 1的小孩安全 |
39 | 编号 4的小孩安全 |
40 | 编号 5的小孩安全 |
41 | 编号 1的小孩离队了,他携带的密码是 5 |
42 | 编号 4的小孩安全 |
43 | 编号 5的小孩安全 |
44 | 编号 4的小孩安全 |
45 | 编号 5的小孩安全 |
46 | 编号 4的小孩离队了,他携带的密码是 2 |
47 | 最后编号 5 的小孩获胜! |
48 | Process returned 23 (0x17) execution time : 8.506 s |
49 | Press any key to continue . |