初识循环链表-《约瑟夫问题》

题目描述:

​ 编号是1,2,……,n的n个人按照顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个仍开始顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他的顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。请设计一个程序输出出列顺序。

提示:存储结构采用不带头结点的循环单链表,结点结构如下:

typedef struct Node
{  
  int ID;
  int password;      
  struct Node *next;
}LNode,*LinkList;

要求:

(1)编写建立循环单链表的函数,依次输入每个人的ID和password建立不带头结点的循环单链表。

(2)编写函数,按照规则依次删除相应的元素。

(3)main()函数调用(1)和(2)中的函数,输出约瑟夫序列。

注意:m=1 时需要特殊处理。

CreateList(LinkList L,int n)-创建不带头节点的循环链表

函数功能描述

接收空链表以及要接收数据的条数n,内部使用尾插法实现结点的插入,并在数据录入完成后实现链表的首位相连。

代码实现

//根据输入创建链表
LinkList CreateList(LinkList L,int n) {
    L = (LinkList) malloc(sizeof(LNode));
    LinkList nd;                                    //新节点,接收数据,动态分配内存
    LinkList p = L;                                 //工作指针

    scanf("%d %d",&L->ID,&L->password);             //接收数据
    L->next = NULL;                                 //头指针指向空,代表空链表

    while(n-- > 1) {                                //向链表内添加数据
        nd = (LinkList) malloc(sizeof(LNode));

        scanf("%d %d",&nd->ID,&nd->password);       //接收数据
        p->next = nd;                               //向链表后端添加节点
        p = p->next;
        if(n == 1) {                                //判断是否到达数据末尾
            p->next = L;                            //连接首位,形成循环链表
        } else {
            p->next = NULL;													//封尾链表
        }
    }
    return L;
}

cycle(LinkList L,int m)-操作约瑟夫环并打印每次出圈人的序号

函数功能描述

接收目标链表以及报数值,运用递归思想实现约瑟夫问题

代码实现

int cycle(LinkList L,int m) {
    int password;

    if(L == L->next) {															//判断环是否只剩下最后一个
        printf("%d\n",L->ID);
        return 0;
    }
    LinkList p = L;																	//工作指针
    int num = 1;																		//记录迭代次数

    if(m == 1) {																		//判断是否是m=1的特殊情况
        password = L->password;											//将表头元素的password取出用以递归调用并打印
        printf("%d\n",L->ID);
        while(p->next != L) {												//迭代链表直至最后一个结点
            p = p->next;
        }
        p->next = p->next->next;										//删除表头结点,并将最后一个结点与第二个结点连接
        p = p->next;																//将工作指针移到删除结点的下一结点,以便递归调用
    } else {
        while(num++ < m-1) {												//迭代至目标结点的上一结点,便于删除
            p = p->next;
        }
        password = p->next->password;								//将目标结点的password取出用以递归调用并打印
        printf("%d\n",p->next->ID);
        p->next = p->next->next;										//删除目标结点并连接前后两结点
        p = p->next;																//将工作指针移到删除结点的下一结点,以便递归调用
    }

    cycle(p,password);															//递归
}

main函数实现

int main(){
    int n,m;
    scanf("%d\n%d",&n,&m);      //获取n,m值
    LinkList N;
    N = CreateList(N,n);

    cycle(N,m);

    return 0;
}

样例输入1

8 
4
1 3
2 1
3 9
4 2
5 4
6 7
7 4
8 6

样例输出1

4 
6 
7
3
5
8
2
1

样例输入2

4
1
1 2
2 3
3 4
4 1

样例输出2

1
3
4
2

注意:m=1的情况、输出最后需要打回车、只剩一个结点时要打印出最后一个结点的ID值

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值