问题描述:
Josephu 问题:
设编号为 1,2,…… n 的n个人未作一圈,约定编号为 k (1<=k<=n)的人从1 1开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,疏导 m 的那个人出列。依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
思路分析:
可以使用单向环形链表来解决该类题目:
例如:当 n = 5, k = 1,m = 2,时
它的出列顺序依次是 2 -> 4 -> 1 -> 5 -> 3;
- 第一步:先创建一个节点类,将单向环形链表的各个节点用类来封装,因为每个节点都包括 序号、next,因此就可以直接从某个特定的类表示,每次添加链表时,创建这个对象即可;
- 第二步:创建一个单向环形链表类,里边包括 创建链表的方法、出链表的方法;
- 第三步:主函数运行即可。
代码实现:
节点类
class BoyNode{
private int no;
private BoyNode next;
public BoyNode(int no){
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public BoyNode getNext() {
return next;
}
public void setNext(BoyNode next) {
this.next = next;
}
}
链表类
class CircleSingleLinkedList{
// 环形链表的第一个节点
private BoyNode first = null;
// 给定一个值 n 创建 大小为 n 的环形链表
public void makeLinkedList(int n){
// 先对 n 做判断
if(n < 1){
System.out.println("n 的值不正确~~~");
return;
}
BoyNode cur = null;
for(int i = 1; i <= n; i++){
BoyNode boy = new BoyNode(i);
if(i == 1){
first = boy;
first.setNext(first); //构成一个环
cur = boy;
}else{
cur.setNext(boy);
boy.setNext(first);
cur = boy;
}
}
}
// 打印验证
public void show(){
if(first == null){
System.out.println("链表为空");
return;
}
BoyNode cur = first;
while(true){
if(cur.getNext() == first){
System.out.printf("这是第%d个小孩\n",cur.getNo());
return;
}
System.out.printf("这是第%d个小孩\n",cur.getNo());
cur = cur.getNext();
}
}
// 出圈的问题:有大小为 n 的链表,从第 k 个人开始,每次喊 m ,喊到 m 的人出列,从出列的下一个人重新开始
/**
*
* @param n 代表这个单向环形链表的大小
* @param k 代表从 第 几个 人开始
* @param m 代表每次喊 几个
*/
public void outLinkedList(int n, int k, int m){
if(first == null | k < 1 || k > n){
System.out.println("参数输入有误~");
return;
}
// (1)先将 first 挪到第 k 个人跟前
for(int i = 1; i < k; i++){
first = first.getNext();
}
// (2)删除
// (2.1)先找着第 k - 1 个节点
BoyNode cur = first;
while(true){
if(cur.getNext() == first){ // 当 cur 的下一个节点是 first 时,就是当前环形链表的最后一个节点
break;
}
cur = cur.getNext();
}
// (2.2)从 第 k 个人开始喊,这是一个死循环,直到链表中只剩下一个的时候
// 要删某个元素,思考单链表删除指定节点的方式
// 删除指定的节点,必须要定义一个 临时节点,使这个临时节点指向要删除节点的上一个节点,此时只需:cur.next = cur.next.next
while(true){
if(cur == first){
break;
}
for(int i = 1; i < m; i++){
first = first.getNext();
cur = cur.getNext();
}
// 这是 first 指向的小孩就是要出圈的小孩,cur是first 的上一个节点
System.out.printf("小孩 %d 要出圈\n",first.getNo());
first = first.getNext();
cur.setNext(first);
}
System.out.printf("最后要出圈的小孩是 %d\n",cur.getNo());
}
}
测试主类
public class JosephCircle {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.makeLinkedList(5);
circleSingleLinkedList.show();
System.out.println("=============================");
circleSingleLinkedList.outLinkedList(5,1,2);
}
}
代码运行结果:
当然这类问题还有其他方法可以解决,比如数组,不过我还没写呢,等写好了再看