据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。所以问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
下面使用 JavaScript 实现的一个循环双向链表来解决这个问题。其实单向的就可以,或者用别的计算方法就可以解决约瑟夫问题,写这个代码主要是想使用 JavaScript 实现一个链表的数据结构并解决实际问题。
/**
* JavaScript 循环双向链表解决约瑟夫问题
*/
function Node (element) {
this.element = element;
this.next = null;
this.prev = null;
}
function LinkList () {
this.head = new Node('head');
this.head.next = this.head;
this.head.prev = this.head;
this.last = this.head;
}
LinkList.prototype.find = function (item) {
let currNode = this.head;
while (currNode.next.element!== 'head' && currNode.element !== item) {
currNode = currNode.next;
}
if (currNode === this.last && currNode.element !== item) {
return null;
}
return currNode;
}
LinkList.prototype.insert = function (element, pos) {
const posNode = pos instanceof Node ? pos : this.find(pos);
const newNode = new Node(element);
newNode.next = posNode.next;
newNode.prev = posNode;
posNode.next.prev = newNode;
posNode.next = newNode;
if (posNode === this.last) {
this.last = newNode;
this.head.prev = newNode;
}
}
LinkList.prototype.append = function (element) {
if (element instanceof Array) {
element.forEach(v => {
this.append(v);
})
} else {
this.insert(element, this.last);
}
}
LinkList.prototype.remove = function (element) {
const currNode = this.find(element);
if (currNode) {
currNode.prev.next = currNode.next;
currNode.next.prev = currNode.prev;
if (currNode === this.last) {
this.last = currNode.prev;
}
}
}
LinkList.prototype.display = function () {
let currNode = this.head;
while (currNode.next.element !== 'head') {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
/**
* 约瑟夫问题
* @param {number} total 参与的总人数
* @param {number} killIndex 数到几杀人
* @param {number} livecnt 需要存活多少人
*/
function survive(total, killIndex, livecnt) {
const llist = new LinkList();
const initData = [];
// 初始化数组
for (let i = 0; i < total; i ++) {
initData[i] = i + 1;
}
llist.append(initData);
// 杀人
let currNode = llist.head;
let index = 1;
while (total > livecnt) {
if (currNode !== llist.head) {
if (index % killIndex === 0) {
llist.remove(currNode.element);
total --;
}
index ++;
}
currNode = currNode.next;
}
// 展示存活的人
llist.display();
}
// 答案是16和31能存活
survive(41, 3, 2);