package 数据结构;
/*
*
* 约瑟夫问题:
*
* 设编号1、2、3...n的n个小孩围坐一圈,约定编号为k的人从1开始报数,数到m的人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推
* 直到所有人出列位置,由此产生一个出队的编号序列
* (first指向第一个结点)一开始的单个结点保持环形的性质——自反
* 添加一个结点:创建一个辅助指针,next指向新结点,新结点next指向起始结点,这样就保证了环形性质
* 每次操作后将辅助指针后移一个结点(这个指针用来定位“最后”的结点进行尾插法)
*
*
* 实现:
* 1.创建一个辅助指针(变量)helper,初始指向环形链表的最后一个结点
* 2.小孩报数时,将first和helper指针同时移动m-1次。
* 3.这时就可以将first指针指向的结点出圈:
* (1)first后移:first = first.next;
* (2)helper.next = first ;
* 从被删除位置的前一个直接指向后一个,则这个结点被从整个圈中被“删去”,等待被回收
*
*
* */
public class TestJosephu
{
public static void main(String[] args)
{
CircleSingleLinkedList csll = new CircleSingleLinkedList();
csll.addBoy(5);// 加入5个小孩
csll.showBoy();
csll.countBoy(1, 4, 5);
}
}
//右击Source——Generate的getter和setter可以被快速构建
//构建环形链表
class CircleSingleLinkedList
{
//创建一个还没进入序列的first指针
private Boy first = new Boy(-1);
//添加小孩结点,形成一个环形的链表
public void addBoy(int nums)
{
//数据校验
if(nums < 1)
{
System.out.println("不正确的nums值:");
return;
}
//使用for来创建环形链表
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;//开始游戏之前first不能动,创建一个同样位置的指针帮助我们向后走
}else
{
curBoy.setNext(boy);
boy.setNext(first);
curBoy = boy;
}
}
}
//遍历环形链表
public void showBoy()
{
if(first==null)
{
System.out.println("没有任何小孩");
return;
}
Boy curBoy = first;
while(true)
{
System.out.printf("小孩的编号%d\n",curBoy.getNo());
if(curBoy.getNext()==first)
{
break;
}
curBoy=curBoy.getNext();
}
}
//计算小孩出圈顺序
/**
*
* @param startNo表示从第几个小孩开始数数
* @param countNum表示数的次数
* @param nums 表示最初在圈中的小孩数目
* */
//通过先让first和helper先移动startNo-1次来控制开始位置
public void countBoy(int startNo,int countNum,int nums)
{
if(first == null||startNo<1||startNo>nums)
{
System.out.println("请重新输入值");
return;
}
Boy helper = first;
while(true)
{
if(helper.getNext()==first)
{
break;
}
helper = helper.getNext();
} //小孩报数前,first和helper移动k-1次
for(int j=0;j<startNo-1;j++)
{
first = first.getNext();
helper = helper.getNext();
}
//当小孩报数时,让first和helper指针同时移动m-1次,然后出圈
while(true)
{
if(helper == first)
{
break;
}
for(int j=0;j<countNum-1;j++)
{
first = first.getNext();
helper = helper.getNext();
}
System.out.printf("小孩%d出圈\n",first.getNo());
first =first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈中的小孩编号:%d\n",first.getNo());
}
}
/**
* @param Boy类是本次作为环形队列的结点
* @param no是孩子的编号(初始)
* @param next指向下一个结点,默认null
* 测试失败,param是会映射到下面方法的参数的,这里用在类上就不能正常显示所有文本
*
* */
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;
}
}
3————循环数组实现约瑟夫(报数出圈)问题
最新推荐文章于 2021-02-13 00:08:35 发布