基于数据结构解决约瑟夫问题

  

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

本次课程设计便是在约瑟夫生死环问题的背景下,在一定的条件限制下(第九个人开始、数到的第十五个人结束)完成的。实现的主要方式是使用单向循环链表,再通过创建辅助指针进行链表的遍历和删除链结点完成出队操作。

关键词:约瑟夫生死环  单向循环链表  辅助指针  链结点的删除  

章  绪  论

1.1 课设主要研究问题

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

此上问题可归为约瑟夫生死环问题,约瑟夫问题,是一个计算机咳血数学中的问题,在计算机编程算法中,类似问题又称为约瑟夫环,又称“丢手绢问题”。

本次课设就是在约瑟夫问题基础上进行改编的:30 个旅客同乘一条船,因为严重超载,加上风大浪高,危险万 分,因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。 无奈,大家只得同意这种办法,并议定 30 个人围成一圈,由第一个人开始,依 次报数,数到第 9 人,便把他投入大海中,然后从他的下一个人数起,数到第 9 人,再将他投入大海,如此循环,直到剩下 15 个乘客为止。问哪些位置是将被扔下大海的位置?

第二章  课设实现过程

2.1 需求分析

2.1.1 存储结构

按照课程设计要求,首先确定使用的存储结构,本次课程设计选择的是单向循环链表的存储结构。

2.1.2 程序所能达成的内容

用循环链表模拟30个人围坐一圈,用链表中每一个结点代表一个人,从第一个结点开始,对该链表进行遍历,直到第9个结点,通过辅助指针删除该节点,完成出队即题目中要求的“扔下船”。重复上述过程,直至剩下15个乘客为止。

2.1.3 建立的模块及功能要求  

根据课程设计要求,可将要建立的功能模块分为3部分,分别为辅助指针的创建、循环链表的创立、循环链表的遍历及删除。

其中每个模块的功能要求如下

  1. 辅助指针的创立:能传回结点的编号即分辨是第几个人,辅助链表的创建、遍历和删除等功能
  2. 循环链表的创建:首先创建第一个结点,并形成环形,再借助辅助指针和辅助结点完成循环链表的创建
  3. 循环链表的遍历及删除:借助辅助指针完成循环链表的遍历和删除

2.2概要分析  

为了实现上述的功能要求,应用循环链表来模拟整个过程。

  1. 本次课程设计使用的是借助辅助指针完成循环链表的创建,首先先创一个boy类作为辅助指针,其中包括getNo()、setNo()、getNext()、setNext()方法,来分辨结点位于链表中的位置(即分辨这个人是第几个人)、实现链表的遍历以此完成链表的创建。

2、创建一个循环链表,采取的方法是先创建第一个节点,让first指向该节点,并形成环形;后面每当我们创建一个新的节点,就借助辅助指针,将该节点加入到已有的环形链表中。

3、循环链表的删除(即题目中的将选中的人扔下船)主要用first和helper两个指针完成,其中first指针在链表创建时创建,指向链表中的第一个节点,helper指针指向最后一个节点,当报数时,让first和helper指针通过while语句同时移动8次,就可通过辅助指针的操作完成出圈的操作。

第三章  详细设计

3.1详细设计

