是否有内置的JavaScript函数来执行partial sort?如果没有,实施它的好方法是什么?
给定 N 元素的未排序数组,我想找到关于某些加权函数最小的 K 元素。 K 远小于 N ,因此对整个数组进行排序并获取第一个 K 元素效率很低。
即使存在非标准的,依赖于浏览器的东西,我也会很高兴。我仍然可以回退到自定义JavaScript实现。
PS:这是我目前的自定义实现(没有考虑加权函数,只是简单地对元素进行排序):
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (x < items[mid]) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
function partialSort(items, k) {
var smallest = [];
for (var i = 0, len = items.length; i < len; ++i) {
var item = items[i];
if (smallest.length < k || item < smallest[smallest.length - 1]) {
insort(smallest, item);
if (smallest.length > k)
smallest.splice(k, 1);
}
}
return smallest;
}
console.log(partialSort([5, 4, 3, 2, 1, 6, 7, 8, 1, 9], 3));
算法一次遍历给定的数组,跟踪到目前为止 k 最小项的排序列表,使用二进制搜索来插入新元素。
如果您认为可能更快或更优雅,请发布替代解决方案。时间表非常受欢迎。
答案 0 :(得分:5)
没有。只有full array sort
,因此您需要使用自己的实现。
您的代码几乎没有改进(我曾想过完全相同的算法: - )):
function partialSort(items, k) {
var smallest = items.slice(0, k).sort(),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < max) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
(偶然seems to be a little faster,我想是因为缓存了max
变量)
答案 1 :(得分:2)
没有原生的部分排序功能。最接近你想要的是Array.filter。
function isSmallEnough(element, index, array) {
return (element <= 10);
}
var filtered = [12, 5, 8, 130, 44].filter(isSmallEnough);
// filtered is [5, 8]
该示例是从上面的链接借用(并稍加修改)。
答案 2 :(得分:1)
对于相对较小的 k ,实现Max Heap(由于JavaScript中缺少本机堆)可能是值得的:
对于每个剩余值:
最后对堆进行排序并返回它。
实际上,这是对使用 Min 堆的另一种想法的改进,但是需要对整个数组进行堆放,因此运行速度不会很快。堆满整个数组后,只需从该堆中提取 k 倍值,然后返回这些值。
我已经将两个解决方案添加到Bergi创建的performance tests中。对于该特定测试(5000个数组值,k = 10),Max Heap解决方案的速度提高了2倍。但是,随着 k 的增加,这一优势将缩小。
以下是Max Heap解决方案的代码:
// A few Heap-functions that operate on an array
function maxSiftDown(arr, i=0, value=arr[i]) {
if (i >= arr.length) return;
while (true) {
var j = i*2+1;
if (j+1 < arr.length && arr[j] < arr[j+1]) j++;
if (j >= arr.length || value >= arr[j]) break;
arr[i] = arr[j];
i = j;
}
arr[i] = value;
}
function maxHeapify(arr) {
for (var i = arr.length>>1; i--; ) maxSiftDown(arr, i);
return arr;
}
// The main algorithm
function partialSortWithMaxHeap(items, k) {
var heap = maxHeapify(items.slice(0, k));
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (item < heap[0]) maxSiftDown(heap, 0, item);
}
return heap.sort((a,b) => a-b);
}
// Sample data & call
var arr = Array.from({length:5000}, () => Math.floor(Math.random() * 1e5));
console.log(partialSortWithMaxHeap(arr, 10));
答案 3 :(得分:0)
我制作了一个版本,无法与Array.sort(f)之类的对象配合使用:
function partialSort(items, k,f) {
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
if (typeof(hi) == 'undefined') hi = items.length;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (0>f(x,items[mid])) hi = mid;
else lo = mid + 1;
}
return lo;
}
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
var smallest = items.slice(0, k).sort(f),
max = smallest[k-1];
for (var i = k, len = items.length; i < len; ++i) {
var item = items[i];
if (0>f(item,max)) {
insort(smallest, item);
smallest.length = k;
max = smallest[k-1];
}
}
return smallest;
}
// [ { e: 1 }, { e: 1 }, { e: 2 } ]
console.log(partialSort([{e:4},{e:6},{e:1},{e:8},{e:3},{e:1},{e:6},{e:2}],3,(a,b)=>a.e-b.e))
console.log()