约瑟夫问题。
统称处理环状排队出列逻辑,例如一圈小孩围成一个圈,任意指定哪一个小孩开始报数,报指定数之后指向的小孩退出,接下来退出小孩的下一个小孩开始报数,固定数目之后指向的小孩退出,依次循环,直到最后一个小孩。
约瑟夫问题是一个经典的算法问题,其解决过程涉可能用到的数据结构有数组、链表,涉及的算法包括模拟、递归、递推、动态规划等等,因此非常适合做一道面试题。
问题描述:
首先n个候选人围成一个圈,依次编号为0..n-1。然后指定一个正整数k,并0号候选人开始按从1到k的顺序依次报数,n-1号候选人报数之后,又再次从0开始。当有人报到K时,这个人被淘汰,从圈里出去。下一个人从1开始重新报数。重复上述过程,直至剩下一个人,求问最后这一人在最开始的圈中的编号。
实现
我们用(java)循环链表实现约瑟夫问题
代码如下:
/**
* @date 19-9-21
* @auther jackliang
* @description TODO
*/
public class Josepofu {
private Boy first = null;
public static void main(String[] args) {
Josepofu circleLinkedList = new Josepofu();
circleLinkedList.addBoy(25);//这里数目可以自己定
circleLinkedList.list();
circleLinkedList.countBoy(1, 2, 25);//从第一个小孩开始报数,数2下,指针往后移动一位,圈里边有25个小孩
}
//添加小孩节点构成一个环形链表
public void addBoy(int numi) {
if (numi < 2) {//环形链表里边至少应该有两个值
System.out.println("numi 值不正确");
}
//for 循环创建循环链表
Boy curBoy = null; //辅助指针
for (int i = 1; i <= numi; i++) {
//根据编号创建一个小孩节点
Boy boy = new Boy(i);
if (i == 1) {
first = boy;
first.next = first;//构成环状
curBoy = first;//辅助指针定义,为了后面的小孩出圈操作,指定头指针前的指针指向
} else {
curBoy.next = boy;
boy.next = first;//这也是为了构成新节点和first节点的指针闭环
curBoy = boy;//指针移动至下一个节点以便下一次新节点的前一个节点可以指向新节点
}
}
}
//根据用户的输入形成小孩出圈的顺序
/**
* @param startNo //表示从第几个小孩还是数数
* @param countNum // 表示数几下
* @param nums // 表示最初有多少个小孩
*/
public void countBoy(int startNo, int countNum, int nums) {
//
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("参数输入有误");
}
Boy helper = first;
// 创建一个辅助变量helper,事先应该指向环形链表的最后这个节点
while (true) {
if (helper.next == first) {
break;
}
helper = helper.next;
}
//小孩报数前先让,first and helper 移动k -1 次,以便确定从那个小孩开始报数
for (int j = 0; j < startNo - 1; j++) {
first = first.next;
helper = helper.next;
}
//当小孩开始报数,让first 和 helper 指针同时移动m - 1 次,然后出圈,
while (true) {
if (helper == first) {
break;
}
//让first 和 helper 指针同时移动 countNum 次
for (int j = 0; j < countNum - 1; j++) {
first = first.next;
helper = helper.next;
}
System.out.printf("小孩%d出圈\n", first.no);
/*指定小孩出圈,出圈小孩前一个小孩的指针指向出圈小孩的下一个小孩,并且指向出圈小孩的头指针指向下一个小孩*/
first = first.next;
helper.next = first;
}
System.out.printf("最后留在圈中的小孩为%d号", helper.no);
}
public void list() {
//判断链表是否为空
if (first == null) {
System.out.println("链表为空");
return;
}
Boy curBoy = first;
while (true) {
System.out.printf("打印小孩编号=%d\n", curBoy.no);
if (curBoy.next == first) {
System.out.println("遍历完毕");
break;
}
curBoy = curBoy.next;
}
}
}
class Boy {
public int no;
public Boy next;
public Boy(int no) {
this.no = no;
}
@Override
public String toString() {
return "Boy{" +
"no=" + no +
'}';
}
}