使用java的循环单向链表解决约瑟夫问题
Posted menglong1108
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用java的循环单向链表解决约瑟夫问题相关的知识,希望对你有一定的参考价值。
什么是约瑟夫问题
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?
约瑟夫的故事比较残忍,我们把约瑟夫问题改为一个小游戏,游戏规则如下:
假设编号为1,2,3...n个人手拉手围坐在一圈,约定编号为k(1<= k <= n)的人开始报数,数到m的人出圈,他的下一位又从1开始报数,数到m的人又出圈,依次类推,直到剩余一个人即为胜出者,由此产生一个出圈编号的序列,
使用循环链表模拟n个人围坐一圈
首先我们定义一个循环链表,由此来模拟n个人围坐在一圈。下面先看下单链表的实现代码
1 /** 2 * 单向循环链表 3 */ 4 class CycleSingleLinkedList 5 PersonalNode first = null; 6 /** 7 * 向循环链表中添加num个节点 8 * @param num 9 */ 10 public void add(int num) 11 if(num < 1) 12 System.out.printf("参数不合法"); 13 return; 14 15 PersonalNode curr = null; 16 for (int i = 1; i <=num; i++) 17 PersonalNode personalNode = new PersonalNode(i); 18 if(i == 1) 19 //添加第一个节点 20 first = personalNode; 21 curr = first; 22 curr.setNext(first); 23 else 24 curr.setNext(personalNode); 25 personalNode.setNext(first); 26 curr = personalNode; 27 28 29 30 31 32 33 34 /** 35 * 人物节点 36 */ 37 class PersonalNode 38 private int no; 39 private PersonalNode next; 40 41 public PersonalNode(int no) 42 this.no = no; 43 44 45 public int getNo() 46 return no; 47 48 49 public void setNo(int no) 50 this.no = no; 51 52 53 public PersonalNode getNext() 54 return next; 55 56 57 public void setNext(PersonalNode next) 58 this.next = next; 59 60
下面我们用图例的方式讲解一下添加n个节点的循环链表的流程,当创建第一个节点时,我们让first和current指向第一个节点,并将current的next指向first节点,以此来形成一个循环。当添加第二个节点时,我们将current的next指针指向第二个节点,第二个节点的next指向first,然后将current后移,即current指向第二个节点,以此类推。
现在我们问题的第一步即n个人围坐在一起解决了,下面来实现第二步从第k个人开始数数。第二步比较简单,即让first指针后移k-1次,由此让first指针指向第一个数数的人。但由于first指向的节点需要出圈,这里我们再定义一个helper指针,来指向first指针的前一个节点,即让helper指针首先指向first,依此循环直到helper的next指针等于first(因为是循环链表)。并保持helper指针始终在first指针的前一个节点(只有一个节点时与first指针指向同一个节点)。假设k=2即从第二个人开始数,m=3即数到3的人出圈,如下图所示
找到node4的人出圈,这时让helper的next等于first的next,然后让first等于helper的next,如下图所示
继续从node1开始数,数到3,即node3出圈,此时指针指向情况如下图
node3出圈后,下一个要出圈的为node1,依此循环,直到helper与first指针指向相同节点,即node2为最后一个要出圈的人。代码如下
1 /** 2 * 约瑟夫问题 3 * @param n n个人围成圈 4 * @param k 第k个人开始报数 5 * @param m 数到m的人出圈 6 */ 7 public void joseph(int n,int k,int m) 8 //添加n个人 9 add(n); 10 //helper指针指向first的前一个节点 11 PersonalNode helper = first; 12 while (helper.getNext() != first) 13 helper = helper.getNext(); 14 15 //找到第k个人 16 for (int i = 0; i < (k-1); i++) 17 first = first.getNext(); 18 helper = helper.getNext(); 19 20 //数到m的人出圈 21 while (true) 22 if(helper == first) 23 break; 24 25 for (int i = 0; i < (m-1); i++) 26 first = first.getNext(); 27 helper = helper.getNext(); 28 29 System.out.printf("编号为%d的人出圈\\n",first.getNo()); 30 first = first.getNext(); 31 helper.setNext(first); 32 33 System.out.println("最后出圈的人是:"+first.getNo()); 34 35
测试代码
1 public static void main(String []args) 2 CycleSingleLinkedList linkedList = new CycleSingleLinkedList(); 3 linkedList.joseph(4,2,3); 4
测试结果:
总结
以上就是实现约瑟夫问题的循环链表解决思路,当然肯定还有很多别的解决思路,欢迎留言探讨,谢谢。
以上是关于使用java的循环单向链表解决约瑟夫问题的主要内容,如果未能解决你的问题,请参考以下文章