Josephu(约瑟夫、约瑟夫环) 问题

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

提示:用一个不带头结点的循环链表来处理Josephu 问题:
先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时
,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,
直到最后一个结点从链表中删除算法结束。

代码实现
package com.atguigu.linkedList;

public class Josepfu {
public static void main(String[] args) {
//测试遍历和创建方法
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.showBoy();

	//测试出圈
	circleSingleLinkedList.countBoy(1, 2, 5);

}

}

//创建一个环形的单项链表
class CircleSingleLinkedList{
//创建一个first结点,当前没有编号
private Boy first=null;
//加入结点,构建成一个环形链表
public void addBoy(int nums) {
//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;
	}
	//因为first不能动,因此我们任然使用一个
	//辅助指针来完成遍历
	Boy curBoy=first;
	while(true) {
		System.out.printf("小孩的编号为:%d\n",curBoy.getNo());
		if(curBoy.getNext()==first) {
			break;//说明已经遍历完毕了
		}
		curBoy=curBoy.getNext();
	}
}
//根据用户的输入,计算出出圈的顺序
/*startNo:表示从第几个小孩开始数数
 * countNum表示数几下
 * nums 表示最初有多少个小孩在圈中
 * 
 * */
public void countBoy(int startNo,int countNum,int nums){
	//先对数据进行校验
	if(first==null||startNo<1||startNo>nums) {
		System.out.println("参数输入有问题,请重新输入");
		return;
	}
	//创建辅助变量,帮助小孩出圈
	Boy helper=first;
	//helper,事先应该指向环形链表最后的节点
	while(true) {
		if(helper.getNext()==first) {
			break;
		}
		helper=helper.getNext();	
	}
	//小孩报数前,先让first和helper移动startNo-1次(也就是移动到报数的那个小孩这里)
	for(int j=0;j<startNo-1;j++) {
		helper=helper.getNext();
		first=first.getNext();
	}
	//当小孩报数时,先让first,helper移动countNum-1次
	//然后准备出圈,这里是一个反复的过程,直到圈中只有一个人
	while(true) {
		if(helper==first) {
			break;
		}
		for(int j=0;j<countNum-1;j++) {
			helper=helper.getNext();
			first=first.getNext();
		}
		//这个first所指向的结点,就是需要出圈的结点
		System.out.printf("小孩%d出圈\n",first.getNo());
		first=first.getNext();
		helper.setNext(first);
		
	}
	System.out.printf("圈子中最后剩下的小孩的编号是%d\n",first.getNo());
}

}

//创建一个Boy类表示一个结点
class Boy{
private int no;//编号
private Boy next;//指向下一个结点
public Boy() {}
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;
}

}
运行结果
在这里插入图片描述
在这里插入图片描述用环形链表的思想求解约瑟夫问题比较直观,容易理解,希望这篇文章对大家有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值