703、scala單向循環列表解決-Josephu (雅瑟夫)問題

Josephu 問題爲:
	設編號爲 1,2,… n 的 n 個人圍坐一圈,約定編號爲 k(1<=k<=n)的人從 1 開始報數,
數到 m 的那個人出列,它的下一位又從 1 開始報數,數到 m 的那個人又出列,依次類推,
直到所有人出列爲止,由此產生一個出隊編號的序列。

提示:
	用一個不帶頭結點的循環鏈表來處理 Josephu 問題:先構成一個有 n 個結點的單循環鏈表,
然後由 k 結點起從 1 開始計數,計到 m 時,對應結點從鏈表中刪除,然後再從被刪除結點
的下一個結點又從 1 開始計數,直到最後一個結點從鏈表中刪除算法結束。

帶頭節點的鏈表處理比較麻煩

代碼實現:

/**
  * 問題:1 to 7 個小朋友,從座標4的位置開始數2個座標出局一個小朋友,並返回出局節點的下個節點,爲下一輪遊戲做準備,
  * 依次出局的小朋友順序爲?
  * 答案:7-->3-->6-->4-->2-->5-->1-->
  */
object BoyGame {
  def main(args: Array[String]): Unit = {
    //1. 初始化遊戲對象
    val game = new BoyGame(7)
    game.showData()
    println()
    //初始化遊戲,並開始
    game.initGame(4,3)

  }
}

class BoyGame {
  var size: Int = _
  var first: Node = _
  var cur: Node = _ //表示隊列指針,輔助隊列初始化

  /**
    * 輔助構造器
    *
    * @param newSize 數據個數
    * @param start   開始座標
    * @param len     步長
    */
  def this(newSize: Int) {
    this
    size = newSize
    createLoopLink //初始化隊列
  }

  /**
    * 初始化隊列
    */
  def createLoopLink(): Unit = {
    for (i <- 1 to size) {
      if (i == 1) {
        first = new Node
        first.value = i
        first.next = first
        cur = first
      } else {
        val node = new Node
        node.value = i
        node.next = cur.next
        cur.next = node
        cur = node
      }
    }
  }

  /**
    * 根據座標取節點
    * @param index
    * @return
    */
  def getNode(index:Int):Node={
     var cur = first
     for(i <- 0 until  index){
        cur = cur.next
     }
    cur
  }

  def initGame(index:Int, len: Int): Unit ={
    //1、開始節點
    val startNode = getNode(index)
    //2、開始遊戲,注意這裏的步長
        //因爲單向鏈表需要待移除節點前一個節點操作,所以步長 -1
        //並且這裏是由開始節點在內往後數,所以需要-1
        //綜合這裏減2
    startGame(startNode,len)
  }
  /**
    * 遊戲開始
    * @param node 開始節點
    * @param len  步長
    */
  def startGame(node: Node, len: Int): Unit = {
    if (size < 1) {
      return
    } else {
      //移除節點
      val nextNode = remove(node, len)
      //遞歸處理
      startGame(nextNode, len)
    }
  }

  /**
    * 移除節點
    * @param node 開始節點
    * @param len  步長
    * @return     下個開始節點
    */
  def remove(node: Node, len: Int): Node = {
    var cur = node
    //1、查到待刪除節點前一個節點,所以-1,同理從開始節點算步長,所以又需要-1
    for (i <- 0 until len-2) {
      cur = cur.next
    }
    //2、移除節點
    val delNode = cur.next
    cur.next = delNode.next
    delNode.next = null
    size -= 1
    print(delNode.value +"-->")
    //3、返回下個開始節點
    cur.next
  }


  def showData(): Unit = {
    var tmp = first
    for (i <- 0 until size) {
      print(tmp.value + "-->")
      tmp = tmp.next
    }
  }
}


代碼擴展:帶頭結點的單向循環鏈表解決

package com.cjy.chapter19.linktable


/** 問題:1 to 7 個小朋友,從座標4的位置開始數2個座標出局一個小朋友,並返回出局節點的下個節點,爲下一輪遊戲做準備,
  *     依次出局的小朋友順序爲?
  * 答案:7-->3-->6-->4-->2-->5-->1-->
  */

object LoopLink {
  def main(args: Array[String]): Unit = {
    val link = new LoopLink()
    (1 to 7).foreach(link.addLastNode(_))
    link.showData()
    println()
    //座標4開始,步長2,頭結點開始數
    link.game(4, 2, link.head)
    link.showData()
  }
}

/**
  * 單向環形鏈表
  */
class LoopLink {
  //頭結點
  val head = new Node
  head.next = head
  //長度
  private var length = 0

  //添加在首位
  def addFirstNode(value: Int): Unit = {
    add(value, 0, 0, head)
  }

