约瑟夫环有关问题 java代码实现(高效率)
约瑟夫环问题 java代码实现(高效率)
运行结果:
问题来历编辑
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。[1]
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
*问题分析与算法设计
约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
这里我使用java数据结构中的双向循环链表
完整代码如下:
<pre name="code" class="java">//约瑟夫问题 package com.test; public class test { public static void main(String[] args) { CycLink cycLink= new CycLink(5,2,3); } } class ChildNode{ int Num;//编号 ChildNode nextChildNode=null;//指向下一个结点 ChildNode preChildNode=null; public ChildNode(int num){ this.Num=num; } } class CycLink{//环形链表 //先定义一个指向链表第一个结点的引用,指向第一个结点的引用,不能动 ChildNode TofirstChildNode=null; ChildNode temp=null; int len=0;//共有多少个结点 int k=0;//从第几个人开始数数 int m=0;//数m下 int n=1;//第n个人出局 //设置链表大小 public CycLink(int len,int k,int m){ this.len=len;//设置环形链表大小 this.k=k;//设置从第几个人开始数数 this.m=m;//设置数m下 creatLink(); showLink(); play(); } //开始约瑟夫规则游戏 private void play(){ ChildNode cursor=this.TofirstChildNode; //1.先开始找到开始数数的人 for(int i=1;i<k;i++){ cursor=cursor.nextChildNode; } while(this.len!=1){ //2.数m下,找到了要删除的结点 for(int j=1;j<m;j++){ cursor=cursor.nextChildNode; //其实没人引用了,那么过会儿就会被GC回收 } //3.将数到m的结点,退出圈 System.out.println("第"+this.n+"次出局的人编号是:"+cursor.Num); cursor.preChildNode.nextChildNode=cursor.nextChildNode;//删除操作 cursor.nextChildNode.preChildNode=cursor.preChildNode;//删除操作 //让cursor指向下一个开始数数的人,这个动作很重要,不然游标就不动了 cursor=cursor.nextChildNode; this.len--;//出圈一个人, this.n++; } //最后一个剩下的结点 System.out.println("最后还在桌子上坐着的人编号:"+cursor.Num); } //初始化环形链表 private void creatLink(){ for(int i=1;i<=len;i++){ if(i==1){ //创建第一个结点 ChildNode ch=new ChildNode(i); this.TofirstChildNode=ch; temp=ch; } else{ //创建最后一个结点 if(i==len){ ChildNode ch=new ChildNode(i); temp.nextChildNode=ch; ch.preChildNode=temp; temp=ch; temp.nextChildNode=this.TofirstChildNode; this.TofirstChildNode.preChildNode=ch; } else{ //继续创建结点 ChildNode ch=new ChildNode(i); temp.nextChildNode=ch; ch.preChildNode=temp; temp=ch; } } } } //打印环形链表 private void showLink(){ //定义一个游标 ChildNode cursor=this.TofirstChildNode; do{ System.out.println("参员编号为:"+cursor.Num); cursor=cursor.preChildNode; } while(cursor!=this.TofirstChildNode); } }
运行结果:
参员编号为:1
参员编号为:5
参员编号为:4
参员编号为:3
参员编号为:2
第1次出局的人编号是:4
第2次出局的人编号是:2
第3次出局的人编号是:1
第4次出局的人编号是:3
最后还在桌子上坐着的人编号:5
参员编号为:5
参员编号为:4
参员编号为:3
参员编号为:2
第1次出局的人编号是:4
第2次出局的人编号是:2
第3次出局的人编号是:1
第4次出局的人编号是:3
最后还在桌子上坐着的人编号:5