我的目标是编写一个indexOf
函数,该函数使用二进制搜索来查找数组中所需元素的索引。
我的问题是,编写一个递归解决方案,我总是吹掉调用堆栈 - 即使在绝对很小的数组中。我的期望是,即使是非尾调用优化语言的二进制搜索也不应该为任何不是 monstrous 大的数组吹掉调用堆栈。
在英文/伪代码中,我的想法是
middle
为数组的长度除以2,向下舍入currentValue
成为数组中间的任何内容(又名arrayToSearch[middle]
middle
以下是我在JS中写的方式:
function indexOf(soughtValue, arrayToSearch) {
let middle = Math.floor(arrayToSearch.length / 2);
let currentValue = arrayToSearch[middle];
if (soughtValue === currentValue) {
return middle;
} else if (soughtValue > currentValue) {
return indexOf(soughtValue, arrayToSearch.slice(middle, arrayToSearch.length));
} else if (soughtValue < currentValue) {
return indexOf(soughtValue, arrayToSearch.slice(0, middle));
}
}
let x = indexOf("Sarah", ["Jennifer", "Sarah", "David", "Jon"]);
console.log(x); // expecting 1
然而正如我之前所说,堆栈溢出。
我仔细阅读了我的教科书对二分查找的解释,然后我查阅了哈佛Intro CS关于算法的讲座。我很自信我理解它的想法,我相当自信的JavaScript正在做我期望的事情。我理解堆栈溢出的概念以及递归算法可能触发的原因。
然而,一遍又一遍地重读我的代码,我无法发现我搞砸了这一点并犯了一个逻辑错误。
如果有人能够启发我,我会非常感激,因为我在这里已经完全耗尽了自己的心理资源而且空洞。
答案 0 :(得分:0)
根据我上面的评论,这是一个完整的工作示例。重大变化:
start
和end
索引传递原始数组。这是确保返回的索引是原始数组索引的一种方法。-1
的基本案例(就像大多数内置indexOf
实现的行为一样)。当然,根据我的第一条评论,我开始使用排序数组,因为二进制搜索仅适用于已排序的输入。
function indexOf(soughtValue, arrayToSearch, start=0, end=(arrayToSearch.length-1)) {
// base case for an empty array or otherwise failing to find the element
if (start > end) {
return -1;
}
let middle = start + Math.floor((end - start) / 2);
let currentValue = arrayToSearch[middle];
if (soughtValue === currentValue) {
return middle;
} else if (soughtValue > currentValue) {
return indexOf(soughtValue, arrayToSearch, middle + 1, end);
} else if (soughtValue < currentValue) {
return indexOf(soughtValue, arrayToSearch, start, middle - 1);
}
}
let input = ["David", "Jennifer", "Jon", "Sarah"];
console.log(indexOf("David", input)); // 0
console.log(indexOf("Jennifer", input)); // 1
console.log(indexOf("Jon", input)); // 2
console.log(indexOf("Sarah", input)); // 3
console.log(indexOf("NOT THERE", input)); // -1
&#13;
<强>更新强>
仅供参考,这样的函数可以很容易地迭代编写而不是递归。在这种情况下,我认为我更喜欢迭代版本:
function indexOf(soughtValue, arrayToSearch) {
let start = 0;
let end = arrayToSearch.length - 1;
while (start <= end) {
let middle = start + Math.floor((end-start) / 2);
let currentValue = arrayToSearch[middle];
if (soughtValue > currentValue) {
start = middle + 1;
} else if (soughtValue < currentValue) {
end = middle - 1;
} else {
return middle;
}
}
// not found
return -1;
}
let input = ["David", "Jennifer", "Jon", "Sarah"];
console.log(indexOf("David", input)); // 0
console.log(indexOf("Jennifer", input)); // 1
console.log(indexOf("Jon", input)); // 2
console.log(indexOf("Sarah", input)); // 3
console.log(indexOf("NOT THERE", input)); // -1
&#13;