java单向环形链表解决约瑟夫问题



提示:以下是本篇文章正文内容,下面案例可供参考

一、约瑟夫问题是什么?

Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数
到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由
此产生一个出队编号的序列

思路:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结
点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直
到最后一个结点从链表中删除算法结束

二、代码实现

1.节点类

public class Node {
    int no;
    Node next;

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

    @Override
    public String toString() {
        return "Node{" +
                "no=" + no +
                '}';
    }
}

2.代码实现

代码如下(示例):

public class GroupLinkedList {
    Node first;
    int size;
    /**
     * 动态创建环形链表
     * @param size 链表的最大长度
     */
    public GroupLinkedList(int size) {

        if (size<1){
            System.out.println("输入的值不正确");
            return;
        }
        this.size=size;
        Node cur=null;//用来指向单向链表的最后一个节点
        for (int i = 1; i <size+1 ; i++) {
            Node node=new Node(i);
            if (i==1){
                first=node;//第一个有效节点
                first.next=first;//自己指向自己
                cur=first;//记录最后的有效节点
            }else {
                cur.next=node;//在最后节点后面添加节点
                node.next=first;//最后节点始终指向头结点形成环形
                cur=cur.next;//后移
            }
        }
    }

    public Node getLast(){
        if (first==null){
            throw new RuntimeException("链表为空");
        }
        Node temp=first;
        while (true){
            if (temp.next==first){
                return temp;
            }
            temp=temp.next;
        }
    }


    /**
     * 从第k个开始报数,每查m个就踢出一个
     * @param k 从第k个开始查
     * @param m 查m个
     */
    public void quan(int k,int m){
        //先判断k是否合理
        if (k<0||k>size){
            System.out.println("k值不合理");
            return;
        }
        /*
        因为是单向链表,如果想将数据剔除,
        就要找到剔除数据的前一个位置 通过getlast方法找到单向链表的最后一个节点
         */
        Node last=getLast();
        Node head=first;//创建辅助节点
        //如果从第k开始查,n那么现将last和head向后移动k-1个位置
        for (int i = 0; i <k-1 ; i++) {
            last=last.next;
            head=head.next;
        }

        //没查m个数就踢出一个节点,同时将last和head移动m-1个位置,
        //是的head指针指向被踢出节点,last节点指向head的前一个节点
        while (true){
            for (int i = 0; i <m-1 ; i++) {
                last=last.next;
                head=head.next;
            }
            System.out.println(head);//输出被删除节点
            last.next=last.next.next;//将节点剔除
            head=head.next;//让head指向下一个
            if (last==last.next){//如果只剩下,直接输出,
                            // 这里判断方式很多,此时head和last的next都指向自身,并且last等于head
                System.out.println(head);
                return;
            }
        }


    }
}

总结

在这里插入图片描述
思路就是,定义两个辅助指针a和b,a是从第几个开始查,b指向a身后的节点,在查数的时候两个指针同时查,让b指针删除a的指向,再让a指针的指向被删除后,a移动一位指向下一个有效节点,继续查,删除,直到最后只剩下一个节点的时候,直接删除就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值