约瑟夫问题
设编号为1,2,3……n的n个人围坐成一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
构件一个环形链表
创建思路
1. 先创建一个结点,让first指向其,并形成环
2. 后面每创建一个新的结点,并将此结点加入到其中即可。
遍历思路
# 遍历思路
1. 让辅助指针cur指向first
2. 通过while循环遍历这个环即可
# 终止条件
(cur.next == first)
出列(圈)思路
1. 需要创建一个辅助指针(变量)helper,将其指向环形链表的最后这个结点。
2. 小孩报数前,先让first和helper移动(k-1)次
3. 当小孩报数时,让first和helper指针同时的移动(m-1)次
4. 这时就可以将first指向的小孩结点出圈了
#
first = first.next
helper.next = first
5. 原来的first指向的结点没有任何引用,就会被回收。
代码实现
public class Joseph {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addPeople(5);
circleSingleLinkedList.outCircle(1,2,5);
}
}
//创建一个环形的单向链表
class CircleSingleLinkedList {
private People first;
/**
* 添加小孩结点
*
* @param nums 添加的人数
*/
public void addPeople(int nums) {
if (nums < 1) {
System.out.println("传入数据不正确");
return;
}
People cur = null;
for (int i = 1; i <= nums; i++) {
//根据编号创建小孩结点
People people = new People(i);
//加入结点(第一个情况特殊)
if (i == 1) {
first = people;
first.setNext(first);//构成一个环
cur = first;
} else {
cur.setNext(people);
people.setNext(first);
cur = people;
}
}
}
/**
* 遍历当前环形链表
*/
public void list() {
if (first == null) {
System.out.println("当前没有任何小孩子");
return;
}
//因为first不允许被修改所以需要使用辅助指针来完成遍历操作
People cur = first;
while (true) {
System.out.printf("the people's no is %d \n", cur.getNo());
if (cur.getNext() == first) {
//已经遍历完了
break;
}
cur = cur.getNext();//指针后移
}
}
/**
* 小孩出圈
*
* @param startNum 从第几个开始数
* @param countNum 数几出圈
* @param nums 总共有多少小孩
*/
public void outCircle(int startNum, int countNum, int nums) {
if (first == null || startNum < 1 || startNum > nums) {
System.out.println("您给的参数有误");
return;
}
//创建辅助指针,帮助完成小孩出圈
People helper = first;
while (helper.getNext() != first){
helper = helper.getNext();
}
//从第几个开始
for (int i = 0; i < startNum - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//小朋友在报数的时候让first和helper指针同时移动m-1次
//直到剩最后一个小朋友的时候停止
while (true){
if (helper == first){
break;
}
//让first和helper指针同时移动countNum - 1
for (int i = 0; i < countNum - 1 ; i++) {
first = first.getNext();
helper = helper.getNext();
}
//这时first指向的结点,就是要出圈的结点
System.out.printf("%d玩家出圈\n",first.getNo());
first = first.getNext();
helper.setNext(first);
}
System.out.println("最后的小孩编号为"+first.getNo());
}
}
// People 表示一个小孩结点
class People {
private int no;
private People next;
public People(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public People getNext() {
return next;
}
public void setNext(People next) {
this.next = next;
}
@Override
public String toString() {
return "小朋友编号{" +
"no=" + no +
'}';
}
}