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


NC132 环形链表的约瑟夫问题

  • 题目描述
  • 思路一(链表直通版)
  • 思路二(数组巧解版)
  • 思路三(变态秒杀版)
  • Thanks♪(・ω・)ノ谢谢阅读
  • 下一篇文章见!!!

题目描述

【刷题】牛客网 NC132 环形链表的约瑟夫问题_第1张图片
根据描述,该题思路类似于报数,第一想法就是构建环形链表。

思路一(链表直通版)

  1. 构建环形链表,赋予对应序号
  2. 进行约瑟夫问题
  3. 报到对应数,删除节点
  4. 一直到只剩一个节点。
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;
}

来看运行效果:
【刷题】牛客网 NC132 环形链表的约瑟夫问题_第2张图片
运行很顺利

思路二(数组巧解版)

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

  1. 创建数组,并赋予对应序号值
  2. 开始遍历计数,报到m 将数组前移覆盖删除即可
  3. 一直反复进行到只剩一个元素。
#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,所以我们便可以开始逆推。

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♪(・ω・)ノ谢谢阅读

下一篇文章见!!!

你可能感兴趣的:(刷题,链表,数据结构)