【问题标题】:Most efficient way to sort a circular buffer对循环缓冲区进行排序的最有效方法
【发布时间】:2013-05-10 12:30:39
【问题描述】:

我使用固定长度数组实现了一个循环缓冲区。为了指向有效数据的开头,我使用了一个索引 (_startIndex)。同样,为了指向有效数据的末尾,我使用了另一个索引(_endIndex)。下面是一个例子。

  9   8   7   6   5   4   3   2   1   0   <-- array indices
  3   2   1   0                   5   4   <-- buffer indices
-----------------------------------------
|   |   |   |   |   |   |   |   |   |   |
-----------------------------------------
              ^                   ^
             _startIndex         _endIndex

现在我需要重新排列这个缓冲区的元素:最小的元素应该移动到缓冲区的位置 0,而最大的元素应该移动到缓冲区的位置 5。

我的想法是基于以下方法。

int GetArrayIndex(int bufferIndex)
{
    return (_startIndex + bufferIndex) % LENGTH;
    // LENGTH is the length of the array
}

通过这种方式,排序算法可以使用上述方法顺序读取缓冲区,因此不必知道缓冲区由同一数组的两个不连续部分组成。 有没有更好的方法来对循环缓冲区进行排序?

【问题讨论】:

  • 就地排序有多重要?你可以根据缓冲区生成一个IEnumerable&lt;T&gt; 的数据,然后调用 orderby,然后根据结果生成一个新的缓冲区吗?
  • 这实际上是排序还是目标是旋转缓冲区以使第一个元素位于底层数组的第一个元素处?
  • @Servy:缓冲区应该包含要发送到网络中节点的消息。我如何评估是否需要进行就地排序?
  • @UlrichEckhardt:这实际上是排序。
  • @enzom83 好吧,如果你真的需要一个就地排序,你总是可以做我上面描述的,然后把所有的值复制回缓冲区,但是你确定你真的需要就地排序吗?

标签: c# .net algorithm sorting


【解决方案1】:

如果要进行就地排序,则应先压缩缓冲区。也就是说,让它成为一个从索引 0 到索引 5 的连续块。

然后您可以调用Array.Sort(T[], index, length) 重载对数组的该部分进行排序。

一旦确定要移动的内容和位置,只需一次操作即可移动项目。

分三种情况:

  1. start_index == 0:无需移动

  2. start_index &lt; end_index:要移动的项目数是(end_index - start_index + 1)。将项目从 start_indexend_index 移动到位置 '0' 到 'count-1'

  3. start_index &gt; end_index:数组中有一个“洞”(如您所示)。您需要将项目从start_index 移动到数组末尾到array.length - start_index - count 的位置。

确定源位置和目标位置后,您可以致电Buffer.BlockCopy 移动项目。

需要注意的是,移动和排序完成后,可以将start_index设置为0,将end_index设置为count-1。或者,如果您真的希望缓冲区处于与之前相同的状态(只是重新排序的项目),您可以使用我上面描述的相同类型的逻辑将事物移回。您为什么要这样做尚不清楚。

【讨论】:

  • +1。从编码的角度来看,显然是最有效的,从性能的角度来看,效率与任何事情一样——(重新排列块 + 排序):O(n) + O(n log n))仍然是O(n log n)
【解决方案2】:

简单的解决方案:

  1. 移动元素,使值占据位置 0、1、...、M-1,其中 M 是数组中使用的位置数。
  2. 修改 _startIndex = 0 和 _endIndex = M-1
  3. 在 0 和 _endIndex 之间的部分缓冲区上调用 Array.Sort。

此解决方案以 O(M) 步骤运行以重新排列元素,并以 O(MlogM) 步骤对它们进行排序,总计 O(MlogM) 时间。换句话说 - 将元素重新排列到缓冲区开头所需的时间与对它们进行排序所需的时间相比可以忽略不计。

另一种解决方案是分别对缓冲区的第一部分和第二部分进行排序,然后将它们合并排序到数组的开头。运行时间相等(准确的说稍微大一点),代码更复杂。

【讨论】:

    【解决方案3】:

    我建议实施修改后的快速排序。

    快速排序是一种“分而治之”的算法,这意味着将集合分成两部分,然后继续。 您的缓冲区已经分为两部分,只需要调整它们。第一步是对这两部分进行排序,第一部分在数组的开头,第二部分在数组的末尾。您只需“预排序”元素,以便第二部分中的每个元素都小于第一部分中的任何部分。然后您可以将快速排序算法应用于每个部分并进行排序

    【讨论】:

    • +0:我不明白如何在没有实际排序的情况下“预排序” - 快速排序不能保证分区元素的位置。我认为在单独对 2 个单独的部分进行排序后,可以使用合并排序作为后处理步骤。除非是面试问题,否则不会这样做。
    猜你喜欢
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    相关资源
    最近更新 更多