约瑟夫环的算法解析(java实现)

什么是约瑟夫环?

约瑟夫环(约瑟夫问题)是一个数学的应用问题,又称“丢手绢问题”
已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。
从编号为k的人开始报数,数到m的那个人出列;
他的下一个人又从1开始报数,数到m的那个人又出列;
依此规律重复下去,直到圆桌周围的人全部出列。
通常解决这类问题时我们把编号从0~n-1,最后 结果+1即为原问题的解。

如何求得最后一个出列的人的编号?

方法一 映射
核心 将第一次出列之后的环进行映射,使得第一次与其余出列的次数的编号一一致
从而得到递推公式
![
递推公式推导
约瑟夫环的算法解析(java实现)_第1张图片
循环版本(java实现)

public static int LastRemaining_Sol1(int n, int m) {
	        if(n == 0 || m <= 0) {
	            return -1;
	        }
	        int last = 0;
	        for(int i = 2; i <= n; i++) {
	            last = (last + m) % i;
	        }
	        return last;
	    }

递归版本

publicstatic int LastRemaining_Sol1((int n, int m) {
    if (n == 0)     
        return -1;
    if (n == 1)     // 递归返回条件
        return 0;
    return (LastRemaining_Sol1(n - 1, m) + m) % n;
}

方法二借助容器链表
用链表模拟圆圈,创建一个总共有 n 个结点的链表,然后每次在这个链表中删除第 m 个结点。
使用单链表,需要循环遍历,但链表不断删除出列元素,降低问题规模

java代码如下

package Demo_Josephuse;
import java.util.LinkedList;

public class Josephuse {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println(LastRemaining_Sol2(5,2)); 
	}
	public static int LastRemaining_Sol2(int n, int m) {
	        if(n == 0 || m <= 0) {
	            return -1;
	        }
	        LinkedList llist = new LinkedList<>();  
	        for(int i = 0; i < n; i++) {
	            list.add(i);
	        }
	        int index = 0; 
	        while( list.size() > 1) {
	            index = (index + m - 1) %  list.size();   // 依次为:1%5=1,2%4=2;3%3=0; 1%2=1;
	            list.remove(index);              //  % list.size() 实现环
	        }
	        return  list.get(0); // 初始编号2
	    }
}

你可能感兴趣的:(约瑟夫环的算法解析(java实现))