【刷题】牛客网 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♪(・ω・)ノ谢谢阅读

下一篇文章见!!!

相关推荐
丶Darling.1 小时前
代码随想录 | Day26 | 二叉树:二叉搜索树中的插入操作&&删除二叉搜索树中的节点&&修剪二叉搜索树
开发语言·数据结构·c++·笔记·学习·算法
no_play_no_games2 小时前
「3.3」虫洞 Wormholes
数据结构·c++·算法·图论
PYSpring3 小时前
数据结构-LRU缓存(C语言实现)
c语言·数据结构·缓存
Mr Aokey3 小时前
双向无头非循环链表的简单实现及介绍
数据结构
狐小粟同学4 小时前
链表面试编程题
数据结构·链表·面试
CyberMuse5 小时前
数值计算的程序设计问题举例
数据结构
陈奕迅本讯5 小时前
数据结构-链表笔记
数据结构·笔记·链表
江奖蒋犟5 小时前
【初阶数据结构】排序——归并排序
c语言·数据结构·算法
无限大.5 小时前
c语言实例 -- 循环链表
c语言·开发语言·链表
cdut_suye5 小时前
STL之list篇(下)(从底层分析实现list容器,逐步剥开list的外表)
开发语言·数据结构·c++·学习·算法·stl·list