JavaScript中的部分排序

时间:2013-03-25 17:33:16

标签: javascript sorting

是否有内置的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 最小项的排序列表,使用二进制搜索来插入新元素。

如果您认为可能更快或更优雅,请发布替代解决方案。时间表非常受欢迎。

4 个答案:

答案 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中缺少本机堆)可能是值得的:

  • 创建前 k 个值的 Max
  • 对于每个剩余值:

    • 如果它小于堆的根,请用此值替换根。否则忽略该值。请注意,堆大小永远不会改变。
  • 最后对堆进行排序并返回它。

实际上,这是对使用 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()