约瑟夫问题(有时也称为约瑟夫斯置换,是一个计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.) 其一般形式:约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。
问题来历:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 [1] 17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
以上资料均来自------约瑟夫问题_百度百科
我们在这里把问题简化成:N个孩子围成一圈,从第k个孩子开始报数,每次从一开始报数,报到M的孩子出列,下一个孩子继续从1开始报数,直到剩下最后一个孩子,在此期间,我们要打印出出队的孩子的编号。 我们可以用单向循环链表和利用数组,对数组下标进行取模运算来解决此问题,在这里我们只用单向循环链表来解决此问题。
假设此时N=5,k=1,M=2
按照孩子的编号创建一个没有头节点的单向循环链表,first代表的是第一个报数的孩子 将first指向的指定的第一个报数的孩子(first = first.next执行k-1次,注意不是执行k次,这是因为第一个孩子本身也要报数),在我们假定的情况下,first不需要移动。创建一个辅助指针helper,事先让这个指针指向最后一个节点(判断条件:helper==first) 当小孩报数时,helper和first同时移动M-1次,这时候,first所指向的孩子就是要出列的孩子
将first所指向的孩子的编号进行打印出列的操作:实际上就是循环队列的删除节点的操作: first = first.next; helper.next = first附上源码:
创建循环单链表类,并在里面定义创建没有头节点的循环单链表的方法,和打印出列的孩子的方法,和遍历整条循环单链表的方法:
startNumber表示从编号为startNumber的孩子开始报数, countNumber表示从1一直报到countNumber, sum表示孩子的总数
public void countBoy(int startNumber, int countNumber, int sum) { addBoy(sum);//创建循环单链表 if(first==null || startNumber<1 || startNumber>sum) {//先对传入的实参进行校验 throw new RuntimeException("输入的数据有误!!"); } //把first指针指向第一个报数的孩子 for(int i=1; i<startNumber; i++) { first = first.getNext(); } //创建辅助指针,并让辅助指针指向循环单链表的最后一个孩子节点 Boy helper = first; while(helper.getNext()!=first) { helper = helper.getNext(); } while(true) { if(first==helper) {//此时只剩下了一个孩子节点 break; }else { for(int i=1; i<countNumber; i++) {//对first和helper的移位操作 first = first.getNext(); helper = helper.getNext(); } System.out.println("出圈的小孩的编号为:"+first.getNumber()); first = first.getNext();//删除孩子节点操作 helper.setNext(first);; } } System.out.println("最后留下的小孩编号为:" + first.getNumber()); }