我有一个排序数组:
[0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000]
我正在尝试使用二进制搜索来分析此排序后的数组,并返回该数组中大于或等于给定值的所有值。因此,如果我输入搜索6
,则需要的结果数组将是:
[6, 6, 6, 50, 70, 80, 81, 100, 10000]
我不能仅仅寻找一个值的第一次出现,因为该值可能不存在于数组中。例如,我可能需要寻找5
,它不存在,但是我的输出将与上面的相同。
我这里有一些代码陷入了这个jsFiddle的无限循环中:http://jsfiddle.net/2mBdL/691/
var arr = [0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000];
function binarySearch(arr, i) {
var mid = Math.floor(arr.length / 2);
var leftOfMid = mid - 1;
console.log(arr[mid], i);
if (arr[mid] == i && arr[leftOfMid] == i) {
console.log('needle is exactly at 6, but left is same, checking lower half', arr[mid], i);
binarySearch(arr.splice(leftOfMid, Number.MAX_VALUE), i);
} else if (arr[mid] < i && arr.length > 1) {
console.log('needle is at a value that is less than 6', arr[mid], i);
binarySearch(arr.splice(mid, Number.MAX_VALUE), i);
} else if (arr[mid] > i && arr.length > 1) {
console.log('needle is at a value that is higher than 6 days', arr[mid], i);
binarySearch(arr.splice(0, mid), i);
} else if (arr[mid] == i && arr[leftOfMid] < i) {
console.log('MATCH, needle is the beginning of the range', arr[mid], i);
return arr[mid];
}
else {
console.log('not here', i);d
return -1;
}
}
var result = binarySearch(arr, 6);
console.log(result);
对于该用例,我如何利用二进制搜索?数组可能会变得非常大,所以我真的想让它正常工作!
答案 0 :(得分:0)
您可以简单地使用Array.prototype.filter
来过滤数组中的元素。
array.filter(i => i >= 6)
console.log(
[0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000]
.filter(i => i >= 6)
);
或以功能形式:
function search(arr, n) {
return arr.filter(i => i >= n);
}
let arr = [0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000]
function search(arr, n) {
return arr.filter(i => i >= n);
}
console.log( search(arr, 6) );
答案 1 :(得分:0)
二进制搜索在这里无济于事,因为它搜索的不是范围内的单个值,因此您不知道当前迭代中测试的值是否是第一个大于6的值。解决方法非常简单:
arr.filter(e => e > i);
答案 2 :(得分:0)
您可以为此使用二进制搜索。您只需要搜索值从小于目标到大于或等于目标的边界。
function bsearch(arr, lim, start = 0, end = arr.length - 1) {
let index = Math.floor((end - start) / 2) + start
if (start >= end) return -1
if (arr[index] > lim)
return (arr[index - 1] < lim || arr[index - 1] === undefined) ? index : bsearch(arr, lim, start, index)
if (arr[index] < lim)
return arr[index - 1] > lim ? index : bsearch(arr, lim, index + 1, end)
if (arr[index] === lim) {
return (arr[index - 1] === lim) ? bsearch(arr, lim, start, index) : index
}
}
let arr = [0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000]
// all values >= 6
let index = bsearch(arr, 6)
console.log(arr.slice(index))
// all values >= 5
index = bsearch(arr, 5)
console.log(arr.slice(index))
在大型数组上,这将具有二进制搜索的所有优点。例如,搜索10000个元素的数组仅需进行13次迭代。
答案 3 :(得分:0)
二进制搜索确实可以帮助大型数组,对于较小的数组则可能无关紧要。我认为您的策略还可以,但是实施过程似乎有些复杂,所以我看不到您的错误在哪里。
考虑以下内容,我尝试通过添加注释使其简单:
var arr = [0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000];
function binaryFind(arr, value) {
// Helper: go down until stop getting matches
function goLeft(arr, value, pos) {
while (arr[pos - 1] == value) --pos;
return arr.slice(pos);
}
// Find match or first value larger
function search(arr, value, pos, iteration, direction) {
// Update position for this iteration
++iteration;
pos += direction * arr.length / (iteration*2) | 0;
// Check if at start or end of array
if (pos >= arr.length) {
return [];
} else if (pos <=0) {
return arr.slice(0);
}
// Hit value
if (arr[pos] == value) {
return goLeft(arr, value, pos);
}
// Under
if (arr[pos] < value) {
// Check higher adjacent
if (arr[pos + 1] >= value) {
return arr.slice(pos+1);
}
//Otherwise search up
return search(arr, value, pos, iteration, 1);
}
// Over
if (arr[pos] > value) {
// Check lower adjacent
if (arr[pos - 1] < value) {
return arr.slice(pos);
}
// Otherwise search down
return search(arr, value, pos, iteration, -1);
}
}
return search(arr, value, 0, 0, 1);
}
[0,1,2,3,4,5,6,7,55,1e6,555,70,69,-1,71].forEach(v =>
console.log(v + ': ' + binaryFind(arr, v))
);
马克·迈耶(Mark Meyer)搜索边界的想法让我开始思考,所以这是一个应该更有效的版本,因为它继续使用二进制搜索,而不是“向下”查找匹配值(它寻找第一个值是)。当然,使用真实数据进行测试将确定这是否有好处,但这是一个更简单的解决方案。
将搜索功能包装在外部功能中,以防止滥用其他参数。没有包装程序,它只有3行代码,但其中的一行有点长。 ;-)
// Use a binary search to find all elements in a sorted array
// that are equal to or greater than value.
function binaryFind(arr, value) {
function search(arr, value, pos=0, iteration=1, direction=1) {
pos += direction * arr.length / (iteration*2) | 0;
return pos >= arr.length? [] :
pos <=0? arr.slice(0) :
arr[pos] < value? (arr[pos + 1] >= value? arr.slice(pos+1) : search(arr, value, pos, ++iteration, 1)):
(arr[pos - 1] < value? arr.slice(pos) : search(arr, value, pos, ++iteration, -1));
}
return search(arr, value);
}
// Testing
var arr = [0, 1, 1, 2, 3, 4, 6, 6, 6, 50, 70, 80, 81, 100, 10000];
[0,1,2,3,4,5,6,7,55,1e6,555,70,69,-1,71].forEach(v =>
console.log(v + ': ' + binaryFind(arr, v))
);