排序循环缓冲区的最有效方法

时间:2013-05-15 19:30:45

标签: c# .net algorithm sorting

我使用固定长度的数组实现了循环缓冲区。为了指向有效数据的开始,我使用索引(_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
}
以这种方式,排序算法可以使用上述方法顺序读取缓冲区,因此不必知道缓冲区由同一阵列的两个非连续部分组成。 是否有更好的方法来对循环缓冲区进行排序?

3 个答案:

答案 0 :(得分:7)

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

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

一旦弄清楚要移动的内容,您可以通过一次操作移动项目。

有三种情况:

  1. start_index == 0:无需移动

  2. start_index < end_index:要移动的项目数为(end_index - start_index + 1)。将项目从start_index移至end_index至位置'0'至'count-1'

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

  4. 一旦确定了源位置和目标位置,就可以调用Buffer.BlockCopy来移动项目。

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

答案 1 :(得分:1)

简单的解决方案:

  1. 移动元素,使值占据位置0,1,...,M-1,其中M是数组中已使用位置的数量。
  2. 修改_startIndex = 0和_endIndex = M-1
  3. 在0和_endIndex之间的缓冲区的一部分上调用Array.Sort。
  4. 此解决方案以O(M)步骤运行以重新排列元素和O(MlogM)以对它们进行排序,总计O(MlogM)时间。换句话说 - 将元素重新排列到缓冲区开头所需的时间与排序它们所需的时间相比可以忽略不计。

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

答案 2 :(得分:1)

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

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