ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

约瑟夫问题的解决(后面附有完整实现代码)

2021-09-07 22:04:19  阅读:174  来源: 互联网

标签:结点 cur 附有 int 代码 约瑟夫 链表 next first


首先我们先明确什么是约瑟夫问题:

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

思路:

  1. 先创建一个环形链表,环形链表通过for循环来创建,关键要形成一个循环。
  2. 创建一个方法getCount(),计算出链表的有效数据的个数,通过循环遍历的方式获取链表的有效数据

        作用:创建一个数组来存储出队编号的序列,长度为有效数据的个数

     3.约瑟夫问题,首先传入两个参数k,m,k为开始报数的那个结点,m为出列的结点,所以我们需要先创建一个方法getCircleNode(),在初始报数时,获取到初始报数的那个结点.

     4. 该问题解决为:先找到对应开始报数的那个结点,然后开始循环(即报数)m次(实际为m-1次,可以在代码中浏览),找到对应的结点后,在存储出队编号序列的数组中添加该结点的no值,即编号(有循环遍历,我们一般需要借助一个辅助结点cur,cur指向当前遍历的结点)。添加完该节点后,把该节点删除(故我们也需要创建一个结点删除的方法),然后cur后移,继续循环遍历,重复以上步骤,最终可获得的结果。

前期准备:

1.创建环形链表

 /**
     * 创建一个环形链表
     *
     * @param num 传入要创建的结点的个数
     */
    public void addNode(int num) {
        CircleNode cur = null;
        for (int i = 1; i <= num; i++) {
            if (first == null) {//还没有节点时
                first = new CircleNode(i);
                cur = first;//指向当前节点
            } else {
                CircleNode circleNode = new CircleNode(i);//创建结点对象
                cur.next = circleNode;//让当前结点指向新创建的结点
                circleNode.next = first;//形成循环
                cur = circleNode;//向后递进
            }
        }
    }
class CircleNode {
    int no;//编号
    CircleNode next;

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

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

 2.创建一个方法getCount(),计算出链表的有效数据的个数,通过循环遍历的方式获取链表的有效数据        

  /**
     * 获取链表中有效数据的个数
     *
     * @return
     */
    public int getCount() {
        if (first == null) {
            throw new RuntimeException("该链表中没有数据");
        }
        CircleNode cur = first.next;//接用辅助结点
        int count;//记录有效数据的个数
        if (cur != first) {
            count = 2;//此时已经有两个数据
        } else {//此时只有一个数据
            count = 1;
            return count;
        }
        //如果程序运行到此,说明此时至少有两个数据
        while (cur.next != first) {
            cur = cur.next;//向后递进
            count++;//个数递进
        }
        return count;
    }

 3.约瑟夫问题,首先传入两个参数k,m,k为开始报数的那个结点,m为出列的结点,所以我们需要先创建一个方法getCircleNode(),在初始报数时,获取到初始报数的那个结点.

  /**
     * 查询对应序号的结点并返回
     *
     * @param k 查询的序号
     * @return 返回对应的结点
     */
    //这里在调用之前已经判断k是符合范围的,所以肯定能找到对应的结点,无需考虑找不到
    public CircleNode getCircleNode(int k) {
        CircleNode cur = first;//接用辅助结点
        while (true) {
            if (cur.no == k) {
                return cur;
            }
            cur = cur.next;
        }
    }

4.创建一个删除结点的方法

 /**
     * 删除环形队列中的结点
     *
     * @param circleNode 要删除的结点
     */
    public void deleteNode(CircleNode circleNode) {
        if (first == null) {
            System.out.println("该链表中没有数据");
            return;
        }
        CircleNode cur = first;//借助辅助指针
        if (first.no == circleNode.no) {//如果第一个数据就是要删除的结点
            first = first.next;//需要将first向后移动
            for (int i = 1; i < getCount(); i++) {
                cur = cur.next;//找出最后链表中的最后一个数据
            }
            cur.next = first;//让链表中的最后一个数据指向新的first,继续形成循环
            return;
        }
        boolean flag = false;//记录是否找到对应的结点
        for (int i = 0; i < getCount(); i++) {//对链表的所有有效数据进行遍历
            if (cur.next.no == circleNode.no) {//此时cur指向的是要删除结点的前一个结点(因为这是单向链表,删除要找到待删除结点的前一个结点)
                flag = true;//找到后标记
                break;
            }
            cur = cur.next;//向后递进
        }
        if (flag) {
            cur.next = cur.next.next;//进行指针的向后递进,没被指向的结点会被java的垃圾处理机制gc()处理,即被删除的结点
        }else{
            System.out.println("没有该结点,无法删除");
        }

    }

 最后实现约瑟夫问题的解决

 /**
     * 解决约瑟夫问题
     *
     * @param k 编号为k的人开始报数
     * @param m 报数到m的人出列
     * @return 返回一个出列编号的序列
     */
    public int[] Josephu(int k, int m) {
        if (k > getCount() || k <= 0) {//编号大于总的有效人数
            throw new RuntimeException("输入的数:" + k + "有误");
        }
        int[] result = new int[getCount()];//记录出列序号,长度为有效数据的个数
        int index = 0;//记录数组的指针
        CircleNode curNode = null;//记录当前的结点
        while (first.next != first) {//当链表中只剩下一个数据时,循环终止
            if (curNode == null) {//初次判断curNode结点是否赋值
                curNode = getCircleNode(k);//如果没赋值,就通过getCircleNode()方法获取到对应编号的数。
            }
            for (int i = 1; i < m; i++) {
                curNode = curNode.next;//循环m-1次,因为在下面的代码中已经移动到下一个开始计数的点了
            }

            deleteNode(curNode);//要删除已找到的这个结点
            //经过循环后获取到对应的出列的人
            result[index++] = curNode.no;
            curNode=curNode.next;//把curNode指针向后移动一位,故只需循环m-1次


        }
        result[index] = first.no;
        return result;

    }

即问题解决。

完整代码附上:

package com.liu.linkedlist;


import java.util.Arrays;

/**
 * @author liuweixin
 * @create 2021-09-07 19:00
 */
//约瑟夫问题:设编号为1、2、....n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,
// 数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,
// 由此产生一个出队编号的序列

//使用没有头结点的链表来实现
public class JosephuLinkedList {
    private CircleNode first;//创建一个first节点

