使用链表解决Josephus问题

Posted on 2020-07-15 17:54  GLLegolas  阅读(293)  评论(0编辑  收藏  举报

问题描述:

Josephus问题是下面的游戏:N个人编号从1到N,围坐成一个圆圈。从1号开始传递一个热土豆。经过M次传递后拿着热土豆的人被清除离座,围坐的圆圈紧缩,由坐在被清除的人后面的人拿起热土豆继续进行游戏。最后剩下的人取胜。

 

提供两种方案

package com.light.springboot.algorithm;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import org.apache.poi.ss.formula.functions.T; /** * Josephus问题是下面的游戏:N个人编号从1到N,围坐成一个圆圈。从1号开始传递一个热土豆。 * 经过M次传递后拿着热土豆的人被清除离座,围坐的圆圈紧缩,由坐在被清除的人后面的人拿起热土豆继续进行游戏。最后剩下的人取胜。 * @author qiaozhong */ public class Josephus { /** * 方案一:通过循环找list下标进行删除的方法解决约瑟夫问题 * @param person * @param num * @return */ public static String josephus(ArrayList<String> person, int num){ int i=0; while(person.size() > 1){ //循环num次,确定i for(int j=0; j < num; j++){ i++; } //当i大于当前总人数时,i减去总人数,使i小于总人数 while(i >= person.size()){ i = i - person.size(); } person.remove(i); } return person.get(0); }
     
/**
	 * 泛型写法
	 * @param person
	 * @param num
	 * @return
	 */
	public static <T> T josephusGeneric(ArrayList<T> person, int num){
		int i=0;
		while(person.size() > 1){
			//循环num次,确定i
			for(int j=0; j < num; j++){
				i++;
			}
			//当i大于当前总人数时,i减去总人数,使i小于总人数
			while(i >= person.size()){
				i = i - person.size();
			}
			person.remove(i);
		}
		return person.get(0);
	}

/** * 方案二:通过list的迭代器iterator解决约瑟夫问题,速度更快 * @param person * @param num * @return */ public static String iteratorJosephus(ArrayList<String> person, int num){ Iterator<String> iterator = person.iterator(); int count = 0; while (person.size()>1) { //步骤1:如果到链表尾部,就重新获取迭代器,以重头开始 if(!iterator.hasNext()){ iterator = person.iterator(); } //步骤2:只要没有到链表结尾,就一直循环查下一个元素 while(iterator.hasNext() && count++ <= num){ iterator.next(); } //步骤3:上边的while结束有两种可能,一种是count循环了num次,那么count++应该大于num,此种情况该删除当前元素 //另一种是查找到了链表尾部,此种情况不删除当前元素,应该回到步骤1重新执行iterator = person.iterator()继续查找 if (count > num) { count = 0; iterator.remove(); } } return person.get(0); } //测试两种方案效率 public static void main(String args[]){ ArrayList<String> person = new ArrayList(); for (int i = 0; i < 800000; i++) { person.add(String.valueOf("第" + (i+1) + "位客人")); } long start = new Date().getTime(); System.out.println(josephus(person, 7) + "获胜!"); System.out.println("方案一消耗时间:" + (new Date().getTime() - start)); long iteratorStart = new Date().getTime(); System.out.println(iteratorJosephus(person, 7) + "获胜!"); System.out.println("方案二消耗时间:" + (new Date().getTime() - iteratorStart)); } } 

 

测试结果:

第194525位客人获胜!
方案一消耗时间:43501
第194525位客人获胜!
方案二消耗时间:1

 

分析:

方案一:我们的代码设计部分,我们每次调用remove时,都重新遍历了一遍表,调用n次remove,消耗时间为O(N2)

方案二:通过迭代器能够有效地利用List的删除效率高的特性。因为迭代器的时间是O(1),迭代器的remove时间也是O(1),整体的运行时间理论上是O(N)。