前言
本篇文章撰写的初衷是为了记录本人的学习以及便于本人的再次学习,并为感兴趣的读者提供一个简单的思路参考。由于本人的能力有限,所以出现错误之处尽请见谅,如若能指出错误之处,笔者不胜感激。对于思路的分析,由于本人编写的代码比较简单,未给出详细分析,读者参照代码和部分注释即可理解。
约瑟夫环故事背景:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
问题描述:
已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
Java 数组实现代码:
import java.util.ArrayList;
public class JosephusCircleByArrayList {
private static ArrayList<Integer> rank;//用来存储约瑟夫环中淘汰出环的顺序
/**
* 构造表示约瑟夫环的数组
* @param n 约瑟夫环成员个数
* @return 表示约瑟夫环的数组
*/
public static boolean[] createJosephusCircle(int n) {
boolean[] josephusCircle = new boolean[n];//创建一个boolean数组来表示约瑟夫环,其中值true表示在环中,false表示已出环
for (int i = 0; i < josephusCircle.length; i++) {
josephusCircle[i] = true;
}
return josephusCircle;
}
/**
* 输出约瑟夫环
* @param josephusCircle 表示约瑟夫环的数组
*/
public static void printJosephusCircle(boolean[] josephusCircle) {
if (josephusCircle.length == 0) {
System.out.println("约瑟夫环为空!");
}
int head = 0;
System.out.print("约瑟夫环: ");
for (int i = 0; i < josephusCircle.length; i++) {
if (josephusCircle[i]) {
if (head == 0) {
head = i+1;
}
System.out.print((i+1) + "->");
}
}
System.out.println(head);
}
/**
* 约瑟夫环的求解方法
*
* @param josephusCircle 表示约瑟夫环的数组
* @param k
* @param m
*/
public static void solveJosephusCircle(boolean[] josephusCircle, int k, int m) {
rank = new ArrayList<Integer>();
if (josephusCircle.length == 0) {
System.out.println("约瑟夫环为空,不能进行求解!");
return ;
}
int number = josephusCircle.length;//用来表示约瑟夫环中成员剩余个数
int index = k%(number+1);//用来指向当前成员
int count = 0;//用来
while(number > 1) {//若约瑟夫环中成员剩余个数为1则跳出循环
if (josephusCircle[index-1]) {
count++;
if (count == m) {
josephusCircle[index-1] = false;
rank.add(index);
number--;//环中成员数减1
count = 0;
}
}
index++;
if (index == josephusCircle.length + 1) {
index = 1;
}
}
for (int i = 0; i < josephusCircle.length; i++) {//用来找到约瑟夫环中最后一个成员
if (josephusCircle[i]) {
rank.add(i+1);
}
}
}
public static void main(String[] args) {
int n = 41;
int k = 1;
int m = 3;
boolean[] josephusCircle = createJosephusCircle(n);
printJosephusCircle(josephusCircle);
solveJosephusCircle(josephusCircle, k, m);
System.out.println("淘汰顺序:" + rank);
}
}
Java 循环链表实现代码:
import java.util.ArrayList;
/**
* 问题:约瑟夫环问题
*
* @author 独孤猿1998
*
*/
public class JosephusCircleByLinkedList {
private static ArrayList<Integer> rank;//用来存储约瑟夫环中淘汰出环的顺序
/**
* 构造表示约瑟夫环的循环链表
* @param n 约瑟夫环成员个数
* @return 表示约瑟夫环的循环链表的头指针
*/
public static Node createJosephusCircle(int n) {
if (n < 1) {
return null;
}
Node head = new Node(1);//循环链表的头结点,指针为head
head.next = head;//初始为一个结点,此时结点的next指针指向自身
Node tail = head;//定义尾指针
for (int i = 2; i <= n; i++) {
Node node = new Node(i);
node.next = tail.next;
tail.next = node;
tail = node;
}
return head;
}
/**
* 输出约瑟夫环
* @param head 表示约瑟夫环的循环链表的头指针
*/
public static void printJosephusCircle(Node head) {
if (head == null) {
System.out.println("约瑟夫环为空!");
return ;
}
Node helper = head;
System.out.print("约瑟夫环: " + head.getNumber() + "->");
helper = helper.next;
while(helper != head) {
System.out.print(helper.getNumber() + "->");
helper = helper.next;
}
System.out.println(helper.getNumber());
}
/**
* 约瑟夫环的求解方法
*
* @param head 表示约瑟夫环的循环链表的头指针
* @param k
* @param m
*/
public static void solveJosephusCircle(Node head, int k, int m) {
rank = new ArrayList<Integer>();
if (head == null) {
System.out.println("约瑟夫环为空,不能进行求解!");
return ;
}
Node helper = head;//辅助指针
while(helper.next != head) {//使辅助指针指向尾结点
helper = helper.next;
}
for (int i = 1; i < k; i++) {//使辅助指针指向k号结点的前一个结点
helper = helper.next;
}
while (helper.next != helper) {//helper.next == helper表示循环链表只剩下一个结点,即其next指针指向自身
for (int i = 0; i < m - 1; i++) {//使helper指针指向即将删除节点的前一个结点
helper = helper.next;
}
rank.add(helper.next.getNumber());
helper.next = helper.next.next;//删除helper.next结点
}
rank.add(helper.getNumber());//将最后一个结点的序号添加进rank中
}
public static void main(String[] args) {
int n = 41;
int k = 1;
int m = 3;
Node head = createJosephusCircle(n);
printJosephusCircle(head);
solveJosephusCircle(head, k, m);
System.out.println("淘汰顺序:" + rank);
}
}
/**
* 链表结点类
*/
class Node {
private int number;
public Node next;
public Node() {}
public Node(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
代码运行环境:
jdk: java version “1.8.0_241”
eclipse:
Eclipse IDE for Java Developers
Version: 2019-12 (4.14.0)