单向循环链表解决(详细思路+图解)约瑟夫问题
发布日期:2021-05-25 17:46:10 浏览次数:23 分类:精选文章

本文共 3832 字,大约阅读时间需要 12 分钟。

Josephu 问题解释与实现

什么是Josephu问题?

Josephu问题是一个经典的算法问题,通常用于模拟圆圈中的数字排除过程。在这个问题中,编号为1,2,…,n的n个人围坐在一圈,编号为k的第一个人从1开始报数,报数到m的那个人就被排出队列。然后从下一个剩下的下一个人继续从1开始报数,直到所有人被排除,直到最后的输出顺序生成。

Josephu问题的解决思路

在解决Josephu问题时,采用单向环形链表数据结构是一个非常高效的方法。整个过程可分为以下几个步骤:

  • 构建单向环形链表:将n个人依次加入链表,形成一个环。
  • 处理出队过程
    • 从指定的起始点开始报数,找到第m个人的位置并将其从链表中删除。
    • 从当前下一个位置重新开始报数,重复上述过程,直到所有人被排除。
  • 单向环形链表的实现步骤

    1. 创建一个环形链表

    public class CircleSingleLinkedList {
    private BoyNode first = null;
    public void add(int nums) {
    if (nums < 1) {
    System.out.println("数值输入有误,请输入大于1的数");
    return;
    }
    BoyNode curBoy = null;
    for (int i = 1; i <= nums; i++) {
    BoyNode boyNode = new BoyNode(i);
    if (i == 1) {
    first = boyNode;
    first.next = first; // 由于是环形结构,每个节点指向自己形成环
    curBoy = first;
    } else {
    curBoy.next = boyNode;
    boyNode.next = first;
    curBoy = boyNode;
    }
    }
    }
    public void list() {
    if (first == null) {
    System.out.println("单链表为空");
    return;
    }
    BoyNode curBoy = first;
    while (true) {
    System.out.printf("小孩的编号为 %d \n", curBoy.no);
    if (curBoy.next == first) {
    System.out.println("说明遍历完毕");
    break;
    }
    curBoy = curBoy.next;
    }
    }
    public void getBoy(int startNo, int countNum, int nums) {
    if (first == null || startNo < 1 || startNo > nums) {
    System.out.println("输入的数字不合法...");
    return;
    }
    // 需要将起始点调整为正确的位置
    BoyNode helper = first;
    // 找到最后一个节点
    while (true) {
    if (helper.next == first) {
    break;
    }
    helper = helper.next;
    }
    // 调整起始位置
    for (int j = 0; j < startNo - 1; j++) {
    first = first.next;
    helper = helper.next;
    }
    // 开始排除过程
    while (true) {
    if (helper == first) {
    break;
    }
    // 找到要排除的节点
    for (int j = 0; j < countNum - 1; j++) {
    first = first.next;
    helper = helper.next;
    }
    System.out.println("排除的小孩编号是:" + first.no);
    first = first.next;
    helper.next = first;
    }
    System.out.println("最后剩下的小孩编号是:" + first.no);
    }
    public static void main(String[] args) {
    CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    circleSingleLinkedList.add(5);
    circleSingleLinkedList.list();
    // 开始处理Josephu问题
    circleSingleLinkedList.getBoy(2, 2, 5);
    }
    }
    // BoyNode类
    class BoyNode {
    private int no;
    private BoyNode next;
    public BoyNode(int no) {
    this.no = no;
    }
    public int getNo() {
    return no;
    }
    public void setNo(int no) {
    this.no = no;
    }
    public BoyNode getNext() {
    return next;
    }
    public void setNext(BoyNode next) {
    this.next = next;
    }
    public String toString() {
    return "BoyNode{" + "no=" + no + ", next=" + next + '}';
    }
    }

    2. 处理Josephu问题

    • 参数说明startNo 初始起始节点编号,countNum 每次报数次数,nums 初始人数。
    • 工作流程
    • 准备阶段:调整起始点,使起始点正确位于指定的节点。
    • 排除过程
      • 使用两个指针firsthelper同时移动,找到要排除的节点。
      • 每找到一个节点后,继续从新起始位置开始下一次报数。

    整体流程示例

    • 初始化:创建一个环形链表,包含5个人。
    • 调用getBoy(2,2,5)
    • 起始位置设置为编号为2的节点。
    • 从2开始报数,报数3次,找到需要排除的节点(编号3)。
    • 现在,从下一个节点(4)开始,再次报数2次,排除编号5。
    • 最后一个节点(1)也被排除,整个过程结束。

    优化后的文章结构

  • Josephu问题介绍
    • 简要说明问题背景和用途。
  • 解决方案
    • 介绍实现的核心思路。
  • 单向环形链表的实现
    • 包括节点类和链表操作方法的详细解释。
  • 代码功能展示
    • 通过具体例子展示代码处理流程。
  • 结论与展望
    • 总结实现成果,展望可能的改进空间。
  • 代码运行结果示例

    • 执行后,终端会输出每个被排除的节点编号,最后显示剩余的最后一个节点编号。例如,在运行getBoy(2, 2, 5)时,输出如下:
    排除的小孩编号是:3
    排除的小孩编号是:5
    最后剩下的小孩编号是:1

    总结

    通过本次优化,实现了一个高效的单向环形链表解决方案,并验证了其在Josephu问题中的应用。这种方法不仅保持了代码的高效性,还提供了清晰的可读性,便于后续的扩展和优化。

    上一篇:java是使用数组模拟栈
    下一篇:java双链表的实现+模拟水浒英雄排行

    发表评论

    最新留言

    做的很好,不错不错
    [***.243.131.199]2025年04月17日 06时56分22秒