Josephus环问题

问题描述:

设编号为1,2,3,4.......,n个人按顺时针方向围坐一圈,约定编号为k(1<<k<<n)的人按顺时针方向从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推直到所有人出列为止,由此产生一个出对编号的序列,试设计算法求出n个人的出列顺序。

这里删除操作频繁,我们用单循环链表解决这个问题比较方便,注意当只有一个结点时的处理问题。

下面的代码是自行输入数据(不一定是自然数每次递增1的数列),每一个数据代表一个人,输出出圈的序列。

#include <stdio.h>
#include <stdlib.h>
#define maxsize 100
typedef struct list
{
    int data;
    struct list*next;
} Lnode,*Linklist;

Linklist creat(int *arr,int len)//创建单循环链表
{
    int i;
    Linklist head,tail,ptr;
    head=(Linklist)malloc(sizeof(Lnode));
    if(!head)
    {
        printf("内存分配失败!");
        return NULL;
    }
    head->data=arr[0];
    head->next=NULL;
    tail=head;

    for(i=1; i<len; i++)
    {
        ptr=(Linklist)malloc(sizeof(Lnode));
        if(!ptr)
        {
            printf("内存分配失败!");
            return NULL;
        }
        ptr->data=arr[i];
        ptr->next=NULL;
        tail->next=ptr;
        tail=tail->next;
    }
    ptr->next=head;//使最后结点的后继指向头结点,形成循环链表
    return head;
}

void rotation(Linklist s,int m,int i)//循环圈函数,实现目标数出圈函数
{
    Lnode *p,*q;int cout=0,delete=0;
    p=s;
    q=p;//q为待删结点的前驱,p为待删结点
    while(delete<i)
    {
        cout++;
        if(cout==m)//若报数到m则出圈
        {
            printf("->%d",p->data);
            q->next=p->next;
            free(p);
            delete++;//记录已删除数据的个数,防止只有一个结点时进入死循环
            p=q;
            cout=0;
        }
        q=p;
        p=p->next;
    }
}
int main()
{
    Linklist s;
    int arr[maxsize];
    int i=0,j=0,flag=1,x,m;
    printf("please input the nums(-1 is stand for stopping):\n");
    while(flag)//将数据存储到arr数组中
    {
        scanf("%d",&x);
        if(x==-1)
            flag=0;
        else
        {
            arr[i]=x;
            i++;
        }
    }
    s=creat(arr,i);
    while(j<i)//j记录输出结点数据的次数,防止其为循环链表导致死循环
    {
        printf("%d ",s->data);
        s=s->next;
        j++;
    }
    printf("\nplease input the nums you want to go out:\n");
    scanf("%d",&m);
    rotation(s,m,i);
    return 0;
}

变式:1)顺序表解决该问题。需考虑如何实现循环的顺序结构。

           2)m不再固定。假设n个人每人持有一个密码(正整数),从编号k的人开始从一开始顺序报数,报到m的人出列,此时将他的密码作为新的m值,如此循环下去·,直到所有人出列为止

下面是从1开始报数,初始的m值由用户给定

#include <stdio.h>
#include <stdlib.h>
#define maxsize 100

typedef struct
{
    int num;
    int data;
} nums;
typedef struct
{
    nums stu[maxsize];
    int last;
} seqlist;

