循环链表解决约瑟夫问题

约瑟夫问题:

据说著名犹太历史学家 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;
}

结果展示:


猜你喜欢

转载自blog.csdn.net/sinat_38486449/article/details/80233351