单项循环链表经典问题——问题一:约瑟夫问题

  • 问题描述:据说著名犹太历史学家Josephus有过以下的故事:

在罗马人占领桥塔帕特后,39个犹太人与Josephus以及他的朋友躲在一个洞中,39个犹太人决定宁愿四也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第一个人开始报数,每报数到第3个人该人必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus和他的朋友并不想遵从,Josephus要它的朋友先假装遵从,他将朋友与自己安排在了第16个与第31个位置,于是逃过了这场死亡游戏。

  • 思路解析:我们先来看一看下面的图解:图中只画了20个,因为41个实在太多了,画不下,每数到3的人就自杀,我们先来看看第一轮死掉了哪些人(图中黄色为挂掉的人)123(123)456(123)789(123)10 11 12(123)13 14 15 (123)16 17 18 (123)19 20 1(123)(这里要悄悄的感谢一下我的老师,这个图确实太难画了,我小小的尝试了一下下,结果可想而知,惨兮兮的)

单项循环链表经典问题——问题一:约瑟夫问题_第1张图片单项循环链表经典问题——问题一:约瑟夫问题_第2张图片

    然后以此类推,最后剩下的就是13和20(41个人的话剩下的就是16和31),所有我们可以利用单向循环链表来解决这个问题,通过删除的方法,判断出最后剩下的两个元素,即可解决这个问题,目前还不太了解单向循环链表的小可爱们可以参照我之前所写一篇关于单向循环链表的文章,可以作为参考哦!https://blog.csdn.net/qq_44941119/article/details/100868410

     所以我们定义了两个指针,分别指向头结点和尾结点,另外定义一个指针,因为每数到3的那个人gameover,那么只需要这个指针每次走到他前面那个元素(走一次),然后将要删除掉的节点的下一跳给指针所指的节点的下一跳,然后指针后移,依次循环,但是在循环的过程中当要删除的是尾结点的时候,我们不需要再和链表一样从头开始遍历找到删除的上一个结点,因为我们现在定义的指针就是指向要删除的结点的上一个结点,所以只需要将指针所指的结点的下一跳指向头结点,尾指针移动到这个结点(或者先移动尾指针至要指针所指向的结点,再将尾结点的下一跳指向头结点),要删除的是头结点的时候,头指针后移,尾指针指向当前头结点。

单项循环链表经典问题——问题一:约瑟夫问题_第3张图片单项循环链表经典问题——问题一:约瑟夫问题_第4张图片

单项循环链表经典问题——问题一:约瑟夫问题_第5张图片

单项循环链表经典问题——问题一:约瑟夫问题_第6张图片 删除头结点

       但是在我们这个图中20是不会被删除的,因为最终留下了的就是13和20,所以尾结点是没有被删除的可能,但是我们还是还模拟一下尾结点被删除的情况,也就是当指针p指向19的时候,我们要删除尾指针,我们要将20的下一跳给19,然后尾指针指向19。

     

单项循环链表经典问题——问题一:约瑟夫问题_第7张图片 删除尾结点

 

 public  LinkedList JossephusLoop(int number,int step) {
	   if(number<=0||step<=0) {
		   throw new IllegalArgumentException("参数不合法");
	   }
	 
	   head=null; //头指针为空
	   rear=null; //尾指针
	   size=0;   //有效元素个数
	   for(Integer i=1;i<=number;i++) { 
		  addLast((E) new Integer(i));
	   }  
	  //System.out.println(this);
	   LinkedList list=new LinkedList();//创建新的LinkedList对象
	   Node p=head; //判断要删除元素的指针
	   while(true) {
		   for(int i=0;i

     当然,我们这个问题的解决是建立在循环链表的基础上的,所以大家还是要很了解循环链表滴!

     下面给出测试类,测试方法也是写在循环链表的测试类里面的。

 

LinkedListout=loop.JossephusLoop(41, 3);
System.out.println(out);

运行结果:

你可能感兴趣的:(题目总结)