JAVA约瑟夫问题(丢手帕问题)

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);
		
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值