一、辅助指针类的创建

   class Boy{

     private int no;

     private Boy next;

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;

     }

}

     二、创建循环链表

  class CircleSingleLinkedList{

    private Boy first=null;

    //添加小孩节点,构成环形链表

    public void addBoy(int nums) {

        //nums做一个数据检验

        if (nums < 1) {

            System.out.println("nums的值不正确");

            return;

        }

        Boy curBoy = null;//辅助指针,构建环形链表

        //使用for循环创建链表

        for (int i = 1; i <= nums; i++) {

            //根据编号,创建小孩节点

            Boy boy = new Boy(i);

//如果是第一个小孩

            if (i == 1) {

                first = boy;

                first.setNext(first);//构成环

                curBoy = first;//让curBody指向第一个小孩

            } else {

                curBoy.setNext(boy);

                boy.setNext(first);

                curBoy = boy;

            }

        }

    }

    //遍历当前的环形链表

    public void showBoy(){

        //判断链表是否为空

        if(first==null) {

            System.out.println("没有任何小孩");

            return;

        }

        //因为first不能动,因此我们仍然使用一个辅助指针完成遍历

        Boy curBoy=first;

        while (true){

            //System.out.printf("小孩的编号%d/n",curBoy.getNo());

            if (curBoy.getNext()==first) {

                //说明已经遍历完毕

                break;

            }

            curBoy=curBoy.getNext();//curBoy后移

            }

        }

     三、创建循环链表

public  void countBoy(int startNo,int countNum,int nums){

    //对数据进行检验

    if(first==null||startNo<1||startNo>nums) {

        System.out.println("参数有误,请重新输入");

        return;

    }

    //创建辅助指针帮助小孩出圈

    Boy helper=first;

    //需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点

    while (true) {

        if (helper.getNext() == first) {//说明helper指向最后小孩节点

            break;

        }

        helper = helper.getNext();

    }

    //小孩报数前,先让first和helper移动k-1次

    for(int j=0;j<startNo-1;j++) {

        first = first.getNext();

        helper = helper.getNext();

    }

    //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈

    //这里是一个循环操作,知道圈中只有一个节点

    while (true) {

        if (helper == first) {//说明中只有一个节点

            break;

        }

        //

        //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈

        //这里是一个循环操作,知道圈中只有一个节点

        for (int j = 0; j < countNum - 1; j++) {

            first = first.getNext();

            helper = helper.getNext();

        }

        //这时first指向的节点,就是要出圈的小孩节点

        System.out.printf("小孩%d出圈\n", first.getNo());

        first = first.getNext();

        helper.setNext(first);

}

程序代码

//整体思路
//先创建环形链表
//出队列
public class Josepfu {
    public static void main(String  args[]){
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(30);// 加入30个小孩节点
        circleSingleLinkedList.showBoy();
        circleSingleLinkedList.countBoy(1, 9, 30); //
    }
}
//创建环形链表
class CircleSingleLinkedList{
    //创建first节点,当前没有编号
    private Boy first=null;
    //添加小孩节点,构成环形链表
    public void addBoy(int nums) {
        //nums做一个数据检验
        if (nums < 1) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null;//辅助指针,构建环形链表
        //使用for循环创建链表
        for (int i = 1; i <= nums; i++) {
            //根据编号,创建小孩节点
            Boy boy = new Boy(i);
//如果是第一个小孩
            if (i == 1) {
                first = boy;
                first.setNext(first);//构成环
                curBoy = first;//让curBody指向第一个小孩
            } else {
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }
    //遍历当前的环形链表
    public void showBoy(){
        //判断链表是否为空
        if(first==null) {
            System.out.println("没有任何小孩");
            return;
        }
        //因为first不能动,因此我们仍然使用一个辅助指针完成遍历
        Boy curBoy=first;
        while (true){
            //System.out.printf("小孩的编号%d/n",curBoy.getNo());
            if (curBoy.getNext()==first) {
                //说明已经遍历完毕
                break;
            }
            curBoy=curBoy.getNext();//curBoy后移
        }
    }
//根据用户的输入,计算出小孩出圈的顺序
    public  void countBoy(int startNo,int countNum,int nums){
        //对数据进行检验
        if(first==null||startNo<1||startNo>nums) {
            System.out.println("参数有误,请重新输入");
            return;
        }
        //创建辅助指针帮助小孩出圈
        Boy helper=first;
        int sum=0;
        //需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点
        while (true) {
            if (helper.getNext() == first) {//说明helper指向最后小孩节点
                break;
            }
            helper = helper.getNext();
        }
        //小孩报数前,先让first和helper移动k-1次
        for(int j=0;j<startNo-1;j++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈
        //这里是一个循环操作,知道圈中只有一个节点
        while (true) {
            if (helper == first) {//说明中只有一个节点
                break;
            }
            //
            //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈
            //这里是一个循环操作,知道圈中只有一个节点
            for (int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            sum++;
            //这时first指向的节点,就是要出圈的小孩节点
            if(sum<16) {
                System.out.printf("小孩%d出圈\n", first.getNo());
                first = first.getNext();
                helper.setNext(first);
            }
        }
    }
}
class Boy{
    private int no;
    private Boy next;
    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;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_54570435/article/details/130390753