问题阐述
据说著名犹太历史学家 Josephus有过以下故事,在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲在一个洞中,39个犹太人决定宁愿死也不要被敌人抓住,于是决定了一个自杀方式,41人排成一个圆圈,由第1人开始报数,每报数到第3人,该人就必须自杀,然后由下一人继续报数,知道所有人都自杀为止,Josephus与朋友并不想这么死去,他们商量后,选择了两个位置,最后存活了下来。
目前网上主流的解决方法是用循环链表,Up主采用类似的思路,由JavaScript代码实现。
核心思路
报数索引head:把人群看成一个数组,每次开始报数的数组索引
算法:每次报数索引head +=2,如果head指向仍在数组范围内,就从数组中剔除该数字,表明该顺序的人自杀;如果报数索引超过了数组的范围,就以该索引为界分割为两个数组,然后由后向前连接两数组成一个新的数组,该过程其实是实现循环列表,并设置head为0,重新执行,直到数组中只剩2人,即存活的两人所站的顺序。
let arr = []
for(let i = 0; i <= 40; i++){
arr[i] = i+1
}
let head = 0
while(arr.length >=3){
head +=2
if(head < arr.length-1){
arr.splice(head,1)
if(head === arr.length-1) {
let arr1 = []
let arr2 = []
arr1 = [arr[arr.length-1]]
arr2 = arr.slice(0,head)
arr = arr1.concat(arr2)
head = 0
}
}else if(head > arr.length-1){
let arr1 = []
let arr2 = []
head -=2
arr1 = arr.slice(head,arr.length)
arr2 = arr.slice(0,head)
arr = arr1.concat(arr2)
head = 0
} else if(head === arr.length -1){
arr.splice(head,1)
head = 0
}
}
console.log("最后活下来的人排序:",arr[0],arr[1]) //最后活下来的人排序: 16 31