09. 约瑟夫环

一、什么是约瑟夫环

  约瑟夫问题为:设编号为 1, 2, ……, n 的 n 个人围坐一圈,约定编号为 k (1≤ k ≤ n) 的人从 1 开始报数,数到 m 的那个人出列,她的下一位又从 1 开始报数,数到 m 的那个人又出列,以此类推,知道所有人出列为止,由此可以产生一个出队编号的序列。

  假设一共有 5 个人(n = 5),从第一个人开始报数(k = 1),数到 2 的那个人出列(m = 2),得到的出队编号如下:

约瑟夫环

  约瑟夫环的表示:

typedef int ElementType;

typedef struct LNode {
    ElementType Data;
    struct LNode * Next;
} LNode, * Josephus;

  生成约瑟夫环:

/**
 * @brief 生成约瑟夫环
 * 
 * @param count 小孩个数
 * @return Josephus 指向约瑟夫环的第一个节点的指针
 */
Josephus GenerateJosephus(int count)
{
    Josephus first = NULL, current = NULL, s = NULL;

    for (int i = 0; i < count; i++)
    {
        if (i == 0)
        {
            first = (Josephus)malloc(sizeof(LNode));
            first->Data = i + 1;
            first->Next = first;   
            current = first;
        }
        else
        {
            s = (Josephus)malloc(sizeof(LNode));
            s->Data = i + 1;
            s->Next = first;
            current->Next = s;
            current = s;
        }
    }

    return first;
}

  获取约瑟夫环的小孩出圈队列:

/**
 * @brief 获取约瑟夫环的小孩出圈队列
 * 
 * @param first 约瑟夫环
 * @param count 小孩个数
 * @param start 从哪个小孩开始报数
 * @param number 报数间隔
 */
void GetJosephusQuene(Josephus first, int count, int start, int number)
{
    Josephus s = NULL;
    Josephus current = first;

    if (first == NULL || count < 1 || start < 1 || start > count || number < 1)
    {
        printf("参数输入有误!\n");
        return;
    }

    // 报数前,current指针移动start-1次
    for (int i = 0; i < start - 1; i++)
    {
        current = current->Next;
    }
  
    // 创建一个辅助指针,帮助完成小孩出圈
    Josephus helper = first;
    // helper指针指向current指针前一个位置
    while (helper->Next != current)
    {
        helper = helper->Next;
    }
  
  
    // 开始报数,让current和helper指针同时移动number-1次,然后出圈,直到圈中只有一个小孩
    while (current->Next != current)
    {   
        // 让current和helper指针同时移动number-1次
        for (int i = 0; i < number - 1; i++)
        {
            current = current->Next;
            helper = helper->Next;
        }
        // 此时,current指针指向的就是要出圈的小孩
        printf("小孩%d出圈\n", current->Data);
        s = current;
        current = current->Next;
        helper->Next = current;
        free(s);
    }

    printf("最后一个小孩是%d\n", current->Data);
}

  打印生成的约瑟夫环:

/**
 * @brief 打印约瑟夫环
 * 
 * @param first 约瑟夫环的开始节点
 */
void PrintJosephus(Josephus first)
{
    Josephus current = first;

    if (current == NULL)
    {
        return;
    }
  
    while (1)
    {
        printf("%d ", current->Data);
        if (current->Next == first)
        {
            break;
        }
        current = current->Next;
    }
    printf("\n");
}

  main() 函数:

int main(void)
{
    Josephus first = 0, current = 0, helper = 0;
  
    int count = 0, start = 0, number = 0;

    printf("请输入小孩个数:");
    scanf("%d", &count);

    printf("请输入从第几个小孩开始报数:");
    scanf("%d", &start);

    printf("请输入报数间隔:");
    scanf("%d", &number);


    first = GenerateJosephus(count);
    printf("生成的约瑟夫环如下:\n");
    PrintJosephus(first);

    GetJosephusQuene(first, count, start, number);
  

    return 0;
}
posted @ 2023-07-01 18:51  星光樱梦  阅读(18)  评论(0编辑  收藏  举报