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