• 使用链表解决Josephus问题


    问题描述:

    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)。

     

    个人理解,如有错误,欢迎指正!
  • 相关阅读:
    日志统计|2018年蓝桥杯B组题解析第八题-fishers
    螺旋折线|2018年蓝桥杯B组题解析第七题-fishers
    递增三元组|2018年蓝桥杯B组题解析第六题-fishers
    乘积尾零|2018年蓝桥杯B组题解析第三题-fishers
    明码|2018年蓝桥杯B组题解析第二题-fishers
    第几天|2018年蓝桥杯B组题解析第一题-fishers
    2016年蓝桥杯B组C/C++省赛(预选赛)题目解析
    2016年蓝桥杯B组C/C++省赛(预选赛)试题
    计蒜客习题:同余方程
    数论——同余方程
  • 原文地址:https://www.cnblogs.com/gllegolas/p/13307013.html
Copyright © 2020-2023  润新知