用单循环链表来玩一下约瑟夫环游戏

一群小孩围成一圈,每个小孩都会带有一个随机的密码。然后设定一个数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 
008typedef int ElemType;
009typedef struct Node
010{
011    ElemType num;
012    ElemType password;
013    struct Node *next;
014}Node;
015typedef struct Node CLinkList; /* 定义LinkList */
016 
017void 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/* 释放删除的结点 */
043void 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/* 初始化结点 */
053int InitList(CLinkList *l){
054    l=(CLinkList *) malloc (sizeof(CLinkList));
055    if(!l)
056        return OVERFLOW;
057    return OK;
058}
059/* 创建每个结点携带的密码 */
060void 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 
082void 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/* 根据结点携带的密码释放下一个结点 */
096void 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
102: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 的小孩获胜!
48Process returned 23 (0x17)   execution time : 8.506 s
49Press any key to continue.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值