单向环形链表
最后一个节点的next指向第一个节点,形成一个环形(可以有头结点,也可以无头结点)
约瑟夫问题
Josephu(约瑟夫、约瑟夫环) 问题:
设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
举例:
先完成循环链表的构建:
package linkedlist;
public class Josephu {
public static void main(String[] args) {
//测试
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);//加入5个小孩
circleSingleLinkedList.showBoy();
}
}
// 创建一个环形链表类
class CircleSingleLinkedList {
// 先创建一个first节点,当前没有编号
private Boy first;
// 添加小孩节点,构成一个环形链表
public void addBoy(int nums) {
// 对num进行校验
if (nums < 1) {
System.out.println("小孩数不正确");
return;
}
//创建一个辅助变量
Boy temp = null;
//使用for来创建环形链表
for(int i=1;i<=nums;i++) {
//根据编号创建小孩节点
Boy boy = new Boy(i);
//如果是第一个节点
if(i==1) {
first = boy;
boy.setNext(boy);
temp = boy;
}else {
temp.setNext(boy);
boy.setNext(first);
temp = boy;
}
}
}
//遍历当前链表
public void showBoy() {
//判断链表是否为空
if(first == null) {
System.out.println("没有任何小孩");
return;
}
//因为first不能动,所以我们需要一个辅助变量
Boy temp = first;
while(true) {
System.out.println("小孩的编号为"+temp.getNo());
if(temp.getNext() == first) {
return;
}
temp =temp.getNext();
}
}
}
// 先创建一个Boy类,表示一个节点
class Boy {
private int no;// 编号
private Boy next;// 指向下一个节点
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;
}
}
结果:
小孩的编号为1
小孩的编号为2
小孩的编号为3
小孩的编号为4
小孩的编号为5
小孩出圈思路:
代码:
public class Josephu {
public static void main(String[] args) {
// 测试
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.countBoy(1, 2, 5);
}
}
// 创建一个环形链表类
class CircleSingleLinkedList {
// 先创建一个first节点,当前没有编号
private Boy first;
// 添加小孩节点,构成一个环形链表
public void addBoy(int nums) {
// 对num进行校验
if (nums < 1) {
System.out.println("小孩数不正确");
return;
}
// 创建一个辅助变量
Boy temp = null;
// 使用for来创建环形链表
for (int i = 1; i <= nums; i++) {
// 根据编号创建小孩节点
Boy boy = new Boy(i);
// 如果是第一个节点
if (i == 1) {
first = boy;
boy.setNext(boy);
temp = boy;
} else {
temp.setNext(boy);
boy.setNext(first);
temp = boy;
}
}
}
// 根据用户输入,计算小孩出圈顺序
/*
* startNo 表示从第几个小孩开始报数
* countNum 表示数几下
* nums 表示总共有几个小孩
*/
public void countBoy(int startNo, int countNum, int nums) {
// 往环形链表添加nums个小孩
addBoy(nums);
// 先对数据进行校验
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("参数输入有误,请重新输入");
return;
}
// 先让first移动k-1次
for (int i = 0; i < startNo - 1; i++) {
first = first.getNext();
}
// 创建一个辅助变量
Boy temp = first;
// 将temp指向first前的小孩
while (temp.getNext() != first) {
temp = temp.getNext();
}
// 在小孩报数时,让temp和first同时移动countNum-1次,然后让first指向的小孩出圈
while (temp != first) {// 当圈中只有一个小孩是,跳出循环
// 让temp和first同时移动countNum-1次
for (int i = 0; i < countNum - 1; i++) {
temp = temp.getNext();
first = first.getNext();
}
// 这时first指向的节点就是该出圈的小孩
System.out.println("小孩" + first.getNo() + "出圈");
// 删除该节点
first = first.getNext();
temp.setNext(first);
}
System.out.println("最终留在圈内的小孩编号为" + first.getNo());
}
}
// 先创建一个Boy类,表示一个节点
class Boy {
private int no;// 编号
private Boy next;// 指向下一个节点
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;
}
}
结果:
小孩2出圈
小孩4出圈
小孩1出圈
小孩5出圈
最终留在圈内的小孩编号为3