数据结构与算法从零开始------------------环形单链表(约瑟夫环)

一.回顾导学

在第一章时,我们简单提到了约瑟夫环(即丢手帕)的问题:

想必,这个游戏我们小时候或多或少都接触过,现在把它转换为代码实现,

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

二.约瑟夫环问题解决

2.1逻辑分析

1)根据用户的输入,生成一个小孩出圈的顺序,现在方便理解先假设为下列: 

n = 5 , 即有5个人

k = 1, 从第一个人开始报数

m = 2, 数2下

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

3) 当小孩报数时,让first 和 helper 指针同时移动  m  - 1 次

4) 这时就可以将first 指向的小孩节点 出圈 first = first .next helper.next = first   原来first 指向的节点就没有任何引用,就会被回收 出圈的顺序 2->4->1->5->3

注意: 小孩报数前,先让 first 和  helper 移动 k - 1次。因为我们假设的k = 1,即刚好不需要移动,在实际问题中还是要把这个问题考虑进去

2.2代码实现

package zy.circleList;

import java.util.Scanner;

/**
 * @author 十三
 * @version 1.0
 */
public class Josephu {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        CircleList list = new CircleList();
        list.circle(N);
//        list.show();
        list.count(1,3,N);

    }
}

//创建一个环形单链表
class CircleList {
    private Boy first = null;//创建第一个节点,目前没有编号

    //创建一个方法创建环形单链表
    public void circle(int num) {
        if (num < 1) {//为空的话不满足
            return;
        }
        Boy cur = null;//辅助指针帮助构建环形单链表
        for (int i = 1; i <= num; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                first.setNext(first);
                cur = first;
            } else {
                cur.setNext(boy);
                boy.setNext(first);
                cur = boy;
            }

        }
    }

    public void show() {
        if (first == null) {
            return;
        }
        Boy cur = first;
        while (true) {
            System.out.printf("小孩的编号%d\n", cur.getNo());

            if (cur.getNext() == first) {
                break;
            }
            cur = cur.getNext();

        }
    }

    /**
     * @param start 从第几个小孩开始
     * @param count 步长,
     * @param sums  总共有几个小孩
     */
    public void count(int start, int count, int sums) {
        if (first == null || start < 1 || start > sums) {
            return;
        }
        Boy temp = first;
        while (true) {
            if (temp.getNext() == first){//转一圈了
                break;
            }
            temp = temp.getNext();
        }
        //将小孩移到start
        for (int i = 0; i < start - 1; i++) {
            first = first.getNext();
            temp = temp.getNext();
        }
        while (true){
            if (temp == first){//环形链表只剩一个了
                break;
            }
            for (int i = 0; i < count - 1; i++) {
                first = first.getNext();
                temp = temp.getNext();
            }
            System.out.printf("小孩%d出圈\n",first.getNo());
            first = first.getNext();
            temp.setNext(first);
        }
        System.out.printf("%d留圈\n",first.getNo());
//        System.out.println(first.getNo());
    }


}


class Boy {
    private int no;//编号
    private Boy next;//指向下一个节点,默认为null

    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;
    }
}

可以注意到,我们直接初始化的从一开始,步长为3,有多少个小孩是我们自己输入的,其实如果需要的话,start 和count也可以自己接受参数,只是刚好我们pta上有一个约瑟夫环的问题,它的要求就是从1 开始,步长为3,

但好像pta上只允许有一个class(哎,欲哭无泪)

2.3结果验证

经验证正确~

三.小结

1)需要注意的是: 小孩报数前,记得先让 first 和  helper 移动 k - 1次。因为刚开始假设的k =1,写代码时忘记了移动了,想当然的直接进行出圈判断了,

2)还是导学里说的,代码思想来源于生活中的实际问题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值