数据结构与算法: 约瑟夫问题(丢手绢)

数据结构与算法: 约瑟夫(丢手绢)问题(单向链表,双向链表解决)

Tips: 采用java语言, 关注博主,底部附有完整代码

采用到的知识点:

  • 单向环形链表
  • 双向环形链表
  • 单向 / 双向 环形链表出圈

流程效果图:

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

什么是约瑟夫问题

约瑟夫问题好像小时候玩的丢手绢一样

先来说一下规则, 默认从第一个小朋友开始数,在第n个小朋友过后开始寻手绢, 第n个小朋友每k个人就丢下手绢,然后这个小朋友出圈

直到圈中只剩一人才结束

假设5个小朋友围坐一圈来丢手绢

例如这样:

image-20220511094933129

在第2个小朋友开始丢手绢

image-20220511095742989

那么就是元素三出圈

接着每2个元素依次出圈

约瑟夫问题

tips: 这里单向环形链表和双向环形链表思路有所不同, 所以结果也有所不同!

  • 单向环形链表 先创建好5个元素 然后从第n个孩子开始,每k次出圈 , (第一次从第n个孩子数也会出圈)
  • 双向环形链表 直接调用一个方法,并且传入需要“丢手绢”的元素,在开始出圈 (第一次从第n个孩子数也不会出圈)

单向环形链表

自定义链表 AnnularLinkedList.java:

public class AnnularLinkedList {

    //头节点
    private HeroNode3 head;

    /**
     * get头节点
     * @return 头节点
     */
    public HeroNode3 getHead() {
        return head;
    }

    /**
     * 创建元素
     * @param sum 需要创建的个数
     */
    public void createHeroNode(int sum) {
        if (sum < 1) {
            System.out.println("请正确输入创建个数");
            return;
        }

        HeroNode3 temp = null;

        for (int i = 1; i <= sum; i++) {
            HeroNode3 node3 = new HeroNode3(i, "张三" + i);

            if (i == 1) {
                //记录第一个位置
                head = node3;
                //辅助变量,记录第一个位置
                temp = node3;
                //第一个元素指向自己,构建成环形
                head.next = head;
            }

            //指向新的元素
            temp.next = node3;

            //新的元素指向第一个节点
            node3.next = head;

            //temp后移
            temp = temp.next;
        }
    }

    /**
     * @param start  从元素几开始数
     * @param number 每数几下出圈
     */
    public void count(int start, int number) {

        //获取链表长度
        int sum = size();
        System.out.println("sum为:" + start + "\t" + number + "\t" + sum);

        //start < 1  不能从 < 1 开始的数 数
        //sum < start  链表总数不能 < 开始的位置
        if (start < 1 || sum < start) {
            System.out.println("请正确输入");
            return;
        }

        HeroNode3 help = head;

        //将 help 移动到 head 后面
        while (help.next != head) {
            help = help.next;
        }

        //从第start位置开始循环
        for (int i = 0; i < start - 1; i++) {
            head = head.next;
            help = help.next;
        }

        //如果heap != head 那么就继续循环
        //当help == head 时,则退出循环
        while (help != head) {

            //每number下出圈
            for (int i = 0; i < number - 1; i++) {
                head = head.next;
                help = help.next;
            }
            //此时head则为要出圈的元素
            System.out.println("要出圈的勇士id为:" + head.id);

            //出圈操作
            head = head.next;
            help.next = head;
        }

        //最终help = head  那么help/head就是最后一个在圈中的元素
        System.out.println("留在圈中的勇士id为:" + help.id);
    }

    /**
     * 计算链表长度
     */
    public int size() {
        int length = 1;
        HeroNode3 temp = head;
        while (temp.next != head) {
            length++;
            temp = temp.next;
        }
        return length;
    }

    /**
     * 输出链表所有数据
     */
    public void show() {
        if (head == null) {
            System.out.println("链表为null,不能打印");
        }
        HeroNode3 temp = head;

        while (temp.next != null) {
            System.out.println("show:" + temp);

            //当最后一个元素 = 第一个元素时,表示链表当前在最后一个元素,停止循环
            if (temp.next == head) {
                break;
            }

            //temp后移
            temp = temp.next;
        }
    }
}

HeroNode3.java:

public class HeroNode3 {
    int id;
    String name;
    HeroNode3 next;

