链表(LinkedList)-单向环形链表

3.单向环形链表的应用场景

Josephu问题(丢手帕问题)

设编号为1,2,…,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示:
用一个不带头结点的循环链表来处理josephu问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除,算法结束。
【案例】
总人数n=7,从k=4开始数,第m=3个人出列。出圈顺序为:6,2,5,3,1,4,7。
完成游戏的思路:
1)在first前设计一个辅助指针helper,即将helper指针定位到first前面。
2)将first移动到startNo这个小孩。
3)开始数countNum个数。first和helper对应的移动。
4)删除first指向的这个小孩结点。
5)重复3)和4)。

代码实现

package chapter18.linkedList

import util.control.Breaks._

object Josephu {
  def main(args: Array[String]): Unit = {
    //创建BoyGame
    var boyGame = new BoyGame
    boyGame.addBoy(7)
    boyGame.showBoy()
    println("---------------开始游戏-------------------------")
    boyGame.countBoy(4, 3, 7)
    //测试2
    //    var boyGame2 = new BoyGame
    //    boyGame2.addBoy(70)
    //    boyGame2.showBoy()
    //    println("---------------开始游戏-------------------------")
    //    boyGame2.countBoy(41,30,70)
  }
}

/**
 * 核心类BoyGame
 */
class BoyGame {
  //定义一个初始的头结点,这个first内容会改,first始终指向第一个孩子,始终被最后一个孩子指向。
  //  var first: Boy = null //方法1
  var first: Boy = new Boy(-1) // 方法2

  /**
   * 添加小孩【形成一个单向环形链表】
   *
   * @param nums 表示共有几个小孩
   */
  def addBoy(nums: Int): Unit = {
    if (nums < 1) {
      println("nums的值不正确")
      return
    }
    //在形成环形链表时,需要一个辅助指针,用于表示当前的孩子。
    var curBoy: Boy = null
    for (no <- 1 to nums) {
      //根据编号创建小孩对象
      val boy = new Boy(no)
      //如果是第一个小孩
      if (no == 1) {
        first = boy
        first.next = boy //形成一个环形的链表
        curBoy = first
      } else {
        curBoy.next = boy
        boy.next = first
        curBoy = boy
      }
    }
  }

  /**
   * 编写方法countBoy,完成游戏
   *
   * @param startNo  从第几个人开始数
   * @param countNum 数几下
   * @param nums     总人数
   */
  def countBoy(startNo: Int, countNum: Int, nums: Int): Unit = {
    //对参数进行判断
    if (first.next == null || startNo < 1 || startNo > nums) {
      println("参数有误,请重新输入!")
      return
    }
    /*
    完成游戏的思路:
        1)在first前设计一个辅助指针helper,即将helper指针定位到first前面。
        2)将first移动到startNo这个小孩。
        3)开始数countNum个数。first和helper对应的移动。
        4)删除first指向的这个小孩结点。
        5)重复3)和4)。
     */
    var helper = first
    //1.初始化helper:将helper指针定位到first前面
    breakable {
      while (true) {
        if (helper.next == first) {
          break()
        }
        helper = helper.next
      }
    }
    //2.将first移动到startNo这个小孩(helper 对应移动)
    for (i <- 0 until startNo - 1) {
      first = first.next
      helper = helper.next
    }
    breakable {
      // 开始数数,按照给定的值,每数到一个小孩就出圈,直到环形链表只有一个结点结束。
      while (true) {
        if (helper == first) { //只有一个人
          break()
        }
        //3.开始数countNum个数。first和helper对应的移动。
        for (i <- 0 until countNum - 1) {
          first = first.next
          helper = helper.next
        }
        //输出出圈信息
        printf("小孩%d出圈\n", first.no)
        //将first指向的结点删除
        first = first.next
        helper.next = first
      }
    }
    //当while结束后,只有一个人
    printf("最后留在圈的小孩的编号为%d", first.no)
  }

  /**
   * 遍历单向的环形链表
   */
  def showBoy(): Unit = {
    if (first.next == null) {
      println("链表为空,没有任何小孩")
      return
    }
    //因为first不能动,还是借助一个辅助指针完成遍历
    var curBoy = first
    breakable {
      while (true) {
        printf("小孩编号%d\n", curBoy.no)
        if (curBoy.next == first) {
          break()
        }
        curBoy = curBoy.next //curBoy后移
      }
    }
  }

}

/**
 * 定义节点Boy
 *
 * @param bNo 小孩的编号
 */
class Boy(bNo: Int) {
  val no: Int = bNo
  var next: Boy = null
}

运行结果

链表(LinkedList)-单向环形链表_第1张图片

你可能感兴趣的:(Scala数据结构与算法)