一.回顾导学
在第一章时,我们简单提到了约瑟夫环(即丢手帕)的问题:
想必,这个游戏我们小时候或多或少都接触过,现在把它转换为代码实现,
即Josephu 问题:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
二.约瑟夫环问题解决
2.1逻辑分析
1)根据用户的输入,生成一个小孩出圈的顺序,现在方便理解先假设为下列:
n = 5 , 即有5个人
k = 1, 从第一个人开始报数
m = 2, 数2下
2)需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.
3) 当小孩报数时,让first 和 helper 指针同时移动 m - 1 次
4) 这时就可以将first 指向的小孩节点 出圈 first = first .next helper.next = first 原来first 指向的节点就没有任何引用,就会被回收 出圈的顺序 2->4->1->5->3
注意: 小孩报数前,先让 first 和 helper 移动 k - 1次。因为我们假设的k = 1,即刚好不需要移动,在实际问题中还是要把这个问题考虑进去
2.2代码实现
package zy.circleList;
import java.util.Scanner;
/**
* @author 十三
* @version 1.0
*/
public class Josephu {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
CircleList list = new CircleList();
list.circle(N);
// list.show();
list.count(1,3,N);
}
}
//创建一个环形单链表
class CircleList {
private Boy first = null;//创建第一个节点,目前没有编号
//创建一个方法创建环形单链表
public void circle(int num) {
if (num < 1) {//为空的话不满足
return;
}
Boy cur = null;//辅助指针帮助构建环形单链表
for (int i = 1; i <= num; i++) {
Boy boy = new Boy(i);
if (i == 1) {
first = boy;
first.setNext(first);
cur = first;
} else {
cur.setNext(boy);
boy.setNext(first);
cur = boy;
}
}
}
public void show() {
if (first == null) {
return;
}
Boy cur = first;
while (true) {
System.out.printf("小孩的编号%d\n", cur.getNo());
if (cur.getNext() == first) {
break;
}
cur = cur.getNext();
}
}
/**
* @param start 从第几个小孩开始
* @param count 步长,
* @param sums 总共有几个小孩
*/
public void count(int start, int count, int sums) {
if (first == null || start < 1 || start > sums) {
return;
}
Boy temp = first;
while (true) {
if (temp.getNext() == first){//转一圈了
break;
}
temp = temp.getNext();
}
//将小孩移到start
for (int i = 0; i < start - 1; i++) {
first = first.getNext();
temp = temp.getNext();
}
while (true){
if (temp == first){//环形链表只剩一个了
break;
}
for (int i = 0; i < count - 1; i++) {
first = first.getNext();
temp = temp.getNext();
}
System.out.printf("小孩%d出圈\n",first.getNo());
first = first.getNext();
temp.setNext(first);
}
System.out.printf("%d留圈\n",first.getNo());
// System.out.println(first.getNo());
}
}
class Boy {
private int no;//编号
private Boy next;//指向下一个节点,默认为null
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,有多少个小孩是我们自己输入的,其实如果需要的话,start 和count也可以自己接受参数,只是刚好我们pta上有一个约瑟夫环的问题,它的要求就是从1 开始,步长为3,
但好像pta上只允许有一个class(哎,欲哭无泪)
2.3结果验证
经验证正确~
三.小结
1)需要注意的是: 小孩报数前,记得先让 first 和 helper 移动 k - 1次。因为刚开始假设的k =1,写代码时忘记了移动了,想当然的直接进行出圈判断了,
2)还是导学里说的,代码思想来源于生活中的实际问题!