环形链表介绍和约瑟夫问题
单向环形链表应用场景
问题
Josephu问题为:设编号为1,2, .n的n个人围坐一圈,约定编号为k (1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
- n=5,即有5个人
- k=1,从第一个人开始报数
- m=2,数2下
构建一个单向的环形链表思路
1、先创建第一个节点,让first指向该节点并形成环形
2、后面当我们每创建一一个新的节点, 就把该节点加入到已有的环形链表中即可。
遍历环邢链表
1、先让一个辅助指针(变量)curBoy,指向first节点
2、然后通过一个while循环遍历该环形链表即可curBoy.next == first结束
根据用户输入一个数字,根据这个顺序来出圈
n=5,既有5个人
k=1,从第一个开始报数
m=2,数2下
1、创建一个辅助指针(变量)helper指针同时指向环形链表的最后这个节点
2、当小孩报数前,先让first和helper移动k - 1次
3、当小孩报数时,让first和helper指针同时的移动m - 1次
4、这是就可以将first指向小孩出圈
first = first.next();
hepler.next() = first
原来first指向的节点就没有任何引用,就会被回收
代码示例
package test;
/**
* 约瑟夫
*/
public class Josepfu {
public static void main(String[] args){
CircleSingLinkedList circleSingLinkedList = new CircleSingLinkedList();
circleSingLinkedList.addBoy(5);
circleSingLinkedList.showBoy();
circleSingLinkedList.countBoy(1, 2, 5);
}
}
// 创建一个环形单向列表
class CircleSingLinkedList{
// 创建一个first节点,当前没有编号
private Boy first = new Boy(-1);
// 添加小孩节点,构建成一个环形的链表
public void addBoy(int nums){
// nums 做一个数据校验
if (nums < 1){
System.out.println("nums的值不正确!");
return;
}
// 辅助指针,帮助构建环形链表
Boy curBoy = null;
// 使用for来创建我们的环形链表
for (int i = 1; i <= nums; i++){
// 根据编号,创建小孩节点
Boy boy = new Boy(i);
// 如果是第一个小孩
if (i == 1){
first = boy;
first.setNext(first);// 构成环形
curBoy = first;// 让curBoy指向第一个小孩
} else {
curBoy.setNext(boy);
boy.setNext(first);
curBoy = boy;
}
}
}
// 遍历当前的环形链表
public void showBoy(){
// 判断链表是否为空
if (first == null){
System.out.println("没有任何小孩!");
return;
}
// 因为first不能动,因此我们仍然使用一个辅助指针完成遍历
Boy curBoy = first;
while (true){
System.out.println("小孩的编号是" + curBoy.getNo());
// 说明已经遍历完毕
if (curBoy.getNext() == first){
break;
}
// curBoy后移
curBoy = curBoy.getNext();
}
}
/**
* 根据用户的输入,计算出小孩出圈的顺序
* @param startNo 表示第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有多少个小孩在圈中
*/
public void countBoy(int startNo, int countNum, int nums){
// 先对数据进行校验
if (first == null || startNo < 0 || startNo > nums){
System.out.println("参数输入有误,请重新输入!");
return;
}
// 创建要给辅助指针,帮助完成给小孩出圈
Boy helper = first;
// 需求创建一个辅助指针(变量)helper,实现完成指向环形链表的最后一个节点
while (true){
// 说明helper指向最后小孩节点
if (helper.getNext() == first){
break;
}
helper = helper.getNext();
}
// 当小孩报数前,先让first和helper移动k - 1次
for (int i = 0; i < startNo - 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.println("要出圈的小孩节点是:" + first.getNo());
// 这时将first指向小孩的节点出圈
first = first.getNext();
helper.setNext(first);
}
System.out.println("最后留在圈中小孩节点是:" + first.getNo());
}
}
// 创建一个Boy类,表示一个点
class Boy{
private int no;// 编号
private Boy next;// 指向下一个节点,默认null
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;
}
public Boy(int no) {
this.no = no;
}
}