  //末尾添加
  def addLastNode(value: Int): Unit = {
    add(value, length, 0, head)
  }
  /**
    * 插入結點
    *
    * @param value 結點值
    * @param index 座標
    * @param len   當前所在深度
    * @param cur   當前所在節點
    */
  def add(value: Int, index: Int, len: Int, cur: Node): Unit = {
    if (index > length) {
      println("現有 " + length + "個元素" + ",無法插入座標" + index)
      return
    }
    if (index == len) {
      val newNode = new Node //創建新節點
      newNode.value = value //賦值
      newNode.next = cur.next //掛載到鏈表上
      cur.next = newNode
      length += 1
    } else {
      add(value, index, len + 1, cur.next) //遞歸調用-處理下一個節點
    }
  }

  /**
    * 根據座標查詢數據
    *
    * @param index
    * @return
    */
  def get(index: Int): Int = {
    get(index, 0, head).value
  }

  def getNode(index: Int): Node = {
    get(index, 0, head)
  }

  /**
    * 根據座標查詢數據
    *
    * @param index 座標
    * @param len   當前深度
    * @param cur   當前節點
    * @return
    */
  def get(index: Int, len: Int, cur: Node): Node = {
    if (index > length - 1) {
      throw new IllegalArgumentException("查詢座標存在")
    }
    if (index == len) {
      //以頭結點開始,所以下個節點纔是數據節點
      return cur.next
    } else {
      get(index, len + 1, cur.next)
    }
  }

  /**
    * 根據座標刪除節點
    *
    * @param index
    * @return
    */
  def remove(index: Int): Int = {
    remove(index, 0, head)
  }

  /**
    * 根據座標刪除節點
    *
    * @param index 座標
    * @param len   深度
    * @param cur   當前節點
    * @return 刪除值
    */
  def remove(index: Int, len: Int, cur: Node): Int = {
    //座標大於當前鏈表長度
    if (index > length - 1) {
      throw new IllegalArgumentException("刪除座標存在")
    }
    //待刪除節點位置
    if (index == len) {
      val delNode = cur.next //待刪除節點
      cur.next = delNode.next //重新掛載節點
      delNode.next = null //節點刪除
      length -= 1
      delNode.value
    } else {
      //尋找下一個節點
      remove(index, len + 1, cur.next)
    }
  }

  def size() = {
    length
  }

  def isEmpty() = {
    length == 0
  }

  def showData(): Unit = {
    var curNode = head.next
    for (i <- 0 to size() - 1) {
      if (curNode.value != 0) {
        print(curNode.value + "-->")
      }
      curNode = curNode.next
    }
  }

  /**
    * 設置遊戲規則
    * @param index  開始節點座標
    * @param len    步長
    * @param cur    當前開始節點
    */
  def game(index: Int, len: Int, cur: Node): Unit = {
    val firstNode = getNode(index) //從哪個節點開始
    gameStart(len - 1, firstNode)
  }

  /**
    * 開始遊戲
    * @param len  步長
    * @param cur  開始節點
    */
  def gameStart(len: Int, cur: Node): Unit = {
    //當隊列中剩下一個節點是,不在處理
    if (length <= 1) {
      return
    } else {
      //節點出局,返回下個開始節點
      val fist = removeAndGet(len, cur)
      //遞歸繼續處理
      gameStart(len, fist)
    }
  }

  /**
    * 節點出局
    * @param len  步長
    * @param cur  開始節點
    * @return 下個開始節點
    */
  def removeAndGet(len: Int, cur: Node): Node = {

    var node: Node = cur
    //1. 根據步長查找待出局節點的前一個節點
    for (i <- 0 until len) {
      //經過頭結點,則需要找下下個節點
      if (node.value == 0) {
        node = node.next.next
      }else{
        node = node.next
      }
    }
    //2. 出局數據時-根據待出局節點的前一個節點來操作
                            // 1、所以下一個節點不能是頭節點
                            // 2、待刪除節點的前一個節點爲頭結點,則說明實際步長沒有走;
                            //問題分析:假設當前只有head-2-3-4,當前開始節點爲4,尋找步長爲一的下個節點,則next爲head,其實應該是2的所以需要判斷當前節點不能是頭結點
    if (node.next.value == 0 || node.value == 0) {
        node = node.next
    }
    //根據待出局節點的前一個節點
    val del = node.next
    node.next = del.next
    del.next = null
    //總數減1
    length -= 1
    if (del.value != 0) {
      print(del.value + "-->")
    }
//當前節點下個節點是頭結點,則需要將指針指向頭結點,因爲頭結點需要過濾
//    if (node.next.value == 0) {
//      node = node.next
//    }
    //返回出局節點的下個節點,爲下一輪遊戲的開始節點
    node.next
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章