    public static void main(String[] args) {
        JosephuLinkedList list = new JosephuLinkedList();
        list.addNode(5);
        list.show();
        System.out.println("约瑟夫问题的出列序号");
        System.out.println(Arrays.toString(list.Josephu(1,2)));
        list.Josephu(1, 2);
    }


    /**
     * 解决约瑟夫问题
     *
     * @param k 编号为k的人开始报数
     * @param m 报数到m的人出列
     * @return 返回一个出列编号的序列
     */
    public int[] Josephu(int k, int m) {
        if (k > getCount() || k <= 0) {//编号大于总的有效人数
            throw new RuntimeException("输入的数:" + k + "有误");
        }
        int[] result = new int[getCount()];//记录出列序号,长度为有效数据的个数
        int index = 0;//记录数组的指针
        CircleNode curNode = null;//记录当前的结点
        while (first.next != first) {//当链表中只剩下一个数据时,循环终止
            if (curNode == null) {//初次判断curNode结点是否赋值
                curNode = getCircleNode(k);//如果没赋值,就通过getCircleNode()方法获取到对应编号的数。
            }
            for (int i = 1; i < m; i++) {
                curNode = curNode.next;//循环m-1次,因为在下面的代码中已经移动到下一个开始计数的点了
            }

            deleteNode(curNode);//要删除已找到的这个结点
            //经过循环后获取到对应的出列的人
            result[index++] = curNode.no;
            curNode=curNode.next;//把curNode指针向后移动一位,故只需循环m-1次


        }
        result[index] = first.no;
        return result;

    }

    /**
     * 查询对应序号的结点并返回
     *
     * @param k 查询的序号
     * @return 返回对应的结点
     */
    //这里在调用之前已经判断k是符合范围的,所以肯定能找到对应的结点,无需考虑找不到
    public CircleNode getCircleNode(int k) {
        CircleNode cur = first;//接用辅助结点
        while (true) {
            if (cur.no == k) {
                return cur;
            }
            cur = cur.next;
        }
    }

    /**
     * 获取链表中有效数据的个数
     *
     * @return
     */
    public int getCount() {
        if (first == null) {
            throw new RuntimeException("该链表中没有数据");
        }
        CircleNode cur = first.next;//接用辅助结点
        int count;//记录有效数据的个数
        if (cur != first) {
            count = 2;//此时已经有两个数据
        } else {//此时只有一个数据
            count = 1;
            return count;
        }
        //如果程序运行到此,说明此时至少有两个数据
        while (cur.next != first) {
            cur = cur.next;//向后递进
            count++;//个数递进
        }
        return count;
    }

    /**
     * 遍历链表
     */
    public void show() {
        if (first == null) {
            System.out.println("该链表中没有数据");
            return;
        }
        System.out.println(first);//先打印首个结点
        CircleNode cur = first.next;//接用辅助结点
        while (cur != first) {//借用首个结点进行循环终止条件
            System.out.println(cur);
            cur = cur.next;
        }
    }

    /**
     * 删除环形队列中的结点
     *
     * @param circleNode 要删除的结点
     */
    public void deleteNode(CircleNode circleNode) {
        if (first == null) {
            System.out.println("该链表中没有数据");
            return;
        }
        CircleNode cur = first;//借助辅助指针
        if (first.no == circleNode.no) {//如果第一个数据就是要删除的结点
            first = first.next;//需要将first向后移动
            for (int i = 1; i < getCount(); i++) {
                cur = cur.next;//找出最后链表中的最后一个数据
            }
            cur.next = first;//让链表中的最后一个数据指向新的first,继续形成循环
            return;
        }
        boolean flag = false;//记录是否找到对应的结点
        for (int i = 0; i < getCount(); i++) {//对链表的所有有效数据进行遍历
            if (cur.next.no == circleNode.no) {//此时cur指向的是要删除结点的前一个结点(因为这是单向链表,删除要找到待删除结点的前一个结点)
                flag = true;//找到后标记
                break;
            }
            cur = cur.next;//向后递进
        }
        if (flag) {
            cur.next = cur.next.next;//进行指针的向后递进,没被指向的结点会被java的垃圾处理机制gc()处理,即被删除的结点
        }else{
            System.out.println("没有该结点,无法删除");
        }

    }

    /**
     * 创建一个环形链表
     *
     * @param num 传入要创建的结点的个数
     */
    public void addNode(int num) {
        CircleNode cur = null;
        for (int i = 1; i <= num; i++) {
            if (first == null) {//还没有节点时
                first = new CircleNode(i);
                cur = first;//指向当前节点
            } else {
                CircleNode circleNode = new CircleNode(i);//创建结点对象
                cur.next = circleNode;//让当前结点指向新创建的结点
                circleNode.next = first;//形成循环
                cur = circleNode;//向后递进
            }
        }
    }

}

class CircleNode {
    int no;//编号
    CircleNode next;

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

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

标签:结点,cur,附有,int,代码,约瑟夫,链表,next,first
来源: https://blog.csdn.net/weixin_54950870/article/details/120168129

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有