3.单向环形链表的应用场景
Josephu问题(丢手帕问题)
设编号为1,2,…,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示:
用一个不带头结点的循环链表来处理josephu问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除,算法结束。
【案例】
总人数n=7,从k=4开始数,第m=3个人出列。出圈顺序为:6,2,5,3,1,4,7。
完成游戏的思路:
1)在first前设计一个辅助指针helper,即将helper指针定位到first前面。
2)将first移动到startNo这个小孩。
3)开始数countNum个数。first和helper对应的移动。
4)删除first指向的这个小孩结点。
5)重复3)和4)。
代码实现
package chapter18.linkedList
import util.control.Breaks._
object Josephu {
def main(args: Array[String]): Unit = {
//创建BoyGame
var boyGame = new BoyGame
boyGame.addBoy(7)
boyGame.showBoy()
println("---------------开始游戏-------------------------")
boyGame.countBoy(4, 3, 7)
//测试2
// var boyGame2 = new BoyGame
// boyGame2.addBoy(70)
// boyGame2.showBoy()
// println("---------------开始游戏-------------------------")
// boyGame2.countBoy(41,30,70)
}
}
/**
* 核心类BoyGame
*/
class BoyGame {
//定义一个初始的头结点,这个first内容会改,first始终指向第一个孩子,始终被最后一个孩子指向。
// var first: Boy = null //方法1
var first: Boy = new Boy(-1) // 方法2
/**
* 添加小孩【形成一个单向环形链表】
*
* @param nums 表示共有几个小孩
*/
def addBoy(nums: Int): Unit = {
if (nums < 1) {
println("nums的值不正确")
return
}
//在形成环形链表时,需要一个辅助指针,用于表示当前的孩子。
var curBoy: Boy = null
for (no <- 1 to nums) {
//根据编号创建小孩对象
val boy = new Boy(no)
//如果是第一个小孩
if (no == 1) {
first = boy
first.next = boy //形成一个环形的链表
curBoy = first
} else {
curBoy.next = boy
boy.next = first
curBoy = boy
}
}
}
/**
* 编写方法countBoy,完成游戏
*
* @param startNo 从第几个人开始数
* @param countNum 数几下
* @param nums 总人数
*/
def countBoy(startNo: Int, countNum: Int, nums: Int): Unit = {
//对参数进行判断
if (first.next == null || startNo < 1 || startNo > nums) {
println("参数有误,请重新输入!")
return
}
/*
完成游戏的思路:
1)在first前设计一个辅助指针helper,即将helper指针定位到first前面。
2)将first移动到startNo这个小孩。
3)开始数countNum个数。first和helper对应的移动。
4)删除first指向的这个小孩结点。
5)重复3)和4)。
*/
var helper = first
//1.初始化helper:将helper指针定位到first前面
breakable {
while (true) {
if (helper.next == first) {
break()
}
helper = helper.next
}
}
//2.将first移动到startNo这个小孩(helper 对应移动)
for (i <- 0 until startNo - 1) {
first = first.next
helper = helper.next
}
breakable {
// 开始数数,按照给定的值,每数到一个小孩就出圈,直到环形链表只有一个结点结束。
while (true) {
if (helper == first) { //只有一个人
break()
}
//3.开始数countNum个数。first和helper对应的移动。
for (i <- 0 until countNum - 1) {
first = first.next
helper = helper.next
}
//输出出圈信息
printf("小孩%d出圈\n", first.no)
//将first指向的结点删除
first = first.next
helper.next = first
}
}
//当while结束后,只有一个人
printf("最后留在圈的小孩的编号为%d", first.no)
}
/**
* 遍历单向的环形链表
*/
def showBoy(): Unit = {
if (first.next == null) {
println("链表为空,没有任何小孩")
return
}
//因为first不能动,还是借助一个辅助指针完成遍历
var curBoy = first
breakable {
while (true) {
printf("小孩编号%d\n", curBoy.no)
if (curBoy.next == first) {
break()
}
curBoy = curBoy.next //curBoy后移
}
}
}
}
/**
* 定义节点Boy
*
* @param bNo 小孩的编号
*/
class Boy(bNo: Int) {
val no: Int = bNo
var next: Boy = null
}