package com.eric;
/**
* 功能 : 约瑟夫问题(丢手帕问题)
* N个人围成一圈,才1数 数m个后,该人出列,重下一个继续数,数m个出列,依次循环,知道剩下最后一个人。求该人是的编号~~
* 逻辑不复杂,看了一遍基本就掌握了。
* 有时候玩玩算法,还是挺有趣的~
*
* @author eric<imphp[at]qq[dot]com>
* @date 2014-07-15
*/
public class Demo4 {
public static void main(String[] args)
{
CycLink cyc = new CycLink();
cyc.setLen(5);
cyc.createLink();
// cyc.showLink();
cyc.setStart(2);
cyc.setM(2);
cyc.play();
}
}
// 孩子
class Child {
// 每个孩子的编号
int no;
// 下一个孩子
Child nextChild;
// 构造函数,创建每个孩子的时候给上编号
public Child(int no){
this.no = no;
}
}
// 创建环形链表
class CycLink {
// 指向第一个小孩的引用,不能动
Child firstChild;
// 游标
Child cursor;
// 定义一个链表的大小 (共有多少个小孩)
int len;
// 定义一个起始编号
int start;
// 定义长度
int m;
public void setLen(int len)
{
this.len = len;
}
public void setStart(int start)
{
this.start = start;
}
public void setM(int m){
this.m = m;
}
// 开始玩游戏
public void play()
{
// 游标小孩
Child tmp = this.firstChild;
// 定义要出列的小孩的上一个小孩
Child tmp2 = null;
// 1.找到开始数数的人, 通过数数的人的上一个小孩,找到数数的人
for (int i=1; i<this.start; i++) {
// tmp就是数数的人
tmp = tmp.nextChild;
}
// System.out.println("数数的小孩为:"+tmp.no);
// System.out.println("参与游戏的小朋友总数:"+this.len);
// 当圈内孩子大于1个的时候,就一直游戏下去
while (this.len != 1) {
// 2.数m下,从start小孩数到m下后的那个小孩,也就是要出圈的小孩
for (int j = 1; j<this.m; j++) {
// 出圈小孩的上一个小孩
tmp2 = tmp;
// 要出圈的小孩
tmp = tmp.nextChild;
}
// System.out.println("被退出的小孩编号为:"+tmp.no);
// 找到要出圈小孩的前一个小孩 可以优化写在第二步
// Child tmp2 = tmp;
// 相当于又遍历了一遍,找到 出圈小孩的上一个小孩
// while (tmp != tmp2.nextChild) {
// tmp2 = tmp2.nextChild;
// }
// System.out.println("出圈小孩的上一个小孩"+tmp2.no);
// 4.将出圈小孩退出游戏圈 , 就是将退出圈的小孩的上一个小孩的小一个孩子指向退出圈小孩的下一个小孩即可
tmp2.nextChild = tmp.nextChild;
tmp = tmp.nextChild;
this.len--;
}
System.out.println("最后的小孩编号为:"+tmp.no);
}
// 创建一个环形链接
public void createLink()
{
for (int i=1; i<=this.len; i++) {
Child ch = new Child(i);
// 第一个孩子
if (i == 1) {
this.firstChild = ch;
this.cursor = ch;
// 最后一个孩子
} else if (i == this.len) {
this.cursor.nextChild = ch;
this.cursor = ch;
this.cursor.nextChild = this.firstChild;
// 其他孩子
} else {
this.cursor.nextChild = ch;
this.cursor = ch;
}
}
/*
* 逻辑思路比较清晰的写法
*
for (int i=1; i<=this.len; i++) {
// 如果是第一个小孩
if (i == 1) {
// 创建一个孩子,赋上编号
Child ch = new Child(i);
this.firstChild = ch;
// 指针指向该对象
this.cursor = ch;
} else {
// 如果是最后一个孩子
if (i == this.len) {
Child ch = new Child(i);
// 上一个孩子的下一个孩子是最后一个孩子啦
this.cursor.nextChild = ch;
// 设置游标为最后一个孩子
this.cursor = ch;
// 当为最后一个孩子的时候,那他的下一个孩子自然就是第一个孩子啦
this.cursor.nextChild = this.firstChild;
} else {
// 中间的孩子
Child ch = new Child(i);
this.cursor.nextChild = ch;
this.cursor = ch;
}
}
}*/
}
// 打印环形链表
public void showLink()
{
// 创建游标
Child cursor = this.firstChild;
// do while 先执行一次,故将第一个firstChild pass过去了,再次遇到,则为回到了起始
do {
// 打印每一个孩子
System.out.println("编号:"+cursor.no);
// 打印完,需要将对象指向到该孩子的下一个孩子上
cursor = cursor.nextChild;
// while循环知道 cursor 再次等于 firstChild终止
} while(cursor != this.firstChild);
}
}
JAVA约瑟夫问题(丢手帕问题)
最新推荐文章于 2021-02-26 12:25:10 发布