使用環形單向鏈表演示約瑟夫環問題Scala版本

思路

約瑟夫環問題 :
題目是 假設有N個小朋友按順序圍成一圈,每個小朋友都有一個編號,假設從第m個小朋友從1開始報數,報到k的小朋友出圈,從出圈的下一個小朋友繼續報數,重複上面的報數。直到所有的人出圈位置。
求出圈的小朋友的順序是什麼

解決方案:
我們使用的是單向的環形鏈表作爲數據結構
思路大致分爲

  1. 尋找指定開始節點
  2. 跳動指定的步長-1(需要獲取彈出節點的前一個節點,纔可以做刪除)
  3. 顯示彈出節點

代碼

package com.xipenhui.cn

import scala.collection.mutable.ArrayBuffer
import scala.util.control.Breaks._

/**
 * 約瑟夫環問題 :
 * 題目是  假設有N個小朋友按順序圍成一圈,每個小朋友都有一個編號,假設從第m個小朋友從1開始報數
 * 報到k的小朋友出圈,從出圈的下一個小朋友繼續報數,重複上面的報數。直到所有的人出圈位置。求出圈
 * 的小朋友的順序是什麼
 * 解決方案:
 *   我們使用的是單向的環形鏈表作爲數據結構
 */
object JosephuDemo {
  def main(args: Array[String]): Unit = {

    val josephu = new Josephu()
    val boys = josephu.addBoy(5)
    println("=======創建出來的boys是=======")
    josephu.show()
    println("===彈出小朋友===")
    josephu.popBoy(2,3,5)
  }
}

/**
 * 環形單向鏈表
 * 1. 添加小孩子
 * 2. 按照規則彈出小孩子
 * 3. 顯示環形隊列的值,做過程校驗
 */
class Josephu {
  var head = Boy(-1)

  /**
   *
   * @param num 添加小孩的個數
   */
  def addBoy(num: Int) {

    //參數校驗
    if (num < 1) {
      throw new RuntimeException("input num must big than 1")
    }

    var currBoy: Boy = null
    for (i <- 1 to num) {
      val boy = new Boy(i)
      if (i == 1) {
        head = boy
        head.next = head
        currBoy = boy
      } else {
        currBoy.next = boy
        boy.next = head
        currBoy = boy
      }
    }
  }


  /**
   * 按照給定的規律彈出元素
   * 1. 從開始數到第m個小朋友,拿到當前的小朋友
   * 2. 在當前節點進行後移,移動到k個小朋友的前一個節點(單向鏈表刪除需要使用前一個節點輔助)
   *   彈出當前小朋友,刪除當前節點,
   * 3. 這裏可以使用兩個for循環,
   *    第一次循環的長度爲鏈表長度
   *    第二次循環的長度爲移動的長度 k -1 (因爲要前一個節點)
   *
   * @param startNum 從第num個位置開發數數字
   * @param countNum 數數的長度爲len
   * @param len      環形鏈表的長度
   */
  def popBoy(startNum: Int, countNum: Int, len: Int): Unit = {
    //val resArr = ArrayBuffer[Boy]
    if (head.next == null || startNum < 1 || startNum > len) {
      println("輸入參數錯誤,請重新輸入")
      System.exit(0)

    }
    var currBoy: Boy = head
    //可以找到起始位置的節點
    breakable {
      while (currBoy.next != head) {
        if (currBoy.num == startNum) {
          break()
        } else {
          currBoy = currBoy.next
        }
      }
    }
    if (currBoy.next == head) throw new RuntimeException("can not find input element")

    //點名的次數爲 len次
    for (i <- 0 until len) {
      //從currBoy開始點名,點名的長度爲countNum,這裏是單向鏈表,要藉助前一個節點進行刪除
      for (j <- 1 until countNum-1) {
        currBoy = currBoy.next
      }
      //這裏 currBoy已經移動到需要移除節點的前一個節點
      print(s"output element is ${currBoy.next.num} \n")
      currBoy.next = currBoy.next.next
      currBoy = currBoy.next
    }
  }

  //顯示當前鏈表的內容
  def show(): Unit = {
    var currBoy = head
     while (currBoy.next != head){
       println(s"input boy's num is ${currBoy.num}")
       currBoy=currBoy.next
     }
   println(s"input boy's num is ${currBoy.num}")
  }
}

case class Boy(num: Int) {
  var next: Boy = null
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章