约瑟夫问题

Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此 产生一个出队编号的序列。

解题思路
一、先创建一个循环链表
1.先创建first节点,然他先自己成“环”,即first.next = first;
2.然后开始往后面追加,这时候需要一个辅助变量temp,一开始temp=first。
temp.next = newNode;
newNode.next = first;
temp = newNode;
二、开始根据用户输入,确定出圈顺序
1.首先,要定义一个辅助变量helper,helper指向循环链表最后一个节点。这一步用while循环就可以了
2.先让first移动到要开始数数人的位置,当然helper也跟着移动。
比方说下面的例题就是:小孩报数前,先让first和helper移动k-1次(for循环就可实现)
3.开始数数喽,如果数到m就出列,那么first和helper就同时需要移动m-1次
,然后这时候就可以将first指向小孩节点出圈(first节点就是要出圈的小孩),核心代码:
输出小孩编号,然后
first=first,next;
helper.next = first;
4.最后会剩下一个,判断条件
helper = first;
打印输出最后一个即可。

package com.gk.linkedlist;
/**
 * 约瑟夫问题:
 * Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,
 * 数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,
 * 由此 产生一个出队编号的序列。
 * 
 * 根据用户的输入,生成一个小孩的出圈顺序
 * n=5,即有5个人
 * k=1,从第一个人开始报数
 * m=2,数2下
 * 
 * 1.需求创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
 * 2.小孩报数前,先让first和helper移动k-1次
 * 3.当小孩报数时,让first和helper指针同时的移动m-1次
 * 4.这时候就可以将first指向小孩节点出圈
 * 	first = first.next;
 * 	helper.next = first;
 * 原来first指向的节点就没有任何引用,就会被回收
 * 
 * @author gk
 *
 */
public class Josepfu {
	
	public static void main(String[] args) {
		
		CircleSingleLinkedList cs = new CircleSingleLinkedList();
		cs.add(5);
		cs.showBoy();
		cs.countBoy(1, 2, 5);//出圈顺序2-4-1-5-3
		
	}

}

//创建一个循环链表
class CircleSingleLinkedList{
	//创建一个first节点,当前没有编号
	private Boy first = null;
	
	//添加节点,构成一个单向循环链表
	public void add(int nums) {
		//先做数据校验
		if(nums < 1) {
			System.out.println("nums的值不合法!");
			return;
		}
		
		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;
			} 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();//curBoy后移
		}
	}
	
	/**
	 * 根据用户的输入,计算出小孩的出圈顺序
	 * 
	 * @param startNo 表示从第几个小孩开始数数
	 * @param countNum 表示数几下
	 * @param nums	表示最初由几个小孩
	 */
	public void countBoy(int startNo, int countNum, int nums) {
		//先对数据进行校验
		if(first == null || startNo < 1 || startNo > nums) {
			System.out.println("输入的参数有误,请重新开始");
			return;
		}
		
		//创建要给辅助指针,帮助完成小孩出圈
		Boy helper = first;
		//1.需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后一个节点
		while(true) {
			if(helper.getNext() == first) {//说明helper已经指向最后一个节点了
				break;
			}
			helper = helper.getNext();
		}
		
		//2.小孩报数前先让first和helper同时移动startNo-1次 (因为要从startNo开始报数)
		for(int i = 1; i <= startNo-1; i++) {
			first = first.getNext();
			helper = helper.getNext();
		}
		
		//3.游戏开始,小孩开始报数时,first和helper同时移动countNum-1次
		//这是一个循环操作,直到圈中只有一个小孩
		while(true) {
			if(helper == first) {
				break;
			}
			for(int j = 1; j <= countNum-1; j++) {
				first = first.getNext();
				helper = helper.getNext();
			}
			
			//4.数出一个人了,开始出圈
			System.out.printf("%d号孩子开始出圈\n",first.getNo());
			first = first.getNext();
			helper.setNext(first);
		}
		System.out.printf("最后出圈的小孩为%d",first.getNo());
	}	
}

//创建一个Boy类,表示一个节点
class Boy{
	
	private int no;//编号
	private Boy next;//指向下一个节点,默认null
	
	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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值