Josephu问题:有n个小孩围成一圈,从第k个小孩开始从1报数,其中报数为m的小孩离开队伍,然后在从该位置开始报数,报道为m的再次离开队伍,然后再次重复次过程, 求离开队伍的小孩的编号序列。
使用环形链表类比这类问题的实际情况,再依次分析出循环条件,重点在于增加辅助指针helper来指向first之前的一个元素,以此来简化代码,最后代码实现。
代码如下:
package linkedlist;
/**
* 此类使用环形链表解决约瑟夫问题
* Josephu问题:有n个小孩围成一圈,从第k个小孩开始从1报数,其中报数为m的小孩离开队伍,
* 然后在从该位置开始报数,报道为m的再次离开队伍,然后再次重复次过程, 求离开队伍的小孩的编号序列。
* @author Administrator
*
*/
public class JosePhu {
public static void main(String[] args) {
//测试
CircelSingleLinkedList cs = new CircelSingleLinkedList();
cs.add(125);
cs.showNodes();
//约瑟夫问题解决测试
cs.countNums(10, 20, 125);
}
}
class CircelSingleLinkedList{
//设置第一个节点
private Boy first = null;
public Boy getFirst() {
return first;
}
//批量添加元素的方法
public void add(int nums) {
//对nums进行校验
if(nums < 2) {
System.out.println("输入的num值错误,请重新输入!");
}
Boy temp = null;
//使用for循环创建链表
for(int i = 1; i <= nums; i++) {
Boy boy = new Boy(i);
if(i == 1) {
first = boy;
first.setNext(first);
temp = first;
}else {
temp.setNext(boy);
boy.setNext(first);
//后移指针
temp = boy;
}
}
}
//遍历环形链表的方法
public void showNodes() {
if(first == null) {
System.out.println("链表为空!");
}
Boy temp = first;
while(true) {
//输出当前值
System.out.printf("小男孩的编号为: %d \n" , temp.getNo());
//指针后移
temp = temp.getNext();
//判断结束循环的条件
if(temp == first) {
break;
}
}
}
//解决约瑟夫问题的方法
public void countNums(int startNum, int countNum, int totalNum) {
//判断数据错误的情况
if(first == null || startNum < 1 || countNum > totalNum) {
System.out.println("链表为空,或输入的数据有错误,请重新输入!");
return;
}
//辅助变量helper 指向first的前一个元素
Boy helper = first;
//移动helper
while(true) {
//移动helper
helper = helper.getNext();
//判断helper的位置,跳出循环
if(helper.getNext() == first) {
break;
}
}
//同时移动helper和first指针到startNum - 1 的位置,根据题意,报数应该从这里开始
for(int i = 0; i < startNum - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//开始从队伍中往外筛出符合条件的人
while(true) {
//当环形列表中只剩下一个人时helper与first应该相等
if(helper == first) {
break;
}
//first与helper应同时向后移动countNum - 1 个位置,此时first所指的元素即为要排出链表的元素
for(int j = 0; j < countNum -1; j++) {
first = first.getNext();
helper = helper.getNext();
}
//输出该元素
System.out.printf("儿童%d 被排除出队伍 \n" , first.getNo());
//将first指针移向下一个元素,将当前元素排除出链表
first = first.getNext();
helper.setNext(first);
}
//此时输出链表中的最后一个元素
System.out.printf("链表中的最后一个元素为: %d" , first.getNo());
}
}
class Boy{
private int no; //儿童编号
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
private Boy next; //指针指向下一个儿童
public Boy(int no) {
this.no = no;
}
@Override
public String toString() {
return "Boy [no=" + no + "]";
}
}