约瑟夫问题算法的实现(代码实现) [Java][数据结构]

约瑟夫问题算法的实现(代码实现)

代码如下(我们将这个方法也定义到单向环形链表类中):
  • 其实我们的约瑟夫问题算法就相当于是一个特殊的删除单向环形链表中的结点的方法,所以我们就写到单向环形链表类中
/**
     * 实现约瑟夫问题解决算法(也就是按照指定规则出链表)
     * @param startNo 开始报数的小孩
     * @param countNum 报道多少后出链表
     * @param nums 一共有多少个结点(单向环形链表的长度)
     */
public void countBoy(int startNo, int countNum, int nums){
    //开始的时候先让helper指向first,然后使用helper进行遍历,直到遍历到最后一个元素位置
    Boy helper = first;
    //让我们的Boy指针链表中最后一个结点的位置
    while(helper.getNext() != first){
        helper = helper.getNext();
    }
    //当退出while循环的时候helper指向的就是first的前一个节点

    //小孩开始报数之前先让first和helper移动startNo - 1次,一定startNo - 1次之后first就指向来开始报数的位置(也就是第startNo个结点)
    for (int i = 0; i < startNo; i++) {
        helper = helper.getNext();
        first = first.getNext();
    }

    /*
        然后让我们的first结点移动m - 1次,一直重复执行,每次移动m - 1次之后first就指向了待删除结点,那么我们删除了该待删除结点之后
        first自然就会后移,就会指向我们删除位置的下一个节点,也就是开始报数的小孩,那么我们只需要再遍历m - 1次就可以又找到下一次的待删除位置了
        然后一直重复执行,直到执行到我们的单向环形链表中只有一个节点为止(当环形链表中只有一个节点的时候我们就要退出了,因为当链表中只有一个节点的时候
         这个时候first和helper指向了同一个节点,这个时候都指向了自己,通过上面的通用操作就删除不了这个最后一个节点了)
         */
    while(true) {
        if (first == helper){ //说明链表中只有一个节点了,那么此时就退出循环
            break;
        }
        for (int i = 0; i < countNum - 1; i++) {
            helper = helper.getNext();
            first = first.getNext();
        }
        System.out.printf("小孩%d出圈",first.getNo());
        System.out.println();
        //执行删除结点的操作
        first = first.getNext();
        helper.setNext(first);
    }
    System.out.printf("最后的小孩%d出圈",first.getNo());
}
我们是将约瑟夫算法封装到了单向环形链表类中(这里我们给出我们的单向环形链表类):
package com.ffyc.linkedlist;

/**
 * 单向环形链表类
 */
public class CircleSingleLinkedList {
    //创建一个first指针
    Boy first = null;

    //创建一个curBoy指针
    Boy curBoy = null;

    //创建一个长度为nums的单向环形链表
    public void addBoy(int nums){
        //做一个数据校验(也就是做一个合法性判断)
        if(nums < 1){
            System.out.println("nums的值不正确");
            return;
        }
        for (int i = 0; i < nums; i++) {
            //每次循环都创建一个新的结点,这个创建的结点就是准备插入到单向环形链表中的结点
            Boy boy = new Boy(i+1);
            //判断,如果是第一个节点,这个时候我们要先让其形成一个环,并且还要完成first指针和curBoy指针的指向,所以对于第一个节点我们要进行一个特殊处理
               //而特殊处理我们一般都是使用if语句来实现
            if(i == 0){
                first = boy; //让first指针指向第一个节点
                curBoy = boy; //让curBoy指向唯一存在的结点(我们的curBoy指针是要指向环形链表中的最后一个结点的,但是这个时候显然我们的链表中只有一个节点)
                first.setNext(first); //让first结点自己的next域指向自己,也就是自己和自己形成一个环
            }
            //对于不是第一个节点的情况
            curBoy.setNext(boy); //然curBoy指针指向的结点的next域指向boy(也就是准备添加的结点)
            curBoy = boy;  //完成curBoy的后移(因为curBoy的后面就是boy结点,所有我们直接使用curBoy指针执行boy结点就是完成了结点的后移操作)
            boy.setNext(first); //让boy结点的next域指向我们的单向环形链表中的第一个节点(也就是first指针指向的结点)
        }
    }

