3————循环数组实现约瑟夫(报数出圈)问题

package 数据结构;
/*
 * 
 * 约瑟夫问题:
 * 
 * 设编号1、2、3...n的n个小孩围坐一圈,约定编号为k的人从1开始报数,数到m的人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推
 * 直到所有人出列位置,由此产生一个出队的编号序列
 * (first指向第一个结点)一开始的单个结点保持环形的性质——自反
 * 添加一个结点:创建一个辅助指针,next指向新结点,新结点next指向起始结点,这样就保证了环形性质
 * 每次操作后将辅助指针后移一个结点(这个指针用来定位“最后”的结点进行尾插法)
 * 
 * 
 * 实现:
 *  1.创建一个辅助指针(变量)helper,初始指向环形链表的最后一个结点
 *  2.小孩报数时,将first和helper指针同时移动m-1次。
 *  3.这时就可以将first指针指向的结点出圈:
 *  	(1)first后移:first = first.next;
 * 		(2)helper.next = first ;
 * 			从被删除位置的前一个直接指向后一个,则这个结点被从整个圈中被“删去”,等待被回收
 * 
 *
 * */
public class TestJosephu
{
	public static void main(String[] args) 
	{
		CircleSingleLinkedList csll = new CircleSingleLinkedList();
		csll.addBoy(5);// 加入5个小孩
		csll.showBoy();
		csll.countBoy(1, 4, 5);
	}
}
//右击Source——Generate的getter和setter可以被快速构建



//构建环形链表

class CircleSingleLinkedList
{
	//创建一个还没进入序列的first指针
	private Boy first = new Boy(-1);
	//添加小孩结点,形成一个环形的链表
	public void addBoy(int nums) 
	{
		//数据校验
		if(nums < 1) 
		{
			System.out.println("不正确的nums值:");
			return;
		}
		//使用for来创建环形链表
		Boy curBoy = null;//辅助指针
		for(int i=1;i<=nums;i++) 
		{
			//构建小孩结点
			Boy boy = new Boy(i);
			if(i==1)
			{
				first = boy;
				first.setNext(first);
				curBoy = first;//开始游戏之前first不能动,创建一个同样位置的指针帮助我们向后走
				
			}else
			{
				curBoy.setNext(boy);
				boy.setNext(first);
				curBoy = boy;
			}
		}
	}
	
	//遍历环形链表
	public void showBoy() 
	{
		if(first==null)
		{
				System.out.println("没有任何小孩");
				return;
	
		}
		Boy curBoy = first;
		while(true) 
		{
			System.out.printf("小孩的编号%d\n",curBoy.getNo());
			if(curBoy.getNext()==first)
			{
				break;
			}
			curBoy=curBoy.getNext();
		}
	}
	//计算小孩出圈顺序
	/**
	 * 
	 * @param startNo表示从第几个小孩开始数数
 	 * @param countNum表示数的次数
 	 * @param nums 表示最初在圈中的小孩数目
	 * */
	//通过先让first和helper先移动startNo-1次来控制开始位置
	public void countBoy(int startNo,int countNum,int nums) 
	{
		if(first == null||startNo<1||startNo>nums) 
		{
			System.out.println("请重新输入值");
			return;
		}
		Boy helper = first;
		while(true) 
		{
			if(helper.getNext()==first)
			{
				break;
			}
			helper = helper.getNext();
		}	//小孩报数前,first和helper移动k-1次
			for(int j=0;j<startNo-1;j++) 
			{
				first = first.getNext();
				helper = helper.getNext();
			}
			//当小孩报数时,让first和helper指针同时移动m-1次,然后出圈
			while(true) 
			{
				if(helper == first)
				{
					break;
				}
				for(int j=0;j<countNum-1;j++)
				{

					first = first.getNext();
					helper = helper.getNext();
				}
				System.out.printf("小孩%d出圈\n",first.getNo());
				first =first.getNext();
				helper.setNext(first);
			}
			System.out.printf("最后留在圈中的小孩编号:%d\n",first.getNo());
	}
}


/**
 * @param Boy类是本次作为环形队列的结点
 * @param no是孩子的编号(初始)
 * @param next指向下一个结点,默认null	
 * 测试失败,param是会映射到下面方法的参数的,这里用在类上就不能正常显示所有文本
 * 
 * */

class Boy
{
	private int no;
	private Boy next;
	public Boy(int no)
	{
		this.no=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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值