单向环形链表应用场景
Josephu(约瑟夫、约瑟夫环) 问题
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
思路:用循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
单向环形链表
代码实现
输入n,k,m
n:表示有n个小孩
k:表示从第k个小孩开始计数
m:表示每次计数值为m
输出:小孩出列顺序
用例:
输入:5,2,3;输出:3 5 2 1 4
输入:6,1,4;输出:4 2 1 3 6 5
import java.util.ArrayList;
import java.util.List;
/**
* @author 陈治远
* @create 2019-07-27 21:02
*/
public class JosephuQuestion {
public static void main(String[] args) {
List<Node> nodeList = solve(6, 1, 5);
if (nodeList != null) {
for (Node node : nodeList) {
System.out.print(node.getNum() + "\t");
}
}
}
/**
* 解决约瑟夫问题
* @param n n个节点
* @param k 从第k个节点开始计数
* @param m 每次计数m次
* @return 返回一个按删除顺序保存的节点的List
*/
public static List<Node> solve(int n, int k, int m) {
// 初始化一个长度为n的环形链表
Node firstNode = init(n);
if (firstNode == null) {
return null;
}
// 找到链表中编号为k的节点的前一个节点,用before表示
// 第一个节点的编号是1,所以前一个节点的编号就是k-1
// 因为遍历是从0开始,0表示第一个节点,则k-1-1表示前一个节点
// 当k==1时,k-1=-1,所以应该有一个取模操作
Node before;
Node node = firstNode;
for (int i = 0; i < (k - 2 + n) % n; i++) {
node = node.getNext();
}
before = node;
// 按删除顺序保存所有节点
List<Node> nodeList = new ArrayList<>();
while (true) {
// 得到第一个计数的节点,temp变量是辅助节点
Node temp = before.getNext();
// 开始计数,计数为m,需要循环m-1次
for (int i = 1; i < m; i++) {
if (i == m - 1) {
// 重置下一轮计数的before
before = temp;
}
temp = temp.getNext();
}
// for循环结束后,temp就是计数为m的那个节点,添加到nodeList
nodeList.add(temp);
// 删除这个节点
before.setNext(temp.getNext());
// 当只有一个节点时,结束while循环
if (temp.getNext() == temp) {
break;
}
}
return nodeList;
}
/**
* 初始化一条节点数为n的单向环形链表,并返回第一个节点
* @param n 链表的节点数
* @return 返回第一个节点
*/
public static Node init(int n) {
if (n < 1) {
return null;
}
// 初始化第一个节点
Node firstNode = new Node(1);
// 保存当前节点
Node curNode = firstNode;
for (int i = 2; i <= n; i++) {
Node node = new Node(i);
curNode.setNext(node);
curNode = node;
}
// 首尾对接
curNode.setNext(firstNode);
return firstNode;
}
// 创建一个内部类表示节点(一个节点对象就是一个小孩)
private static class Node {
private int num; // 节点编号
private Node next; // 后节点指针
public Node(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
}