【刷题】牛客网 NC132 环形链表的约瑟夫问题

NC132 环形链表的约瑟夫问题

题目描述

根据描述,该题思路类似于报数,第一想法就是构建环形链表。

思路一(链表直通版)

  1. 构建环形链表,赋予对应序号
  2. 进行约瑟夫问题
  3. 报到对应数,删除节点
  4. 一直到只剩一个节点。
c 复制代码
typedef struct listnode{
    int val ;
    struct listnode* next;
} Listnode;

Listnode* buynode(int n ){
	//开辟空间
    Listnode* node = (Listnode*)malloc(sizeof(Listnode));
    //序号赋值
    node->val = n;
    //next 指针赋值
    node->next = NULL;
    //返回节点
    return node;
}
 Listnode* createlist(int n){
    //序号
    int i = 1;
    //创建头尾
    Listnode* phead ,*ptail;
    //先创建第一个节点
    phead = ptail = buynode(i++);
    //循环构建链表
    while(i<=n){
        ptail->next = buynode(i++);
        ptail = ptail->next;
    }
	//将头尾相连,构成循环链表
    ptail->next = phead;
    //返回头节点
    return phead;

 }
int ysf(int n, int m ) {
    // 创建环形链表
    Listnode * head = createlist(n);

    //约瑟夫问题
    //计数
    int count = 1 ;
    //定义 前指针 当前指针
    Listnode* cur ,*prev;
    cur = head;
    prev = NULL;
    while(cur->next != cur){
    	//报到指定数 删除节点
        if(count == m){
        	//前节点 的next指向 当前节点的下一个节点即可删除
            prev->next = cur->next;
            cur = cur->next;
            //报数重置
            count = 1;

        }else{
        	//没有报到指定数 则向后移动。
            prev = cur;
            cur = cur->next;
            count++;
        }
    }
   	//返回序号
    return cur->val;
}

来看运行效果:

运行很顺利

思路二(数组巧解版)

链表的实现虽然简洁,但是遇到较大数据时难免会开辟较大内存空间,所以我们可以使用数组模拟循环链表的过程。

  1. 创建数组,并赋予对应序号值
  2. 开始遍历计数,报到m 将数组前移覆盖删除即可
  3. 一直反复进行到只剩一个元素。
c 复制代码
#define max_num 100001
int ysf(int n, int m ) {
    //初始化数组
    int man[100001];
    for(int i = 0;i<n;i++){
        man[i] = i + 1;
    }
    //计数
    int count = 1;
    //控制下标
    int i =0;
    while(n > 1 ){
        //如果报到对应数
        if(count == m){
        //向前覆盖删除元素
            for(int j = i;j < n-1;j++){
                man[j] = man[j + 1];
            }
            //计数重置
            count = 1;
            //人数减 1
            n--;
            //注意不需要将 i++ 因为删除过程 i 已经指向了后一个元素。
        }
        else{
        //不是对应数 i++ 计数加1
            i++;
            count++;
        }
		//保证不超出 n 范围
        i %= n;

    }
    //返回对应序号
    return man[i];
}

思路三(变态秒杀版)

该思路使用数学公式,进行快速计算
F(N,M)=(F(N-1,M)+M)%N

即我们可以通过目前幸存者逆推其一开始的序号。

而根据刚才的数组思路,可以知道最后的幸存者数组下标是0,所以我们便可以开始逆推。

c 复制代码
int ysf(int n, int m ) {
    //F(N,M)=(F(N-1,M)+M)%N
    //最后幸存者下标为 0
    int p = 0;
    //从人数为2开始逆推,直到人数为n
    for (int i = 2; i <= n; i++) {
    	//依次移动
        p = (p + m) % i;
    }
    //记得将序号加一
    return p + 1;

}

Thanks♪(・ω・)ノ谢谢阅读

下一篇文章见!!!

相关推荐
YL2004042615 分钟前
027合并两个有序链表
java·数据结构·算法·链表
炽烈小老头1 小时前
【每天学习一点算法 2026/05/10】合并K个排序链表
学习·算法·链表
Zephyr_01 小时前
java数据结构
java·数据结构
xieliyu.2 小时前
Java手搓二叉树:基础遍历与核心操作全解析
java·开发语言·数据结构·学习
期待のcode2 小时前
Redis数据类型
运维·数据结构·redis
博界IT精灵2 小时前
图的遍历(哈喜老师)
数据结构·考研·算法·深度优先
所以遗憾是什么呢?3 小时前
【题解】Codeforces Round 1097 (Div. 2, Based on Zhili Cup 2026) (致理杯) ABCDEF
数据结构·算法·acm·codeforces·icpc·ccpc·xcpc
Lazionr3 小时前
【栈与队列经典OJ】
c语言·数据结构
夏日听雨眠3 小时前
数据结构(哈希函数)
数据结构·算法·哈希算法
诙_3 小时前
C++数据结构--B树,B+树,B*树
数据结构·b树