数据结构入门(4)----约瑟夫问题-单向循环链表

约瑟夫问题是一个经典的问题,描述如下:

有n个人围成一圈,从第一个人开始报数,数到m的人出圈,剩下的人继续从1开始报数,数到m的人再出圈,如此循环,直到只剩下一个人。

1、构建一个单向的环形链表思路
(1). 先创建第一个节点, 让 first 指向该节点,并形成环形
(2). 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可
2、遍历环形链表
(1). 先让一个辅助指针(变量)temp,指向first节点
(2). 然后通过一个while循环遍历 该环形链表即可temp.next == first 结束

代码实现:

public class SinglyCircularLinkedNode {
    public int num;
    public SinglyCircularLinkedNode next;
    public SinglyCircularLinkedNode(int num) {
        this.num = num;
    }
}

class SinglyCircularLinkedList{
    //约瑟夫问题:丢手绢问题,围成一圈,数数,数到谁谁出列,直到最后剩余一个人
    //无头节点,初始化第一个节点
    public SinglyCircularLinkedNode first = null;

    /**
     * 按照节点总数添加
     * @param num
     */
    public void createList(int num){
        //数据验证
        if (num < 1){
            System.out.println("输入人数不合法");
            return;
        }
        //建立一个辅助节点
        SinglyCircularLinkedNode temp = null;
        for (int i =1; i <= num; i++){
            SinglyCircularLinkedNode singlyCircularLinkedNode = new SinglyCircularLinkedNode(i);
            //只有一个节点的情况下,自己指向自己,形成一个循环
            if (i == 1){
                first = singlyCircularLinkedNode;
                first.next = first;
                temp = first;
            }else {
                temp.next = singlyCircularLinkedNode;
                singlyCircularLinkedNode.next = first;
                temp = singlyCircularLinkedNode;
            }
        }
    }

    /**
     * 遍历链表
     */
    public void showAll(){
        //有效性验证
        if (first == null){
            System.out.println("链表内无节点");
            return;
        }
        //辅助节点
        SinglyCircularLinkedNode temp = first;
        while (true){
            //最后一个节点
            if (temp.next == first){
                System.out.print(temp.num);
                System.out.println(" 遍历结束");
                break;
            }
            System.out.print(temp.num + " => ");
            temp = temp.next;
        }
    }

    /**
     * 开始丢手绢
     *
     * @param startNo  表示从第几个人开始数数
     * @param countNum 表示每次报数几
     * @param nums     表示最初圈内的总人数
     */
    public void outNode(int startNo, int countNum, int nums){
        // 数据校验
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("参数输入有误, 请重新输入");
            return;
        }

        // 创建辅助指针
        SinglyCircularLinkedNode helper = first;// 初始化自己套自己,因为圈内目前就一个
        // 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点
        while (true) {
            if (helper.next == first) {
                // 已经到最后一个
                break;
            }
            // 后移
            helper = helper.next;
        }

        //从第几个人开始报数
        for (int j = 0; j < startNo - 1; j++) {
            first = first.next;
            helper = helper.next;
        }

        //报数
        while (true) {
            if (helper == first) {
                // 说明圈中只有一个节点
                break;
            }
            // 让 first 和 helper 指针同时 的移动 countNum - 1
            for (int j = 0; j < countNum - 1; j++) {
                first = first.next;
                helper = helper.next;
            }
            // 这时first指向的节点,就是要出圈的节点
            System.out.printf("节点%d出圈\n", first.num);
            // 这时将first指向的节点出圈
            helper.next = first.next;
            first = first.next;

        }
        System.out.printf("最后留在圈中的节点编号%d \n", first.num);
    }
}

测试:

public class SinglyCircularLinkedList_Test {
    public static void main(String[] args) {

        //初始化链表
        SinglyCircularLinkedList singlyCircularLinkedList = new SinglyCircularLinkedList();
        singlyCircularLinkedList.createList(5);
        singlyCircularLinkedList.showAll();
        singlyCircularLinkedList.outNode(1,2,5);

    }
}

 输出:

1 => 2 => 3 => 4 => 5 遍历结束
节点2出圈
节点4出圈
节点1出圈
节点5出圈
最后留在圈中的节点编号3 

Process finished with exit code 0

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值