Java-约瑟夫问题

本文详细介绍了约瑟夫环问题,通过创建单向环形链表来模拟问题情境。首先分析问题,然后创建链表,接着讲解如何模拟小孩报数出圈的过程,并给出具体代码实现,最后展示出圈顺序。该问题的Java解决方案清晰易懂,适合进阶学习者掌握链表操作和逻辑思考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

Josephu、单向环形链表
假设你已经会Java基础、链表

一、Josephu问题是什么?

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

二、思路

1.分析问题

  1. n个人围坐成一圈:可以通过创建一个不带头结点、n个节点的单向环形链表(单向环形链表:尾节点的next指向头节点 )
  2. 约定编号为k的人从1开始报数: 头结点到k位置时开始
  3. 数到m出列: 头结点移动到m时移除节点

2.创建链表思路

  1. 构建单向环形链表:当创建第一个节点head时,要让head的next指向自己,形成环形(一个节点也是环形)
  2. 创建节点
    代码如下(示例):
	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;
		}
		
	}
  1. 加入节点时,head节点的next指向新节点,并让新结点的next指向head
    代码如下(示例):
	// 借助辅助指针 当前最后节点
	Boy curBoy = null;
	// 要nums个节点
	for (int i = 1; i <= nums; i++) {
		Boy boy = new Boy(i);
		if (i == 1){
			head = boy;
			head.setNext(first);
			curBoy = head;
		}else{
			curBoy.setNext(boy);
			boy.setNext(head);
			curBoy = curBoy.getNext();
		}
	}
  1. 遍历时,当尾结点的next == head时,说明遍历完成
    代码如下(示例):
	if (head == null){
		System.out.println("空");
		return;
	}
	Boy curBoy = first;
	while(true){
		System.out.println(curBoy.getNo());
		if (curBoy.getNext() == head){
			break;
		}
		curBoy = curBoy.getNext();
	}

3.小孩出圈思路(Josepfu)

  1. 假设用户输入,生成一个出圈顺序:
    • n = 5,即有5个小孩
    • k = 1,从1开始报数
    • m = 2,报到2出列
    • 出列顺序为 2->4->1->5->3
  2. 创建一个helperBoy指向尾节点,同时我们有head节点指向头节点,在开始之前,要把头尾指针移动 k-1次
  3. 当小孩报数时,要让helperBoy、head移动 m-1次
  4. 这是把first指向的节点出列
    代码如下(示例):
	head = head.next
	helperBoy.next   = head
  1. 没有引用的节点 就会被gc回收

4.代码实现

代码如下(示例):

	/** 
	 * 
	 * @param startNo 第几个小孩开始数
	 * @param countNum 数几下
	 * @param nums 最初有多少小孩在圈中
	 */
	public void countBoy(int startNo, int countNum, int nums){
		if (startNo < 1 || countNum < 1 || startNo > nums){
			System.out.println("error");
			return;
		}
		// 先移动 头节点
		for (int i = 0; i < startNo - 1; i++) {
			first = first.getNext();
		}
		// 尾节点
		Boy helperBoy = first;
		while (true) {
			if (helperBoy.getNext() == first){
				break;
			}
			helperBoy = helperBoy.getNext();
		}
		while(true){
			// 圈中只有一个节点 退出循环
			if (helperBoy == first){
				break;
			}
			for (int i = 0; i < countNum - 1; i++) {
				first = first.getNext();
				helperBoy = helperBoy.getNext();
			}
			System.out.println("出圈" + first.getNo());
			first = first.getNext();
			helperBoy.setNext(first);
		}
		System.out.println("最后的小孩" + first.getNo());
	}
}

总结

以上就是今天要讲的内容,本文仅仅简单介绍了约瑟夫问题的一种解法,感谢观看,如有错误感谢指正。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值