约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)

约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)

如有错误,欢迎指正。

文章目录

      • 约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)
          • 问题阐述
          • 解决思路
          • 解决方法(2中方法)
            • 方法一(注重下一个节点的报数)
            • 方法二(注重当前节点的报数)

问题阐述

​ m个人围成一圈,按顺序编号。从第一个人开始从1到m报数,凡报到m的人退出圈子,求解最后留下的人的初始编号。程序运行示例;

6 3(两个输入数据之间有空格)

输入格式:scanf("%d%d", &n,&m);

输出格式:printf ("%d", loop[ dest1]):

解决思路

1.根据输入的人数创建环形链表(结构体存储每个人对应的编号)

2.利用变量count来记录报数的次数,当报到m时,将对应的节点从环形链表里面去除

3.重复步骤2,直到链表只剩下一个人的时候,输出结果

解决方法(2中方法)
方法一(注重下一个节点的报数)

此方法基于这个游戏的m不可能为1,且刚结束的一轮,下一轮第一个不会被踢出局。

注意事项:1.在创建环形链表的关键,先当作单链表创建,在于当链表走到最后一个节点时,它的next要指向链表的第一个,这就形成了环形链表(可以看作先制造一个绳子,当达到要求后,再把它首位链接形成环形)

​ 2.在结果判断时,变量count代表的意思下一个人报数的次数,如一开始count=1,表示还没开始时,xian是head(头)节点,count代表的是第一个节点为第一次轮报数的1号,当开始进行循环,count=2,此时xian是第一个节点,count代表下一个节点即第二个节点为第一次轮报数的2号。也就是说,count代表的是当前节点的下一个节点应该报的数。

​ 3.当指向下一个节点为游戏要求的m时

约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)_第1张图片

此时让count=1,即表示下一个节点重新为第一个节点。

4.当count代表的下一个节点报的数为m时,进行 3. 当中的操作,将其踢出局。

#include
#include

typedef struct student
{
    int num;
    struct student* next;
}S;

void Input(S** head,int n)//根据输入,创建相应的链表
{
    S* qian, * xian;
    int count = 0;
    qian = (S *)malloc(sizeof(S));
    * head = qian;
    qian->next = NULL;
    do
    {
        xian = (S*)malloc(sizeof(S));
        xian->num = count + 1;
        if (count != n - 1)//当节点不是最后一个时,当作单链表创建
        {
            xian->next = NULL;
        }
        else//当节点是最后一个时,将节点的next重新指向链表的开头
        {
            xian->next = (*head)->next;
        }
        qian->next = xian;
        qian = xian;
        count++;
    } while (count < n);

}

void Result(S* head,int m)
{
    int count = 1;
    S* xian;
    for (xian = head->next; xian != xian->next; xian = xian->next)
        //xian节点从环形链表第一个开始,当xian节点的下一个也是自己时,代表只剩下自己。不然就继续进行游戏(下一个人报数)
    {
        count++;
        if (count == m)
        {
            xian->next = xian->next->next;//具体细节看注意事项 3.
            count = 1;
        }
    }
    printf("%d",xian->num);//最后打印结果(最后剩下的人)
}

int main()
{
    int n, m;

    S* head;
    printf("请输入m,n: ");
    scanf("%d %d",&n,&m);
    Input(&head,n);
    Result(head,m);

    return 0;
}

方法二(注重当前节点的报数)

注意事项:1.在创建环形链表的关键,先当作单链表创建,在于当链表走到最后一个节点时,它的next要指向链表的第一个,这就形成了环形链表(可以看作先制造一个绳子,当达到要求后,再把它首位链接形成环形)

​ 2.在结果判断时,变量count代表是xian节点当前的报数,如还没开始是,count=0,表示xian节点报数为0号,当开始游戏时,count=1,代表xian节点的报号数,如第一次的count=1,xian节点为第一个节点,表示的是第一个节点在第一轮的报数为1号,当count=2时,xian节点为第二个节点,此时count表示第二个节点报数为2号。

​ 3.当报数号为m时

约瑟夫问题-关于一圈人排序,依次报数出局的问题(运用环形链表解决)_第2张图片

此时让count=0,表示新一轮游戏开始。

​ 4.当count代表的节点报的数为m时,进行 3. 当中的操作,将其踢出局。

#include
#include

typedef struct student
{
    int num;
    struct student * next;
}S;

void Input(S** head, int n)//创建环形链表,上一个方法有细讲,不做阐述
{
    S* qian, * xian;
    int count = 0;
    qian = (S*)malloc(sizeof(S));
    *head = qian;
    qian->next = NULL;
    do
    {
        xian = (S*)malloc(sizeof(S));
        xian->num = count + 1;
        if (count != n - 1)
        {
            xian->next = NULL;
        }
        else
        {
            xian->next = (*head)->next;
        }
        qian->next = xian;
        qian = xian;
        count++;
    } while (count < n);

}

void Result(S* head, int n, int m)
{
    int count = 0;//当前节点的报数号
    int people = n;//用于记录当前人数
    S* qian, * xian;
    qian = head;//最开始的qian节点
    xian = head->next;//环形链表的开始
    while (people != 1)//当人数不为1时,继续游戏
    {
        count++;//当前节点报号数
        if (count != m)//当报号数不为m时,正常进行履历链表
        {
            qian = xian;
            xian = xian->next;
            
        }
        else//当报号数为m时,通过注意事项 3. 将当前节点踢出局
        {
            qian->next = xian->next;
            xian = xian->next;
            count = 0;
            people--;//人数减1
        }
    }
    printf("%d", xian->num);//输出结果
}


int main()
{
    int n, m;
    int i = 0;
    S* head;
    printf("请输入m,n: ");
    scanf("%d %d", &n, &m);
    Input(&head, n);
    Result(head, n, m);

    return 0;
}

你可能感兴趣的:(链表,数据结构)