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

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