单向环形链表实现与约瑟夫问题算法

概述

约瑟夫问题是什么:约瑟夫问题

简单的来讲,约瑟夫问题就是一个拥有n个人围成的圆圈,从第m个人开始报数k,第k个报数的人出圈,直到剩下最后一个人。

解决约瑟夫问题通常有两种算法

  1. 数组取模
  2. 单向环形链表

由于数组取模在数组模拟队列中已经演示过,本次只做单向环形链表的实现

单向环形链表

节点对象:

package com.leolee.dataStructure.linkedList;

/**
 * @ClassName JosephNode
 * @Description: 约瑟夫问题节点
 * @Author LeoLee
 * @Date 2020/9/15
 * @Version V1.0
 **/
public class JosephNode {

    private int no;

    private JosephNode next;//下一个节点,默认为空

    public JosephNode (int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public JosephNode getNext() {
        return next;
    }

    public void setNext(JosephNode next) {
        this.next = next;
    }
}

单项环形链表实现:

package com.leolee.dataStructure.linkedList;

/**
 * @ClassName CircleSingleLinkedList
 * @Description: 环形单向链表
 * 约瑟夫问题,n个小孩围成一圈,从第m个小孩开始报数k,第k个报数的小孩离开,得出离开的顺序。用单向环形链表解决
 * @Author LeoLee
 * @Date 2020/9/15
 * @Version V1.0
 **/
public class CircleSingleLinkedList {

    //创建第一个节点
    private JosephNode firstNode = null;

    //创建有num个节点的单向环形链表
    public void create (int num) {

        if (num < 1) {
            System.out.println("num值不合法");
            return;
        }

        JosephNode currentNode = null;
        for (int i = 1; i <= num; i++) {
            //根据编号创建节点
            JosephNode node = new JosephNode(i);
            //添加节点
            if (i == 1) {
                firstNode = node;
                firstNode.setNext(firstNode);//构成只有一个节点的环状
                currentNode = firstNode;//让currentNode指向第一个节点
            } else {
                currentNode.setNext(node);
                node.setNext(firstNode);
                currentNode = node;
            }
        }
    }

    public void showNodes () {

        if (firstNode == null) {
            System.out.println("当前链表为空");
            return;
        }

        JosephNode currentNode = firstNode;
        while (true) {
            System.out.printf("当前节点编号:[%d]\n", currentNode.getNo());
            if (currentNode.getNext() == firstNode) {//遍历完毕
                break;
            }
            currentNode = currentNode.getNext();//currentNode后移到下一个节点
        }
    }

    /**
     * 功能描述: <br> 用单向环形链表解决约瑟夫问题(也可以用数组取模)
     * 〈〉
     * @Param:
     * [startNo第几个小孩开始,
     * countNum报数次数,
     * num一共多少个小孩]
     * @Return: void
     * @Author: LeoLee
     * @Date: 2020/9/15 13:22
     */
    public void countBoy (int startNo, int countNum, int num) {

        if (firstNode == null || startNo < 1 || countNum < 1 || num < 1 || startNo > num) {
            System.out.println("当前链表不合法");
            return;
        }

        //报数前,先让tempNode和firstNode移动(k - 1)次,相当于firstNode在报数小孩位置,tempNode在firstNode前
        for (int i = 0; i < startNo - 1; i++) {
            firstNode = firstNode.getNext();
        }

        //创建一个辅助“指针”,指向链表的最后一个节点(firstNode的上一个节点)
        JosephNode tempNode = firstNode;
        while (true) {
            if (tempNode.getNext() == firstNode) {
                break;
            }
            tempNode = tempNode.getNext();
        }

        //开始按照规定,把小孩踢出环形链表,直到环形链表中只有一个小孩(节点)
        while (true) {
            if (tempNode == firstNode) {//只有一个节点时,退出循环
                break;
            }
            //将firstNode和tempNode移动 countNum -1 次
            for (int i = 0; i < countNum - 1; i++) {
                firstNode = firstNode.getNext();
                tempNode = tempNode.getNext();
            }
            //当firstNode和tempNode移动完成后,fisrtNode指向的节点(小孩),就是要出圈的节点
            System.out.printf("节点[%d]出圈\n", firstNode.getNo());
            firstNode = firstNode.getNext();
            tempNode.setNext(firstNode);
        }
        System.out.printf("最后留在圈中的节点:[%d]\n", firstNode.getNo());
    }
}

测试方法:

public static void main(String[] args) {

    //单向环形链表
    CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    circleSingleLinkedList.create(5);
    circleSingleLinkedList.showNodes();
    //出圈测试
    circleSingleLinkedList.countBoy(1, 2, 5);
}

输出如下:

当前节点编号:[1]
当前节点编号:[2]
当前节点编号:[3]
当前节点编号:[4]
当前节点编号:[5]
节点[2]出圈
节点[4]出圈
节点[1]出圈
节点[5]出圈
最后留在圈中的节点:[3]

猜你喜欢

转载自blog.csdn.net/qq_25805331/article/details/108599026