约瑟夫(Joeph)环,刚一听觉得挺难,但其实就是一个链表,并不是多难。具体的要求如下:
[问题描述]
约瑟夫(Joeph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
[基本要求]
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
[测试数据]
m的初值为20;密码:3,1,7,2,4,8,4(正确的结果应为6,1,4,7,2,3,5)。
[实现提示]
程序运行后首先要求用户指定初始报数上限值m,然后读取各人的密码。设n≤30。
算法的实现其实并不难,需要自定义一个Node类,即节点类,里面有成员变量no、pass、position,分别是节点的编号,节点的密
码,节点对应的图片的编号。然后建立单向循环链表,即链表的尾节点指向头结点,利用初始密码m遍历链表,找到对应的节点,将
它的编号输出,并得到它的密码作为新的m值继续遍历,直到删除全部的节点为止。而最主要的是动画的实现,利用线程的休眠和不
断地重画可以实现动画效果,需要注意的是,循环中的repaint方法只会执行一次,即最后执行一次,如何让它每次循环都执行一
次,出现动画效果呢?这里把循环放在一个线程中即可,而它执行一次是因为主线程的阻塞问题。
主要的代码如下:
new Thread(new Runnable(){
public void run()
{
//输出报数的人的编号,即出列
Node points; //中间的辅助节点
for(int k=0;k<n;k++)
{
String m1 = String.valueOf(m); //将int型的值转为string类型的值
l7.setText(m1); //在m值的显示处显示当前m值
points = pointer;
if(m==1) //当密码为1 时的操作
{
number(n-k,m,pointer.no,pointer.position);
while(points.next != pointer)
{
points = points.next;
}
try
{
Thread.sleep(m*1000+500);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
remove(pointer.position,n,pointer);
Node p = pointer;
for(int i=p.position;i<(n-k-1);i++)
{
p.next.position = p.next.position-1;
p = p.next;
}
m = pointer.pass; //重新辅密码m的值
String no1 = String.valueOf(pointer.no);
t4.setText(no1); //单个输出出列者的编号
ta1.setText(ta1.getText()+no1+ "\n"); //在主框中输出全部的出列人的编号
pointer = pointer.next; //删除找到的节点
points.next = pointer;
}
else //密码不为1时的操作
{
number(n-k,m,pointer.no,pointer.position);
for(int l=1;l<m;l++)
{
points = pointer;
pointer = pointer.next;
}
try
{
Thread.sleep(m*1000+500);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
remove(pointer.position,n,pointer);
Node p = pointer;
for(int i=p.position;i<(n-k-1);i++)
{
p.next.position = p.next.position-1;
p = p.next;
}
m = pointer.pass; //重新辅密码m的值
String no2 = String.valueOf(pointer.no);
t4.setText(no2); //单个输出出列者的编号
ta1.setText(ta1.getText()+no2+ "\n"); //在主框中输出全部的出列人的编号
pointer = pointer.next; //删除找到的节点
points.next = pointer;
}
}
}
}).start();
这里的remove和number方法是擦除相应的图片和报数的方法,这里的报数方法比较难写,原来用数组存的图发现它和循环的i很难有
一定的关系,所以在思索后决定用ArrayList存这些图片,问题就解决了,两个方法的具体代码如下:
public void remove(int n,int n1,Node pointer) //删除图片的方法,即有人出列,n为出列的人的图片的编号,n1为总人数,pointer是要删除的节点
{
jl1.get(n).setIcon(null);
jl2.get(n).setText(null);
jl1.remove(n);
jl2.remove(n);
p1.repaint();
}
public void number(final int n,final int m,final int no,final int position) //n为当前人数,m为密码值,no为删除的人后面的人的编号,position为删除的人的后面的人的图片编号
{
new Thread(new Runnable(){
public void run()
{
int i = 0;
int num1;
while(i<m)
{
num1 = position+i;
if(num1>=n)
{
num1 = num1%n;
}
String st = String.valueOf(i+1); //将数字转换成字符串
if(jl1.get(num1).getIcon() != null)
{
jl2.get(num1).setText(st);
repaint();
try
{
Thread.sleep(1000);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jl2.get(num1).setText(null);
repaint();
}
i = i+1;
}
}
}).start();
}
}
最后做出的动画效果在预料之中,算是比较好的一次课设,前后大概一个星期,感触颇深,发现自己还是开发实践的次数少,经验
少。但也让我明白:不迈出第一步,永远也到不了终点!