    public HeroNode3(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "HeroNode3{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

调用:

AnnularLinkedList linkedList = new AnnularLinkedList();

linkedList.createHeroNode(5);

System.out.println("测试的值为:"+linkedList.getHead().next.next.next.next.next.next);

linkedList.show();

linkedList.count(2, 2);

来看看运行结果:

gif_1

如果对单向链表或者约瑟夫问题陌生的,项目中还有我画的2张详细流程图 那就下载看看吧

image-20220511101018771

双向环形链表

AnnularDoubleLinkedList.java

/**
 * @author: android 超级兵
 * @create: 2022-05-05 11:53
 * TODO 约瑟夫问题 双向链表
 **/
public class AnnularDoubleLinkedList {

    public AnnularHeroNode headNode = new AnnularHeroNode();

    /**
     * TODO 约瑟夫问题 双向链表
     *
     * @param n    从第几个开始 (0 - list.size())
     * @param k    每次走几个 (k < list.size())
     * @param list 元素
     */
    public void josephusProblem(int n, int k, List<AnnularHeroNode> list) {

        if (list.size() < n) {
            System.out.printf("当前元素一共%d个,无法从第%d个元素开始", list.size(), n);
            return;
        }

        headNode = list.get(0);

        // TODO 先将每个元素链接起来
        for (int i = 0; i < list.size(); i++) {
            AnnularHeroNode node = list.get(i);

            // 下一个元素
            AnnularHeroNode nextNode;
            if (i != list.size() - 1) {
                nextNode = list.get(i + 1);
            } else {// 最后一个节点
                node.setLastNode(true);
                nextNode = list.get(0);
            }

            node.next = nextNode;
            nextNode.pre = node;
        }

        System.out.println("测试是否成功:");
        System.out.println(headNode.next.next.next.next);
        System.out.println(headNode.pre.pre.pre.pre);
        System.out.printf("最后一个节点为:%s\n", headNode.pre);
        System.out.printf("第一个节点为:%s\n", headNode);

        show();

        // 从第几个元素开始
        AnnularHeroNode tempNode = list.get(n);

        System.out.printf("从%s开始\n", tempNode);

        System.out.println();

        AnnularHeroNode currentNode = null;
        // 当前节点上一个节点
        AnnularHeroNode currentPreNode = null;


        while (true) {

            // 每次过n个人
            for (int i = 0; i < k; i++) {
                currentNode = tempNode.next;
                tempNode = tempNode.next;
            }
            System.out.printf("currentNode:%s\n", currentNode);

            // 移除当前选中的
            currentNode.pre.next = currentNode.next;
            currentNode.next.pre = currentNode.pre;

            // 当前位置的最后一个位置
            currentPreNode = currentNode.pre;

            // 如果当前位置后一个位置 = 当前位置 说明只有一个,则退出循环
            if (currentPreNode == currentNode) {
                break;
            }
        }

        System.out.printf("最终留在圈中的是  %s\n", currentNode);

    }


    private void show() {
        System.out.println("\n遍历: ======= start =======");

        AnnularHeroNode tempHeadNode = headNode;

        while (true) {
            System.out.println(tempHeadNode);

            if (tempHeadNode.isLastNode()) {
                break;
            }

            tempHeadNode = tempHeadNode.next;

        }
        System.out.println("遍历:  ======= end =======\n");
    }
}

AnnularHeroNode.java

/**
 * @author: android 超级兵
 * @create: 2022-05-05 11:54
 * TODO
 **/
public class AnnularHeroNode {
    private final int id;
    private final String name;
    private final String title;

    // 是否是最后一个节点
    private boolean isLastNode;

    // 下一个节点
    public AnnularHeroNode next;

    // 上一个节点
    public AnnularHeroNode pre;

    public AnnularHeroNode() {
        id = 0;
        name = "";
        title = "";
    }

    public int getId() {
        return id;
    }

    public AnnularHeroNode(int id, String name, String title) {
        this.id = id;
        this.name = name;
        this.title = title;
    }

    public boolean isLastNode() {
        return isLastNode;
    }

    public void setLastNode(boolean lastNode) {
        isLastNode = lastNode;
    }

    @Override
    public String toString() {
        return "AnnularHeroNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", title='" + title + '\'' +
                ", isLastNode='" + isLastNode + '\'' +
                '}';
    }
}

使用:

package a20220505环形双向列表;

import java.util.ArrayList;

/**
 * @author: android 超级兵
 * @create: 2022-05-05 12:07
 * TODO 约瑟夫问题 双向链表解决
 **/
public class Client {
    public static void main(String[] args) {

        AnnularDoubleLinkedList annularDoubleLinkedList = new AnnularDoubleLinkedList();

        AnnularHeroNode node0 = new AnnularHeroNode(0, "史进", "九纹龙");
        AnnularHeroNode node1 = new AnnularHeroNode(1, "鲁智深", "花和尚");
        AnnularHeroNode node2 = new AnnularHeroNode(2, "林冲", "豹子头");
        AnnularHeroNode node3 = new AnnularHeroNode(3, "宋万", "云里金刚");
        AnnularHeroNode node4 = new AnnularHeroNode(4, "吴用", "智多星");

        ArrayList<AnnularHeroNode> nodeArrayList = new ArrayList<>();
        nodeArrayList.add(node0);
        nodeArrayList.add(node1);
        nodeArrayList.add(node2);
        nodeArrayList.add(node3);
        nodeArrayList.add(node4);

        // 23401
        annularDoubleLinkedList.josephusProblem(1, 2, nodeArrayList);
    }
}

双向约瑟夫问题

完整代码

原创不易,您的点赞与关注就是对我最大的支持!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值