    //为了测试我们写的代码是否正确,我们写一个遍历此单向环形链表的方法来测试
    public void list(){
        //我们要遍历链表,那么肯定是要通过一个临时变量来遍历
        Boy temp = first;

        /*
        首先判断链表是否为空,如果链表为空则直接退出循环
         */
        if(temp == null){
            System.out.println("链表为空");
        }

        do{
            System.out.println(temp.getNo()+",");
            temp = temp.getNext();

        }while(temp != first);
    }

    /**
     * 实现约瑟夫问题解决算法(也就是按照指定规则出链表)
     * @param startNo 开始报数的小孩
     * @param countNum 报道多少后出链表
     * @param nums 一共有多少个结点(单向环形链表的长度)
     */
    public void countBoy(int startNo, int countNum, int nums){
        //开始的时候先让helper指向first,然后使用helper进行遍历,直到遍历到最后一个元素位置
        Boy helper = first;
        //让我们的Boy指针链表中最后一个结点的位置
        while(helper.getNext() != first){
            helper = helper.getNext();
        }
        //当退出while循环的时候helper指向的就是first的前一个节点

        //小孩开始报数之前先让first和helper移动startNo - 1次,一定startNo - 1次之后first就指向来开始报数的位置(也就是第startNo个结点)
        for (int i = 0; i < startNo; i++) {
            helper = helper.getNext();
            first = first.getNext();
        }

        /*
        然后让我们的first结点移动m - 1次,一直重复执行,每次移动m - 1次之后first就指向了待删除结点,那么我们删除了该待删除结点之后
        first自然就会后移,就会指向我们删除位置的下一个节点,也就是开始报数的小孩,那么我们只需要再遍历m - 1次就可以又找到下一次的待删除位置了
        然后一直重复执行,直到执行到我们的单向环形链表中只有一个节点为止(当环形链表中只有一个节点的时候我们就要退出了,因为当链表中只有一个节点的时候
         这个时候first和helper指向了同一个节点,这个时候都指向了自己,通过上面的通用操作就删除不了这个最后一个节点了)
         */
        while(true) {
            if (first == helper){ //说明链表中只有一个节点了,那么此时就退出循环
                break;
            }
            for (int i = 0; i < countNum - 1; i++) {
                helper = helper.getNext();
                first = first.getNext();
            }
            System.out.printf("小孩%d出圈",first.getNo());
            System.out.println();
            //执行删除结点的操作
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后的小孩%d出圈",first.getNo());
    }
}
给出我们的结点类:
/**
 * 单线环形链表的结点类
 */
class Boy{
    private int no;
    private Boy next;

    public Boy(){

    }

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

    public int getNo() {
        return no;
    }

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

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}
给出测试程序:
/**
     * 测试程序
     */
public static void main(String[] args) {
    CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    circleSingleLinkedList.addBoy(10);
    circleSingleLinkedList.list();
    circleSingleLinkedList.countBoy(2,2,10);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
做一门精致,全面详细的 java数据结构算法!!!让天下没有难学的数据结构,让天下没有难学的算法,不吹不黑,我们的讲师及其敬业,可以看到课程视频,课件,代码的录制撰写,都是在深夜,如此用心,其心可鉴,他不掉头发,谁掉头发???总之你知道的,不知道的,我们都讲,并且持续更新,走过路过,不要错过,不敢说是史上最全的课程,怕违反广告法,总而言之,言而总之,这门课你值得拥有,好吃不贵,对于你知识的渴求,我们管够管饱话不多说,牛不多吹,我们要讲的本门课程内容:稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、缀、后缀表达式、缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值