单向环形链表-约瑟夫问题

一、单向环形链表

在这里插入图片描述
如图,链表的最后一个节点的next指向链表的第一个节点,形成一个环形链表。

二、单向环形链表使用场景-约瑟夫问题

josephu(约瑟夫、约瑟夫环)问题:
设编号为1,2,3…n的 n个人坐成一圈,编号为k(1<=k<=n )的人从1开始报数,数到m的那个人出列, 他的下一位继续从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
在这里插入图片描述

三、约瑟夫问题分析

用一个不带头节点的单向环形链表来实现:

  • 首先生成一个 带有 n 个节点的单向环形链表;
    无论几个节点,要保证当前节点创建完后,形成一个环,需要引入:cur变量和first变量。当创建第一个节点的时候,first指针和cur指针都指向这个节点,并且cur.next=first,之后每增加一个节点:
    cur.next = newNode;
    newNode.next = first;
    cur = cur.next;

  • 报数前准备就绪:
    由于是单向环形链表,只能通过next指针向后移动,因此,在某个节点出队时,想要要将链表继续连接成环,就需要出队列的那个节点的前一个节点作为辅助。
    1)Child first = null;
    Child helper = null;
    创建 first 变量,指向第一个节点,遍历整个单向环形链表,找到最后一个节点,创建辅助变量,指向最后一个节点(first前面的那个节点)。
    在这里插入图片描述
    在这里插入图片描述
    2) 由于编号为k的人第一个开始从1报数,在真正开始报数前,需要将first指针和helper指针向前移动 k-1次,让first指向编号为k的人(helper始终保持在first的前一个节点)。
    在这里插入图片描述

  • 循环遍历链表,开始报数并出队列
    1)编号为k的人开始从1报数,数到m的人出队列,
    在这里插入图片描述
    在这里插入图片描述
    first节点和helper节点均向后移动 m-1次,此时first指向要删除的节点。
    first = first.next;
    helper.next = first;
    2)当 helper == first,说明链表中已经剩最后一个节点。

四、代码实现

package linkedlist;

import org.junit.Test;

/**
 * 单向环形链表
 * @author 
 * @create 2020-07-17 8:28 PM
 */
public class SingleCircleLinkedList {

    @Test
    public void testCreateLinkedList(){
        int num = 5;
        Josephu josephu = new  Josephu();
        josephu.createCircle(num);
        josephu.showLinkedList();
        josephu.getOutOrder(5,1,2);
    }
}

class Josephu{

     private Child first = null;

/**
     * 创建一个含有 num 个节点的单向环形链表
     * 无论几个节点,要保证当前节点创建完后,形成一个环,需要引入两个变量:cur变量和first变量。当创建第一个节点的时候,first指针和cur指针都指向这个节点,并且cur.next=first,之后每增加一个节点:
     * cur.next = newNode;
     * newNode.next = first;
     * cur = cur.next;
     * @param num
     */
    public void createCircle(int num){
        if(num<1){
            System.out.println("num的值不正确");
            return ;
        }
        Child curNode = null;
        for(int n = 1;n<=num;n++){
            Child node = new Child(n);
            if(n==1){
                first = node;
                curNode = node; //或者 curNode = first;都行
                first.setNext(first);
            }else{
                curNode.setNext(node);
                node.setNext(first);
                curNode = node;
            }
        }
    }

    /**
     * 遍历链表的节点
     */
    public void showLinkedList(){
        if(first == null){
            System.out.println("链表为空~~~");
            return;
        }
        Child cur = first;
        while(true){
            System.out.println(cur);
            if(cur.getNext()==first){
                break;
            }
            cur = cur.getNext();
        }
    }

    /**
     * 计算小孩出队的顺序
     * @param count  一共有几个小孩
     * @param startNo 从编号为startNo 的小孩开始从 1 报数
     * @param step 数到 step 的这个小孩出队,然后从他的后一个小孩开始,继续游戏。
     */
    public void getOutOrder(int count,int startNo, int step){
        if(first==null || startNo<1 || startNo>count){
            System.out.println("参数输入有误~~~");
            return;
        }
        Child helper = first;

        //1.遍历链表,找到链表的最后一个节点,让helper指向它。

        while(true){
            if(helper.getNext()==first){
                break;
            }
            helper = helper.getNext();
        }
        //2.开始遍历前,让first 和 helper 节点分别向后移动 startNo-1 次。让first指向第一个开始报数的人。
        for(int i = 1;i<=startNo-1;i++){
            first = first.getNext();
            helper = helper.getNext();
        }

        //3.开始报数,数到step的人出队。循环整个链表,直到遍历完。
        while(true){
            if(first.getNext()==first){
               break;
            }
            for(int m = 1;m<step;m++){
                first = first.getNext();
                helper = helper.getNext();
            }
            System.out.printf("此次出队的是学号为%d的学生\n",first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后出队的是学号为%d的学生",first.getNo());
    }
}

class Child{
    private int no;
    private Child next;

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

    public int getNo() {
        return no;
    }

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

    public Child getNext() {
        return next;
    }

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

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

五、测试结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值