环形单向链表解决约瑟夫问题:
首先要创建一个N个小孩的环形单向链表:
1.创建一个first指针,置为空,再创建一个辅助指针curBoy
2.用for循环创建小孩节点,如果是第一个节点(first指向第一个小孩,并且第一个小孩的next域指向自己,再让辅助指针curBoy指向第一个小孩)
3.用for循环创建小孩节点,如果是第一个节点后的节点(首先让curBoy的next域指向当前小孩,再让当前小孩的next域指向first,最后让辅助指针移动到当前小孩上)
4.重复3,直到所有小孩都加完
环形单向链表创建好后,接下来就是小孩出圈:
1.创建一个help指针,让它位于first所在的第一个节点的前一个节点,之后help指针一直都是跟着first指针移动的
2.从第K个小孩开始报数,那么,first指针应该移动到第K个小孩上,help指针应该跟着移动到first指针的后一个节点上
3.开始报数,first指针移动M-1个节点,help也移动M-1个节点
4.第三步中first所在的小孩节点出圈(先让first后移到下一个节点,再让help所在的小孩节点的next域指向first移动后的节点)
5.重复第3、4步,直到first与help指针重合就结束,最后剩下的那个小孩就是first和help指针所在的那个小孩节点
代码:
小孩节点类
/**
* 小孩节点类
* @author codewen
*
*/
public class Boy {
private int bNo;//小孩编号
Boy next;//下一个男孩
public Boy() {
}
public Boy(int bNo) {
this.bNo = bNo;
}
public int getbNo() {
return bNo;
}
public void setbNo(int bNo) {
this.bNo = bNo;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
@Override
public String toString() {
return "Boy [bNo=" + bNo + "]";
}
}
import org.junit.Test;
/**
* 约瑟夫问题
* @author codewen
*
*/
public class JosephTest {
private Boy first = null;//创建第一个节点指针用于标记第一个节点
//增加节点的方法
public void addBoy(int boyNum) {
if(boyNum < 1) {
System.out.println("男孩个数不符合要求,无法增加节点");
return;
}
Boy curBoy = null;//创建一个辅助指针
for(int i=1; i<=boyNum; i++) {
Boy boy = new Boy(i);
if(i == 1) {//如果是第一个男孩
//让first指针指向第一个男孩
first = boy;
//让第一个男孩next域指向自己 形成环形
boy.next = boy;
//让辅助指针指向第一个男孩
curBoy = boy;
}else {//如果不是第一个男孩
curBoy.next = boy;
//让男孩的next域指向头节点 形成环形
boy.next = first;
//辅助指针后移
curBoy = curBoy.next;
}
}
}
//显示节点的方法
public void queryBoy() {
if(first == null) {
System.out.println("该环形链表为空,无法显示");
return;
}
Boy curBoy = first;//辅助指针
while(true) {
System.out.println(curBoy);
if(curBoy.next == first) {
break;
}
curBoy = curBoy.next;
}
}
//小孩出圈的方法 有N个小孩,从第K个开始报数,数到M的小孩出圈
public void outBoy(int N, int K, int M) {
if(K < 1 && K > N) {
System.out.printf("总共%d个小孩,不能从第%d个小孩开始报数\n", N, K);
return;
}
if(M < 1) {
System.out.println("M不能小于1");
return;
}
//1.创建环形单向链表
addBoy(N);
//2.创建一个help指针,让它位于first所在的第一个节点的前一个节点,之后help指针一直都是跟着first指针移动的
Boy help = first;
for(int i=0; i<N-1; i++) {
help = help.next;
}
//2.从第K个小孩开始报数,那么,first指针应该移动到第K个小孩上,help指针应该跟着移动到first指针的后一个节点上
for(int i=0; i<K-1; i++) {
first = first.next;
help = help.next;
}
//5.重复第3、4步,直到first与help指针重合就结束,最后剩下的那个小孩就是first和help指针所在的那个小孩节点
while(help != first) {
//3.开始报数,first指针移动M-1个节点,help也移动M-1个节点
for(int i=0; i<M-1; i++) {
first = first.next;
help = help.next;
}
//4.第三步中first所在的小孩节点出圈(先让first后移一个节点,再让help所在的小孩节点的next域指向first移动后的节点)
System.out.println("出圈的小孩为:"+first);//或者help(此时help与first相等)
first = first.next;
help.next = first;
}
System.out.println("最后剩下的小孩为:"+first);
}
@Test
public void test1() {
//addBoy(5);
//queryBoy();
outBoy(5, 1, 2);
}
}
结果: