6-1 约瑟夫问题

作者 陈越 单位 浙江大学

编号为 1,2,3,⋯,n 的 n 个人按照顺时针方向围坐一圈。从第一个人开始按照顺时针方向从 1 开始报数,当报到指定的数 m 时,报 m 的人出列。再从他顺时针方向的下一位人开始重新从 1 开始报数,报到 m 的人出列,如此下去,直到所有人都出列。请设计算法用单向循环链表模拟约瑟夫问题的出列过程,输出出列的顺序。

函数接口定义:

List JosephusProblem( int n, int m );

其中List结构定义如下:

typedef struct ListNode *Position; /* 指针即结点位置 */
struct ListNode {
    ElemSet data;  /* 存储数据*/
    Position next;  /* 线性表中下一个元素的位置 */
};
typedef struct HeadNode *List;
struct HeadNode {
    Position head;  /* 单链表头指针 */
    int length;    /* 表长 */
};

函数接口定义中,n 是总人数,m 是出列间隔的人数。该函数须将出列人的编号按照出列顺序存为带空头结点的单链表,并返回该链表的表头指针。

裁判测试程序样例:

#include <stdio.h>
#include <stdlib.h>

typedef int ElemSet;
typedef struct ListNode *Position; /* 指针即结点位置 */
struct ListNode {
    ElemSet data;  /* 存储数据 */
    Position next; /* 线性表中下一个元素的位置 */
};
typedef struct HeadNode *List;
struct HeadNode {
    Position head; /* 单链表头指针 */
    int length;    /* 表长 */
};

List JosephusProblem( int n, int m );

void PrintList( List list )
{ /* 顺序输出链表结点数据 */
    Position p;
    
    p = list->head->next; /* p指向第1个结点 */
    while (p) {
        printf("%d ", p->data);
        p = p->next;
    }
}
int main(void)
{
    List list;
    int n, m;
    scanf("%d %d", &n, &m);
    list = JosephusProblem(n, m);
    PrintList( list );
    return 0;
}
/* 你的代码将被嵌在这里 */

输入样例:

10 3

输出样例:

3 6 9 2 7 1 8 5 10 4 

答案:

List JosephusProblem(int n, int m)
{
    Position head = (Position)malloc(sizeof(struct ListNode)); // 头节点
    Position current = head; // 当前节点
    Position newNode;

    // 创建循环链表
    for (int i = 1; i <= n; i++)
    {
        newNode = (Position)malloc(sizeof(struct ListNode));
        newNode->data = i;
        current->next = newNode;
        current = newNode;
    }
    current->next = head->next; // 完成循环链表

    // 创建结果链表
    List resultList = (List)malloc(sizeof(struct HeadNode));
    Position resultHead = (Position)malloc(sizeof(struct ListNode));
    Position resultTail = resultHead; // 结果链表的尾部
    resultList->head = resultHead;
    resultList->length = 0;

    // Josephus 问题处理
    Position prev = head; // prev 指向当前节点的前一个节点
    Position toRemove = head->next; // 要删除的节点
    int count = 1;

    while (resultList->length < n)
    {
        if (count != m)
        {
            // 如果还没到第 m 个节点,继续前进
            prev = toRemove;
            toRemove = toRemove->next;
            count++;
        }
        else
        {
            // 删除第 m 个节点
            prev->next = toRemove->next;
            resultTail->next = toRemove;
            resultTail = resultTail->next;
            resultTail->next = NULL; // 结果链表尾部节点的 next 设为 NULL

            // 更新要删除的下一个节点
            toRemove = prev->next;
            count = 1;
            resultList->length++;
        }
    }

    free(head); // 释放循环链表的头节点
    return resultList;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值