环形链表的约瑟夫问题

文章目录

  • 使用链表模拟
  • ArrayList 类(动态类型顺序表)
  • 数组记录排除(效率很低)
  • 递归
  • 利用数学公式反推(就是递归的方法推导)

牛客网链接
编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。
下一个人继续从 1 开始报数。
n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

数据范围: 1≤n,m≤10000

使用链表模拟

import java.util.*;


public class Solution {
   class node//创建链表节点
    {
        int val;
        node(int val) {
            this.val=val;
        }
        node next;
    }
    public int ysf(int n, int m) {
        if(m==1)return n-1;//一次一个直接返回最后一个即可
        node pHead = new node(1);
        node pTail = pHead;//创建一个链表
        for(int i=2;i<=n;i++)
        {
            pTail.next=new node(i);
            pTail=pTail.next;
        }
        pTail.next=pHead;//使形成环

        int count = 1;//从1开始计数
        while (pHead.next!=pHead) {//当剩余节点不止一个的时候
            //如果count=m-1 那就说明下个节点该删除了
            if(count == m-1)
            {
                pHead.next = pHead.next.next;
                count = 1;
            }
            else {
               count++;
            }
            pHead = pHead.next;
        }
        return pHead.val;
    }
}

ArrayList 类(动态类型顺序表)

import java.util.*;


class Solution {
    public int ysf(int n, int m) {
    	if(m==1) return n-1;
        List<Integer> list=new ArrayList<>();
        for(int i=0;i<n;i++)
        {
            list.add(i);
        }
        int index=0;
        while (list.size()>1)
        {
            index=(index+m-1)%(list.size());
            list.remove(index);
        }
        return list.get(0)+1;
    }
}

数组记录排除(效率很低)

public class Solution {
     public static int ysf(int n, int m) {
        if (m == 1) return n - 1;
        boolean[] arr = new boolean[n];
        int count = 0;//排除的次数
        int k = 1;//k等于m时排除那个数
        int i = 0;//当前元素下标
        while (count != n) {
            i++;
            if (i == n) {
                i = 0;
            }
            if (arr[i] == false) {
                k++;
                if (k == m) {
                    arr[i] = true;
                    count++;
                    k = 0;
                }
            }
        }
        return i + 1;
    }
}

递归

环形链表的约瑟夫问题_第1张图片

编号一定从0开始(取模有0 ,可以形成循环)
题目中编号是从1开始,之后可以变为从0开始,然后+1返回

  • 我们发现倒数第二列的数字分别对应不同n情况下存活的人的编号,称其为答案列
  • 下一行的0下标是当前行的m-1下标后面一个,对比当前行0下标.得出相邻行之间的偏移量为m
  • 答案列最下面的值是0

所以已知下一行答案列的值,可以加上偏移量再%当前行的n得出这一行答案列的值
{ y s f ( n , m ) = 0 , n = = 1 y s f ( n , m ) = ( y s f ( n − 1 , m ) + m ) % n , n ! = 1 \left\{ \begin{matrix} ysf(n,m)=0,n==1 \\ ysf(n,m)= (ysf(n-1,m)+m) \% n, n!=1 \end{matrix} \right. {ysf(n,m)=0,n==1ysf(n,m)=(ysf(n1,m)+m)%n,n!=1

只能获得最后存活者的编号,无法像模拟法一样可以获取淘汰过程中的编号序列。
按照牛客网题目要求编号从1开始

class Solution {
   public int ysf(int n, int m) {
        if(n==1)
            return 1;
        return (ysf(n-1,m)-1+m)%n+1;//先-1下标从0开始计算,之后+1变成从1开始
    }
}

多一个参数i,表示淘汰的次数,这样可以获取淘汰过程中的编号序列
环形链表的约瑟夫问题_第2张图片

  • 比如ysf(n=8,m=3,i=6)的答案列就是倒数第一列,比ysf(n=8,m=3,i=8)少了两次移除元素.不是从n == 1,而是从n==3开始往上推
  • 跟上面没有i的公式相比只是i == 1的情况变了,上面公式假设有 i , i 的值跟n一样, 从n == 1 开始往上推,而这个公式可能不是从n==1开始往上推
    { y s f ( n , m , i ) = ( m − 1 ) % n , i = = 1 y s f ( n , m , i ) = ( y s f ( n − 1 , m , i − 1 ) + m ) % n , i ! = 1 \left\{ \begin{matrix} ysf(n,m,i)=(m-1)\%n,i==1 \\ ysf(n,m,i)= (ysf(n-1,m,i-1)+m) \% n,i!=1 \end{matrix} \right. {ysf(n,m,i)=(m1)%n,i==1ysf(n,m,i)=(ysf(n1,m,i1)+m)%n,i!=1
int ysf(int n,int m,int i) {
        if(i==1) {
            return (m-1)%n;
        }
        return (ysf(n-1,m,i-1)+m)%n;
    }

利用数学公式反推(就是递归的方法推导)

反推过程:(当前index + m) %当前人数。

import java.util.*;
 
 
public class Solution {
    public int ysf (int n, int m) {
    	//答案列最后的值
         int index = 0;
        //从倒数第二行开始
        for (int i = 2; i <= n; i++) {
            index = (index + m) % i;
        }
        return index+1;
    }
}

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