【问题标题】:How to group numbers by size如何按大小对数字进行分组
【发布时间】:2016-02-02 03:08:52
【问题描述】:

我有 n 个不同的数字,我想将它们分类到 k 个组中,这样第 1 组中的任何数字都小于第 2 组中的任何数字,并且任何人在第 2 组中小于第 3 组中的任何人,依此类推,直到第 k 组(每个组内的数字不必排序)。我被要求设计一个在 O(n log k) 中运行的算法,但我只能想出 O(n^2) 个。

我该怎么做?

【问题讨论】:

  • 您是否查看过有关排序的 Wiki 页面,尤其是桶排序算法?见en.wikipedia.org/wiki/Sorting_algorithm
  • 还有其他限制吗?如前所述,您可以将所有数字放在第 1 组中,而将所有其他数字留空。
  • 那么限制是每个桶需要有相同数量的元素(在本例中为 n/k),并且需要在 O(n log k) 时间内运行。
  • 这似乎类似于快速排序,寻找 k-1 个枢轴将数组分成 k 个组。问题是找到时间复杂度较低的 k-1 个枢轴点。我在想类似于median of medians 的东西,但这变得很复杂。可能有一种方法可以使用堆排序之类的方法来做到这一点。
  • @Jaco - 桶排序的问题是桶的大小不同。在这种情况下,目标是将 n 个数组分成大小相等的 k 个组(最后一组除外)。

标签: algorithm sorting complexity-theory


【解决方案1】:

您可以通过修改 Bucket 排序算法来实现这一点,下面我已经包含了一个 JavaScript 实现,有关源代码的更多详细信息,请参阅Github。此实现使用 16 个存储桶,您必须对其进行修改以允许 k 存储桶,并且您可以省略存储桶本身的排序。一种方法是使用2^p 存储桶,其中p 是满足2^p < n 的最小整数。该算法将在 O(n log k)

中运行

// Copyright 2011, Tom Switzer
// Under terms of ISC License: http://www.isc.org/software/license

/**
 * Sorts an array of integers in linear time using bucket sort.
 * This gives a good speed up vs. built-in sort in new JS engines
 * (eg. V8). If a key function is given, then the result of
 * key(a[i]) is used as the integer value to sort on instead a[i].
 *
 * @param a A JavaScript array.
 * @param key A function that maps values of a to integers.
 * @return The array a.
 */
function bsort(a, key) {
  key = key || function(x) {
    return x
  };
  var len = a.length,
    buckets = [],
    i, j, b, d = 0;
  for (; d < 32; d += 4) {
    for (i = 16; i--;)
      buckets[i] = [];
    for (i = len; i--;)
      buckets[(key(a[i]) >> d) & 15].push(a[i]);
    //This implementation uses 16 buckets, you will need to modify this
    for (b = 0; b < 16; b++)
      //The next two lines sort each bucket, you can leave it out
      for (j = buckets[b].length; j--;)
        a[++i] = buckets[b][j];
  }
  return a;
}


var array = [2, 4, 1, 5, 3];

$('#result').text(bsort(array, function(x) {
  return x
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>

【讨论】:

    【解决方案2】:

    请注意,问题陈述是将 n 不同的数字分成 k 组。如果下面的 wiki 链接中有重复项,这将变得更加复杂。

    任何能够以小于 O(n log(k)) 复杂度确定第 k 个最小元素的过程都可以使用 k-1 次来生成与 k 个组之间的边界相对应的元素数组。然后可以对数组进行一次遍历,对边界数组进行二进制搜索,以将数组分成 k 组,复杂度为 O(n log(k))。但是,似乎至少有一种算法可以找到第 k 个最小元素也对数组进行分区,因此可以单独使用该算法来创建 k 个组。

    使用最坏情况时间 O(n) 的选择算法进行无序部分排序是可能的。维基链接:

    http://en.wikipedia.org/wiki/Selection_algorithm

    http://en.wikipedia.org/wiki/Selection_algorithm#Unordered_partial_sorting

    http://en.wikipedia.org/wiki/Quickselect

    http://en.wikipedia.org/wiki/Median_of_medians

    http://en.wikipedia.org/wiki/Soft_heap#Applications

    【讨论】:

    • 感谢您的评论!读完这篇文章就清楚了怎么做!谢谢!!
    【解决方案3】:

    使用 K-selection 算法和来自 QuickSort 的分区函数 - QuickSelect
    为简单起见,让我们 K 是 2 的幂。
    在第一阶段,我们对 N 个元素进行分区,它需要 O(N) ~ p* N 时间,其中 p 是某个常数
    在第二阶段,我们递归地对 N/2 个元素进行 2 个分区,这需要 2* p* N/2 = p*N 时间。
    在第三阶段,我们对 N/4 个元素进行 4 个分区,这需要 4*pN/4 = pN 时间。
    ...
    在最后阶段,我们对 N/K 个元素进行 K 个分区,这需要 K* p* N/K = p*N 时间。

    注意有 Log(K) 个阶段,所以总时间是 Log(K) * p * N = O(N*Log(K)

    【讨论】:

    • 感谢您的帮助!和教授商量后,这是他建议的一种方法!谢谢!
    【解决方案4】:

    感谢您的所有帮助,基本上是一个快速选择(或任何在线性时间内找到第 k 个统计量的线性时间排序算法就足够了),并且在运行它 k-1 次之后,我们对原始数组将元素分成组,得到 O(nlog k)。

    另外,如果你不想做二分查找,在快速选择中,你也可以分离元素,在每个子集中查找统计量! @rcgldr、@MBo 谢谢你的想法!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-25
      • 2016-01-06
      • 2020-10-20
      • 1970-01-01
      相关资源
      最近更新 更多