JAVA代码实现约瑟夫问题

约瑟夫问题(单向环形链表)

今天学习了一下数据结构中的约瑟夫问题。谈了一下自己的解决思路,如果有错误的地方,还望指出,我一定及时改正。

问题描述:有n个小孩围坐成一个圈玩游戏,每个小孩按1,2…n进行编号; 从 第k个小孩开始进行报数,数到第m个的小孩出列,然后从他的下一位继续数m个小孩,直到所有人出列,求出列的顺序。

package com.LinkedList.DoubleLinkedList;

/*
 * 约瑟夫问题:
 *     描述:有n个小孩围坐成一个圈玩游戏,每个小孩按1,2...n进行编号;
 *     游戏规则:
 *         从 第k个小孩开始进行报数,数到第m个的小孩出列,然后从他的下一位继续数m个小孩,直到所有人出列,求出列的顺序
 */
public class Josepfu {

	public static void main(String[] args) {
		// 创建单向环形单链表
		CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
		// 创建5个小男孩
		circleSingleLinkedList.addBoy(5);
		circleSingleLinkedList.countBoy();

		// 出圈
		circleSingleLinkedList.removeBoy(1, 2, 5);

	}

}

//定义一个单向的环形链表
class CircleSingleLinkedList {
	// 定义一个first,用来记录链表的开头
	private Boy first = new Boy(0);

	/**
	 * 出圈
	 * 
	 * @param start 从哪里开始报数
	 * @param count 每次数几个数
	 * @param nums  小男孩的个数(环形链表的长度)
	 */
	public void removeBoy(int start, int count, int nums) {
		// 链表为空、开始位置小于0,开始位置大于总人数都不可以
		if (start < 0 || start > nums || first == null) {
			System.out.println("请输入正确的参数!");
			return;
		}
		// 思路:
		// 1.单向环形链表要想删除某个节点,必须要找到该节点的前一个节点;
		// 2.first标记的是链表的开头,那么指针一开始要在first的前面一个节点,就是在链表的最后一个节点
		// 3.从第start个位置开始报数,所以要将first的位置向前挪start-1次,指针的开始位置也应该跟着挪start-1次
		// 4.每次报数count个,就是每次指针向前移动count-1个的位置就删除,
		// 5.从删除位置的下一个节点重新报数,意味着下一次报数的开始位置也向前移动了count-1个,即first也要向前移动count-1
		// 6.最后剩最后一个的时候,就结束了
		Boy temp = first;// 定义一个指针
		// 1.现先将指针移动到最后一个节点上
		while (true) {
			if (temp.getNext() == first) {
				break;
			}
			temp = temp.getNext();
		}
		// 2.将指针temp和first一起向前挪start-1次
		for (int i = 0; i < start - 1; i++) {
			first = first.getNext();
			temp = temp.getNext();
		}
		// 3.现在开始报数
		while (true) {
			if (temp == first) {// 说明就剩最后一个小男孩了
				break;
			}
			// 4.每次报count个,也就是将指针temp和first同时挪count-1次
			for (int i = 0; i < count - 1; i++) {
				first = first.getNext();
				temp = temp.getNext();
			}
			// 4.此时,first位置上的小男孩出圈,先记录下来
			System.out.printf("编号为%d的小男孩出圈\n", first.getNo());
			// 5.开始出圈
			first = first.getNext();// 新的报数位置是出圈男孩的下一个
			temp.setNext(first);// 将指针位置的节点的后指针指向新的起始位置,完成出圈
		}
		// 最后一个小男孩,记录下来
		System.out.printf("最后留在圈中的小孩编号为%d\n", first.getNo());
	}

	/**
	 * 建立一个有nums个男孩的单向环形链表
	 * 
	 * @param nums
	 */
	public void addBoy(int nums) {
		if (nums < 1) {
			System.out.println("请输入正确的参数!");
			return;
		}
		// first用来标记第一个小男孩,不能移动,所以需要定义一个指针定位每次添加进来的男孩
		Boy temp = null;
		for (int i = 1; i <= nums; i++) {// 创建nums个小男孩
			Boy boy = new Boy(i);
			// 第一个小男孩创建后,需要将boy的后指针指向自己,自己形成环
			if (i == 1) {
				first = boy;
				first.setNext(first);// 将自己形成环
				temp = first;// 指针指向第一个小男孩
			} else {
				temp.setNext(boy);// 将当前小男孩的后指针指向新创建的小男孩
				boy.setNext(first);// 将新创建的小男孩的后指针指向第一个小男孩
				temp = boy;// 指针移动到新创建的小男孩身上
			}
		}
	}

	/**
	 * 遍历单向环形链表
	 */
	public void countBoy() {
		if (first == null) {
			System.out.println("链表为空,无法遍历!");
			return;
		}
		Boy temp = first;// 指针
		while (true) {
			System.out.printf("编号为%d的小男孩\n", temp.getNo());
			if (temp.getNext() == first) {// 遍历完毕
				break;
			}
			temp = temp.getNext();
		}
	}

}

//定义一个小男孩模拟节点
class Boy {
	private int no;// 编号
	private Boy next;// 指向下一个男孩

	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 Boy(int no) {
		this.no = no;
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值