单向环形链表-解决Josephu问题

Josephu, 约瑟夫, 约瑟夫环问题
设编号为1~n的n个人围坐到一圈, 约定编号为k(1<=k<=n)的人开始从1开始报数, 数到m的那个人出列, 它的下一位又开始从1开始数, 一次类推, 直到所有人都出列, 得到一个出列的序列.

分析
利用一个不带头结点的环形链表

创建一个不带头结点的环形链表
1.先创建一个节点, 并让frist指针指向该节点, 并形成环形
2.后面每新建一个新的节点, 就把该节点加入到已有的环形链表中去.

遍历环形链表
1.先让一个辅助指针cur指向第一个节点frist,
2.然后使用一个while循环遍历该环形链表,
3.直到当cur.next= frist结束.

解决问题的算法
1.需要创建一个last作为辅助变量, 实现指向最后一个节点, 当last== frist时就可以判断出是剩下最后一个
2.报数之前应先让frist和last同时移动k-1次, 到达一开始的报数位置
3.报数时, 让frist和last同时移动m-1次
4.这时frist指向的节点就可以出圈
5.frist指向下一个再开始报数, 出圈的这个节点没有被引用, 就会被回收

public class Josephu {
    public static void main(String[] args) {
        Ringsinglerlinkedlist rsll = new Ringsinglerlinkedlist();
        rsll.create(5);
        rsll.show();
        rsll.showJosephu(1,2,5);
        //rsll.show();
    }
}
class Ringsinglerlinkedlist {
    private Boy frist = null;


    //传入一个int值, 创建一个int值个数的单向环形链表
    public void create(int count){
        if (count < 1){
            System.out.println("节点的个数直少有一个");
            return;
        }
        Boy cur = null;
        for (int i = 1; i <= count; i++){
            Boy boy = new Boy(i);
            if (i == 1){
                frist = boy;
                frist.next = frist;
                cur = frist;
            }else{
                cur.next = boy;
                boy.next = frist;
                cur = boy;
            }
        }
    }
    //遍历单向环形链表并打印
    public void show(){
        if (frist == null){
            System.out.println("这个单向环形链表为空");
            return;
        }
        Boy cur = frist;
        while(true){
            System.out.print(cur);
            if (cur.next == frist){
                break;
            }
            cur = cur.next;
        }
        System.out.println();
    }
    //约瑟夫问题的出列序列
    public void showJosephu(int k, int m, int count){
        if (k < 1 || k > count || frist == null){
            System.out.println("链表是空或者开始位置有误~ ");
        }
        Boy last = frist;
        while(true){
            if (last.next == frist){
                break;
            }
            last = last.next;
        }
        for (int i = 1; i<= k-1; i++){
            frist = frist.next;
            last = last.next;
        }
        while(true){
            if (last == frist){
                System.out.println(frist);
                break;
            }
            for (int j = 1; j <= m-1; j++){
                frist = frist.next;
                last = last.next;
            }
            System.out.println(frist);
            last.next = frist.next;
            frist = frist.next;
        }
    }
}
class Boy{
    public int number;
    public Boy next;

    public Boy(int number){
        this.number = number;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "number=" + number +
                '}';
    }
}
package com.itdong.huiwei;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class YueSeFuHuan {
    public static void main(String[] args) {
        //获取n值,代表人数
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入人数:");
        int n = scanner.nextInt();
        //获取k值,代表从哪里开始
        System.out.println("请输入开始开始游戏编号(1<=k<=n): ");
        int k = scanner.nextInt();
        //获取m值,代表游戏报数的数字
        System.out.println("请输入报数的数字: ");
        int m = scanner.nextInt();

        //根据游戏人数n初始化环形链表
        Boy boy = create(n);
        //打印
        show(boy);
        //得到游戏结果list
        List<Boy> play = play(boy, k, m);
        //打印
        System.out.println(play);
    }

    /**
     * 根据头节点和开始k和计数m得到游戏的结果list
     * @param head
     * @param k
     * @param m
     * @return
     */
    public static List<Boy> play(Boy head, int k, int m){
        ArrayList<Boy> boys = new ArrayList<>();
        Boy first = head;
        Boy last = null;
        Boy cur = first;
        while(true){
            if (cur.getNext() == first) {
                last = cur;
                break;
            }
            cur = cur.getNext();
        }
        //first和last移动k-1次,使得first到达开始计数的位置
        for (int i = 1; i <= k-1; i++) {
            first = first.getNext();
            last = last.getNext();
        }
        //得到出环list
        while(true){
            if (first == last){
                boys.add(first);
                break;
            }
            for (int i = 1; i <= m-1; i++) {
                first = first.getNext();
                last = last.getNext();
            }
            //出环
            boys.add(first);
            last.setNext(first.getNext());
            first = first.getNext();
        }
        return boys;
    }
    /**
     * 根据头节点遍历环形链表
     * @param boy
     */
    public static void show(Boy boy){
        Boy cur = boy;
        Boy first = boy;
        do{
            System.out.println(cur);
            cur = cur.getNext();
        }while(cur != first);
    }

    /**
     * 根据n创建环形链表并返回头节点
     * @param n
     * @return
     */
    public static Boy create(int n){
        Boy frist = null;
        Boy  cur = null;
        for (int i = 0; i < n; i++) {
            Boy boy = new Boy();
            boy.setId(i+1);
            if (i == 0){
                frist = boy;
                cur = boy;
            }else if (i == (n-1)){
                cur.setNext(boy);
                boy.setNext(frist);
            } else{
                cur.setNext(boy);
                cur = boy;
            }
        }
        return frist;
    }
}

class Boy{
    private int id;
    private Boy next;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boy getNext() {
        return next;
    }

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

    @Override
    public String toString() {
        return "Boy{" +
                "id=" + id +
                '}';
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值