Reputation: 8320
I've implemented a circular buffer using a fixed-length array. In order to point to the start of valid data, I use an index (_startIndex
). Similarly, in order to point to the end of valid data, I use another index (_endIndex
). Below is an example.
9 8 7 6 5 4 3 2 1 0 <-- array indices
3 2 1 0 5 4 <-- buffer indices
-----------------------------------------
| | | | | | | | | | |
-----------------------------------------
^ ^
_startIndex _endIndex
Now I need to rearrange the elements of this buffer: the smallest element should be moved to position 0 of the buffer, while the largest element should be moved to position 5 of the buffer.
My idea is based on the following method.
int GetArrayIndex(int bufferIndex)
{
return (_startIndex + bufferIndex) % LENGTH;
// LENGTH is the length of the array
}
In this way, the sorting algorithm could read the buffer sequentially, using the above method, without therefore having to be aware that the buffer consists of two non-contiguous portions of the same array. Are there better ways to sort a circular buffer?
Upvotes: 3
Views: 2823
Reputation: 134085
If you want to do an in-place sort, then you should compress the buffer first. That is, make it one contiguous block from index 0 to index 5.
Then you can call the Array.Sort(T[], index, length) overload to sort that portion of the array.
You can move the items with a single operation, once you figure out what to move, and where.
There are three cases:
start_index == 0
: No movement needed
start_index < end_index
: Number of items to move is (end_index - start_index + 1)
. Move items from start_index
through end_index
to positions '0' through 'count-1'
start_index > end_index
: There is a "hole" in the array (as you've shown). You need to move items from start_index
through the end of the array to position array.length - start_index - count
.
Once you figure out the source and destination positions, you can call Buffer.BlockCopy to move the items.
I should note that, after the move and sort is done, you can set start_index
to 0, and end_index
to count-1
. Or, if you really want the buffer to be in the same state it was in before (just with re-ordered items), you can move things back using the same kind of logic I described above. Why you'd want to do that is unclear.
Upvotes: 7
Reputation: 9417
I would propose implementing a modified quicksort.
quicksort is a "divide and conquer" algorithm, which means dividing the set into two pieces, and then continuing. your buffer is already splitted into two parts, they just need to be adjusted. the the first step would be sorting the two pieces, the first piece at the beginning of your array, and the second piece at the end of your array. you would just "presort" the elements so that every element in the second part is smaller than any part in the first part. then you can apply the quicksort algorithm to every piece and you are sorted
Upvotes: 1
Reputation: 11311
Simple solution:
This solution runs in O(M) steps to rearrange elements and O(MlogM) to sort them, which totals O(MlogM) time. In other words - time required to rearrange elements to the beginning of the buffer is negligible compared to time required to sort them.
Alternative solution is to sort first and second part of the buffer separately and then to merge sort them to the beginning of the array. Running time is equal (slightly larger, to be precise), and code is more complicated.
Upvotes: 1