据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
上边的这个故事就是我们约瑟夫问题的由来,约瑟夫问题的简化版,
在我们实现约瑟夫问题中的一个关键就是,我们如何找到那个要出队的元素,这个元素如何出队列?我们的数据结构采用的是有向单链表,我们要向实现元素的剔除就是要通过这个元素的上一个元素的这个t指针指向这个元素的下一个元素,从而实现将目标元素剔除。又因为我们的元素链表是单向的,如何记住我们目标元素的上一个元素,这时候我们需要一个协助指针。协助指针的初始化也是要是我们开始元素的前一个元素。
下面使我们核心代码的一个分析,关于最后一句的分析是不正确的,我们所有的输出是可以直接在循环体中完成的:
节点类:
class Boy{
private int num;
private Boy next;
public Boy(){
}
public Boy(int num){
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
约瑟夫环:
class JusepufLinks {
private Boy firstBoy = null;
private int boysCount = 0;
public int getBoysCount() {
return boysCount;
}
public Boy getFirstBoy() {
return firstBoy;
}
public JusepufLinks(int nums){
this.boysCount = nums;
/**
*@description: 这是一个构造函数,用来构造一个单向循环链表
*@params: 小孩的数量
*/
if(nums<1){
System.out.println("nums,链表数不合法,链表数的最小值为1");
}else{
Boy currentBoy = new Boy();
for (int i = 1; i <= nums; i++) {
Boy boy = new Boy(i);
if(i==1){
firstBoy = boy;
firstBoy.setNext(boy);
currentBoy = firstBoy;
}else{
currentBoy.setNext(boy);
boy.setNext(firstBoy);
currentBoy = boy;
}
}
}
}
public int countBoys(){
int count = 1;
Boy temp = firstBoy;
while (temp.getNext()!=firstBoy){
temp = temp.getNext();
count++;
}
return count;
}
public void showJusepufLinks(){
if(firstBoy==null){
System.out.println("单向循环链表为空");
}else{
Boy showTempBoy = firstBoy;
do {
System.out.printf(showTempBoy.getNum()+"->");
showTempBoy = showTempBoy.getNext();
}while (showTempBoy.getNext()!=firstBoy);
System.out.println(showTempBoy.getNum()+"");
}
}
public void testShow(){
for (int i = 0; i < 20; i++) {
System.out.println(firstBoy.getNum()+"******************");
firstBoy=firstBoy.getNext();
}
}
}
测试代码,约瑟夫问题核心解决逻辑:
public class JusepufLinksTest{
public static void main(String[] args) {
JusepufLinks jusepufLinks = new JusepufLinks(6);
jusepufLinks.showJusepufLinks();
jusepuf(jusepufLinks,2,3);
}
public static void jusepuf(JusepufLinks jusepufLinks,int start,int numbers){
if(jusepufLinks.getFirstBoy()==null&&start<1&&start>numbers){
System.out.println("所输入的参数不合法请重新输入");
return;
}
//找到开始的那个元素的前一个元素
Boy startNodePreviously = jusepufLinks.getFirstBoy();
for (int i = 1; i < jusepufLinks.getBoysCount()+start-1; i++) {
startNodePreviously = startNodePreviously.getNext();
}
//开始的元素
Boy startNode = startNodePreviously.getNext();
for (int i = 1; i <jusepufLinks.getBoysCount(); i++) {
int tagNum = 2;
while(tagNum<=numbers){
startNode = startNode.getNext();
startNodePreviously = startNodePreviously.getNext();
tagNum ++;
}
System.out.printf(startNode.getNum()+"->");
startNodePreviously.setNext(startNode.getNext());
startNode = startNodePreviously.getNext();
}
System.out.printf(startNode.getNum()+"->");
System.out.println();
}
}