@lyc1234
用Java单向循环链表解决约瑟夫问题
约瑟夫问题:设有n个人围坐在一张圆桌周围,先从某个人开始从1报数,数到m的人出列(即离开座位,不参与以后的报数),然后从出列的下一个人开始重新从1报数,数到m的人又出列,如此下去直到所有人都出列为止,试求出它们的出列顺序。
例如:当n=8,m=4时,若从第一个人(每个人的编号依次为1,2,…,n)开始报数,则得到的出列次序为:4,8,5,2,1,3,7,6;
此算法要求以n,m和s(从第s个人开始第1次报数)作为值参
思路
- 用java先构建一个单向循环链表
- java指针类,链表类
- 编写插入算法
- 编写出列算法:每次向后移动相同次数的指针,然后将指定位置的id输出,并删除该位置,继续下一次的移动。
代码实现
1.指针类
/*
通过每一个link内的next指向下一个link,从而达到构建指针的目的,方便使用和移动
*/
public class Link {
public int iData; //id
public double dData; //数据
public double item;//用来返回值(如果函数要返回值,由整个公共变量带回)
public Link next;/*指向下一个的节点,对另一个对象的引用,
并不是包括了这个对象。编译器会自动初始化null值,不需要自己再重新实例化一个对象*/
public Link(int id,double dd)//有参构造器 {
{
iData=id;
dData=dd;
}
public void displayLink() {
System.out.println("{"+iData+","+dData+"}");
}
}
2.单向循环链表类
/*构建一个头节点,尾节点,并采用尾部插入的方式达到循环的目的
*/
public class LinkList2 {
private Link first;//头节点
private Link tail;//尾节点
Link temp1=null;//这个是用来改变函数使用的指针节点位置,将形参带回实参
public int count=0;//记录链表的长度
public double item;//用来返回值
LinkList2(){//无参构造器
first=tail=null;
count=0;//初始化表,将元素的个数清零
}
public void InsertList(Link k) {//在尾部传入节点
if (count==0) {//如果是空表,那么头尾节点相等。
tail=first=k;
}
else {
tail.next=k;//将尾部节点连接到k
k.next=first;//将k连接到头部节点
tail=k;//然后将尾部节点更新为k
}
count++;//元素的个数++
}
public int exitList(Link current,int i) {//出列算法,值参i为current后i位元素出列
int k=1;//因为是输出current下一个节点的id,所以计数变量k要从1开始。
while(k<i) {
current=current.next;//移动i-1个位置的指针,然后输出下一个指针的id
k++;
}
int temp=current.next.iData;
if(count!=1){//如果不是头部,就删除下一个节点
current.next=current.next.next;//直接将当前的next指针连到下下个指针
current=current.next;//移动节点
}
else {
current=first=tail=null;//如果是头部,就将头部节点和尾部节点清空
}
count--;//元素个数
temp1=current;//将当前的指针传出去,不然不能改变指针所在位置
return temp;//将值传出去
}
public void displaylist() {//遍历算法,用来检查单向循环链表是否创建成功
System.out.println("first --> last");
Link current=first;//先将current设置为第一个节点
while(current!=tail) {
current.displayLink();//打印当前节点
current=current.next;//将下一个指向赋给当前节点
}
current.displayLink();
System.out.println("");
}
public boolean isEmpty() {//判断是否为空算法
return(count==0);
}
public void josephus(int n,int m, int s) {//约瑟夫算法
for(int i=1;i<=n;i++) {
this.InsertList(new Link(i,0.0));//初始化链表
}//因为编号是1……n,所以从1开始,创建一个连接对象后传入链表
Link current=first;//将指针设置为头部节点
for(int i=0;i<s-1;i++) {//因为从第s个人开始,先移动s-1次到达要报数的第一个人处
current=current.next;
}
while(!this.isEmpty()) {//如果表为空,就退出
int i;//输出变量
i=this.exitList(current, m-1);//调用出列算法,因为是算自己当前对象,所以每次移动m-1次就好
current=temp1;
System.out.println(i);
}
}
}
3.主程序类
public static void main(String[] args) {
LinkList2 l2=new LinkList2();
l2.josephus(8, 4, 1);
}
其实为什么要使用m-1次,我也没想明白……