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