数据结构和算法练习---环形链表与Josephu问题的解决

Josephu问题:有n个小孩围成一圈,从第k个小孩开始从1报数,其中报数为m的小孩离开队伍,然后在从该位置开始报数,报道为m的再次离开队伍,然后再次重复次过程, 求离开队伍的小孩的编号序列。

使用环形链表类比这类问题的实际情况,再依次分析出循环条件,重点在于增加辅助指针helper来指向first之前的一个元素,以此来简化代码,最后代码实现。

代码如下:

package linkedlist;
/**
 * 此类使用环形链表解决约瑟夫问题
 * Josephu问题:有n个小孩围成一圈,从第k个小孩开始从1报数,其中报数为m的小孩离开队伍,
 * 			      然后在从该位置开始报数,报道为m的再次离开队伍,然后再次重复次过程,   求离开队伍的小孩的编号序列。
 * @author Administrator
 *
 */
public class JosePhu {
	
	public static void main(String[] args) {
		//测试
		CircelSingleLinkedList cs = new CircelSingleLinkedList();
		cs.add(125);
		cs.showNodes();
		//约瑟夫问题解决测试
		cs.countNums(10, 20, 125);
	}
}

class CircelSingleLinkedList{
	
	//设置第一个节点
	private Boy first = null;
	
	public Boy getFirst() {
		return first;
	}

	//批量添加元素的方法
	public void add(int nums) {
		//对nums进行校验
		if(nums < 2) {
			System.out.println("输入的num值错误,请重新输入!");
		}
		Boy temp = null;
		//使用for循环创建链表
		for(int i = 1; i <= nums; i++) {
			Boy boy = new Boy(i);
			if(i == 1) {
				first = boy;
				first.setNext(first);
				temp = first;
			}else {
				temp.setNext(boy);
				boy.setNext(first);
				//后移指针
				temp = boy;
			}
		}
	}
	
	//遍历环形链表的方法
	public void showNodes() {
		if(first == null) {
			System.out.println("链表为空!");
		}
		Boy temp = first;
		while(true) {
			//输出当前值
			System.out.printf("小男孩的编号为: %d  \n" , temp.getNo());
			//指针后移
			temp = temp.getNext();
			//判断结束循环的条件
			if(temp == first) {
				break;
			}
		}
	}
	
	//解决约瑟夫问题的方法
	public void countNums(int startNum, int countNum, int totalNum) {
		//判断数据错误的情况
		if(first == null || startNum < 1 || countNum > totalNum) {
			System.out.println("链表为空,或输入的数据有错误,请重新输入!");
			return;
		}
		//辅助变量helper  指向first的前一个元素
		Boy helper = first;
		//移动helper
		while(true) {
			//移动helper
			helper = helper.getNext();
			//判断helper的位置,跳出循环
			if(helper.getNext() == first) {
				break;
			}
		}
		//同时移动helper和first指针到startNum - 1 的位置,根据题意,报数应该从这里开始
		for(int i = 0; i < startNum - 1; i++) {
			first = first.getNext();
			helper = helper.getNext();
		}
		//开始从队伍中往外筛出符合条件的人
		while(true) {
			//当环形列表中只剩下一个人时helper与first应该相等
			if(helper == first) {
				break;
			}
			//first与helper应同时向后移动countNum - 1 个位置,此时first所指的元素即为要排出链表的元素
			for(int j = 0; j < countNum -1; j++) {
				first = first.getNext();
				helper = helper.getNext();
			}
			//输出该元素
			System.out.printf("儿童%d 被排除出队伍 \n" , first.getNo());
			//将first指针移向下一个元素,将当前元素排除出链表
			first = first.getNext();
			helper.setNext(first);
		}
		//此时输出链表中的最后一个元素
		System.out.printf("链表中的最后一个元素为: %d" , first.getNo());
		
	}
}


class Boy{
	
	private int 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;
	}

	private Boy next;	//指针指向下一个儿童
	
	public Boy(int no) {
		this.no = no;
	}

	@Override
	public String toString() {
		return "Boy [no=" + no + "]";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值