seqlist* init(int arrsize)
{
    seqlist* L;
    L=(seqlist*)malloc(sizeof(seqlist));
    L->last=arrsize-1;
    return L;
}
seqlist* creat(int n)
{
    seqlist* s;
    int x,i;
    s=init(n);
    printf("please input the secret nums of everyone:\n");
    for(i=0; i<n; i++)
    {
        scanf("%d",&x);
        s->stu[i].data=x;
    }
    for(i=0; i<n; i++)
    {
        s->stu[i].num=i+1;
    }
    return s;
}
void result(seqlist* s,int n,int m)
{
    int i=0,j,count=0;
    int k=0,flag=0;
    while(count<n)//while(count<n&&i<=s->last)
    {
        while(s->stu[i].num!=m)//若num不是m则继续报数
        {
            if(i>s->last)
                i=0;
            else
            {
                s->stu[i].num=++k;
                i++;
            }
        }
            m=s->stu[i].data;
            printf("->%d",s->stu[i].data);
            for(j=i+1; j<=s->last; j++)
            {
                s->stu[j-1].num=s->stu[j].num;
                s->stu[j-1].data=s->stu[j].data;
             //   flag=1;//记录有数据出队
            }
           // i--;
            s->last--;
            //数据出队后重置序号
                k=0;
                for(j=i; j<=s->last; j++)//for(j=i+1; j<=s->last; j++)
                {
                    s->stu[j].num=++k;
                }
                for(j=0; j<i; j++)// for(j=0; j<=i; j++)
                {
                    s->stu[j].num=++k;
                }
            count++;//记录删除的数据个数
         //   i++;
    }
}
int main()
{
    seqlist* s;
    int n,m;
    printf("please input the nums of the people:\n");
    scanf("%d",&n);
    printf("please input a num you want to go out at first:\n");
    scanf("%d",&m);
    s=creat(n);
    result(s,n,m);
    return 0;
}

约瑟夫环的输出序列

输入如下:

5//代表指定的人数n

3//第一个开始报数的人的编号k(1<=k<=5)

4//报数上限值m

输出如下:

1 5 2 4 3  //输出序列

输入输出样例:1组

#1

  • 样例输入:
    5
    3
    4
  • 样例输出:
    1 5 2 4 3
#include <stdio.h>
#include <stdlib.h>
#define maxsize 100
typedef struct list
{
    int data;
    struct list *next;
} Lnode,*Linklist;

Linklist creat(int *arr,int len)//创建单循环链表
{
    int i;
    Linklist head,tail,ptr;//
    head=(Linklist)malloc(sizeof(Lnode));
    if(len>1)//若不止一个数,进行链表插入处理
    {
        if(!head)
        {
            printf("内存分配失败!");
            return NULL;
        }
        head->data=arr[0];
        head->next=NULL;
        tail=head;//首尾相连

        for(i=1; i<len; i++)
        {
            ptr=(Linklist)malloc(sizeof(Lnode));
            if(!ptr)
            {
                printf("内存分配失败!");
                return NULL;
            }
            ptr->data=arr[i];
            ptr->next=NULL;
            tail->next=ptr;
            tail=tail->next;
        }
        ptr->next=head;//使最后结点的后继指向头结点,形成循环链表
        return head;
    }
    else//只有一个数,进行首尾相连操作
    {
        if(!head)
        {
            printf("内存分配失败!");
            return NULL;
        }
        head->data=arr[0];
        head->next=NULL;
        tail=head;
        tail->next=head;//首尾相连
        return head;
    }
}

void rotation(Linklist s,int m,int n,int num)//循环圈函数,实现目标数出圈函数
{
    Lnode *p,*q;
    int cout=0,delete=0;
    p=s;
    q=p;//q为待删结点的前驱,p为待删结点
    while(p->data!=num)//先找到编号为num的数字
    {
        q=p;
        p=p->next;
    }
    while(delete<n)//剩最后一个数没有输出,进入循环之前已经跳出一个数据
    {
        cout++;
        if(cout==m)//若报数到m则出圈
        {
            printf("%d ",p->data);
            q->next=p->next;
            free(p);
            delete++;//记录已删除数据的个数,防止只有一个结点时进入死循环
            p=q;
            cout=0;
        }
        q=p;
        p=p->next;
    }
}
int main()
{
    Linklist s;
    int arr[maxsize];
    int i=0,j=0,m,n,num;
    //printf("please input the nums(-1 is stand for stopping):\n");
    scanf("%d",&n);//输入个数

    for(i=1; i<=n; i++) //赋值
    {
        arr[i-1]=i;
    }
        s=creat(arr,n);
        scanf("%d",&num);//开始报数的编号
        scanf("%d",&m);//报数的上限,即报到那个数就出圈
        // printf("\nplease input the nums you want to go out:\n");
        rotation(s,m,n,num);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42107106/article/details/83021162