约瑟夫问题的几种解决方法

本文详细介绍了约瑟夫问题的两种解决方法:一是通过单向环形链表实现,包括创建链表、遍历链表和出圈操作;二是利用列表进行操作,动态移除元素。通过这两种方式,可以有效地解决报数淘汰问题,直至剩下一个节点或元素。

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

        约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称"丢手绢问题".)。

编号为1.2.3…….n的n个人按顺时针方向围坐一圈,开始任意选一个整数作为报数,从第一个人开始顺时针自1开始顺序报数,报到m时停止报数。报m的人出列,从出列的下一个人开始报数,报到m的人再退出,如此下去直到剩下一个人为止。

1.第一种单向的环形链表解决

首先将链表中的节点Boy创建出来

/**
 * 创建Boy节点
 * 
 * @author Administrator
 */
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;
	}

}

单向环形链表的添加方法

// 添加小孩 构成环形链表
	public void addBoy(int nums) {
		// 添加数目小于1时,不让游戏开始
		if (nums < 1) {
			System.out.println("nums的值不正确");
		}
		// 创建curBoy辅助变量 帮助构建环形
		Boy curBoy = null;
		// 使用for创建环形链表
		// 循环创建boy 创建的是第一个就让头的下一个指向自己构成环形
		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;
		}
		// 创建curBoy辅助变量 帮助构建环形
		Boy curBoy = first;
		while (true) {
			System.out.println("输出小孩的编号" + curBoy.getNo());
			if (curBoy.getNext() == first) {
				// 说明链表遍历完了
				System.out.println("遍历完毕");
				break;
			}
			// curBoy后移
			curBoy = curBoy.getNext();
		}
	}

然后创建单向链表的出圈方法

class CircleSingleLinkedList {
	// 创建一个first节点 不赋值
	private Boy first = null;

	/**
	 * 出圈
	 * 根据用户的输入 表示小孩  一共几个  从哪个小孩数数,数几下
	 * @param nums
	 */
	public void countBoy(int nums, int firstNum, int count) {
		
		//起始位置不能大于小孩个数  链表为空也不行  开始位置不能小于1
		if (nums < firstNum ||  firstNum < 1) {
			System.out.println("参数不合理");
			return;
		}
		addBoy(nums);
		//看创建的链表是否成功
		if(first==null) {
			System.out.println("链表为空");
		}
		// 创建辅助指针 并通过循环让他指向环形链表的最后一个节点
		Boy helper = first;
		while(true) {
			if(helper.getNext()==first) {
				break;
			}
			helper=helper.getNext();
		}
		// 报数时first要移动到开始报数时的位置
		for (int i = 1; i < firstNum; i++) {
			first=first.getNext();
			helper=helper.getNext();
		}
		// 循环遍历
		while (true) {
			//假如辅助变量是first  说明只剩下一个节点
			if (helper == first) {
				System.out.println("说明圈中只有一个节点");
				break;
			}
			//开始数数  for循环结束 first的位置就是要删除的节点的位置
			for(int j = 1;j<count;j++) {
				//让first后移  helper也后移
				first=first.getNext();
				helper=helper.getNext();
			}
			//让first后移  然后helper指向 first
			System.out.println("出圈的是"+first.getNo());
			first=first.getNext();
			helper.setNext(first);
		}
		System.out.println("最后一个是"+helper.getNo());
		
	}

2.第二种列表解决约瑟夫问题的思路

	//big是一共有多少个孩子  order是从哪个孩子开始  nums是隔几个开始
	public static void orderByInput(int big,int order,int nums){

		
		//创建了一个数组
		ArrayList<Integer> list = new ArrayList<>();
		//将每个学生加入到数组中
		for(int i =1;i<=big;i++){
			System.out.println("加入"+i+"个周硕");
			list.add(i);
		}
		//当列表中的数只有一个的时候将他打印出来
		while(true){
			if(list.size()==1){
				System.out.println("只剩下"+list.get(0)+"个周硕");
				break;
			}
			//求出要取出的孩子 从哪个孩子开始加上隔的数 就是下一个要去除的孩子
			//加上列表的元素数量是怕order+nums-1是负数  取模是为了数值超出列表长度
			Integer kid = list.get((order+nums-1+list.size())%list.size());
			//输出要移除的这个孩子的编号
			System.out.println("移除"+kid.intValue()+"个周硕");
			//移除这个孩子
			list.remove(kid);
			//让order后移
			order = (order+1+list.size())%list.size();
		}
	}
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("游戏规则 一共n个小孩 从第m个小孩开始  每次数到 k 就移除这个人 最后只剩下一个人");
		System.out.println("需要多少个人玩");
		int i1 = scanner.nextInt();
		System.out.println("第几个开始 最后剩下谁");
		int i12 = scanner.nextInt();
		System.out.println("最后剩下谁");
		int i13 = scanner.nextInt();
		
		orderByInput(i1,i12,i13);
	}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LeeGaKi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值