环形链表,joseph问题

    重点:头指针 first & 尾指针 cut
    思路:
        1、先创建第一个节点,让 first 指向该节点,并形成环状
        2、后面每创建一个新节点,就把该节点加入到已有的环形链表中

    遍历:
        1、先用辅助指针指向 first 节点
        2、然后通过 while 循环遍历,终止条件: cut.next = first

节点类:

// 节点类
class children{
    int id;
    children next;
    public children(int id) {
        this.id = id;
    }
}

环形链表类:

class RingSingleLinkedList{
    // 初始化,头节点,尾指针
    children first = null;

    public void add(int x) {
        // 检查参数
        if (x < 1) {
            System.out.println("x 数值错误");
            return;
        }
        // 创建尾指针
        children last = null;

        // 开始 add 节点
        for (int i = 1; i <= x ; i++) {
            // 循环一次,创建一个节点
            children children = new children(i);
            // 初始化头节点
            if (i == 1) {
                first = children;
                // 此时环形链表中只有一个节点,该节点的 next 属性指向自身,形成环
                first.next = first;
                // last 指向 头节点
                last = first;
            } else {
                // 使 last.next 指向新节点
                last.next = children;
                // 使新节点.next 指向 first
                children.next = first;
                // last 后移,指向最尾元素
                last = children;

            }

        }

    }

    // 解决 约瑟夫 问题
    // 解决 约瑟夫问题
    /**
     *  思路:
     *      1. 需要一个辅助指针,事先指向环形链表的最后 ;
     *      补充: 小孩报数前, 先让 first 和 temp 移动 k-1 次
     *      2. 当小孩报数时, 让 first 和 temp 同时移动 (m - 1)下
     *      3. 此时, 将 first 指向的小孩节点出圈,
     *          first = first.next;
     *          temp.next = first; (原先被 first 指向的节点因为没有引用, 就会被 gc 机制回收)
     *
     * @param start 第 k 个人开始报数
     * @param m 报数报到 m 的节点弹出
     * @param number  环中小孩的总数
     */
    public void joseph(int start, int m, int numbers) {
        // 检查参数
        if (m < 1 || start > numbers || first == null) {
            System.out.println("参数错误");
            return;
        }
        // 创建辅助指针
        children temp = first;
        // 移动辅助指针,使指向 first 节点的前一个节点
        for (int i = 1; i < numbers; i++) {
            temp = temp.next;
        }

        // 此时 temp 指向 first 前一个节点,

        // 报数前的准备工作,两指针同时前移 start-1 个单位
        for (int i = 0; i < start-1; i++) {
            first = first.next;
            temp = temp.next;
        }

        // 开始报数
        while (true) {
            // 两指针同时移动 m-1 个单位,此时 first 指向的节点就是 需要被弹出的节点
            for (int i = 0; i < m-1; i++) {
                first = first.next;
                temp = temp.next;
            }
            System.out.printf("编号为 %d 的节点被弹出\n", first.id);

            // 后移 first
            first = first.next;
            // 让 temp 指向后移后的 first,此时,被弹出的节点因为没有引用指向,被 gc机制回收
            temp.next = first;

            // 终止循环的条件
            if (temp == first) {
                break;
            }
        }
        System.out.printf("最后剩余的节点编号为 %d", temp.id);

    }


    // show
    public void show() {
        // 判断是否为空
        if (first == null) {
            System.out.println("空链表");
            return;
        }
        // 辅助指针
        children temp = first;
        System.out.println("开始遍历:");
        while (true) {
            System.out.printf("编号 %d 弹出\n", temp.id);
            if (temp.next == first) {
                break;
            }
            temp = temp.next;
        }



    }

测试代码:

public class RingSingleLinkedListDome {
    public static void main(String[] args) {
        RingSingleLinkedList ringSingleLinkedList = new RingSingleLinkedList();
        ringSingleLinkedList.add(5);

        ringSingleLinkedList.joseph(1, 2, 5);

    }
}

测试的结果为:

编号为 2 的节点被弹出
编号为 4 的节点被弹出
编号为 1 的节点被弹出
编号为 5 的节点被弹出
最后剩余的节点编号为 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值