QuickSort methods extracted to their own extension class.

This commit is contained in:
mlemanczyk committed 2023-09-03 13:35:49 +02:00
1 parent 7434b28894
commit f928e1e35e
5 files changed
+141 -121

No files matched your search

@@ -0,0 +1,132 @@
// Ignore Spelling: Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
// The source code was taken from
// https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
//
// It was only beautified for .Net 6.0+.
namespace Recyclable.Collections.Linq
{
internal static class QuickSortExtensions<T>
{
public static void InsertionSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
{
var left = startIndex;
while (left < endIndex)
{
int right = left;
T? temp = values[++left];
while (right >= startIndex && 0 < comparer.Compare(values[right], temp))
{
values[right + 1] = values[right--];
}
values[right + 1] = temp;
}
}
public static void InsertionSort(IList<T> values, IComparer<T> comparer)
{
InsertionSort(values, 0, values.Count - 1, comparer);
}
public static void InsertionSort(IList<T> values)
{
InsertionSort(values, Comparer<T>.Default);
}
public static void QuickSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{
var range = (startIndex, endIndex);
var stack = new Stack<(int, int)>();
do
{
startIndex = range.startIndex;
endIndex = range.endIndex;
if (endIndex - startIndex + 1 < 31)
{
InsertionSort(values, startIndex, endIndex, comparer);
continue;
}
var pivot = SampleMedian(values, startIndex, endIndex, comparer, randomNumberGenerator);
var left = startIndex;
var right = endIndex;
while (left <= right)
{
while (0 > comparer.Compare(values[left], pivot))
{
left++;
}
while (0 > comparer.Compare(pivot, values[right]))
{
right--;
}
if (left <= right)
{
Swap(values, left++, right--);
}
}
if (startIndex < right)
{
stack.Push((startIndex, right));
}
if (left < endIndex)
{
stack.Push((left, endIndex));
}
}
while (stack.TryPop(out range));
}
public static void QuickSort(IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
=> QuickSort(values, 0, values.Count - 1, comparer, randomNumberGenerator);
public static void QuickSort(IList<T> values)
=> QuickSort(values, Comparer<T>.Default, new SystemRandomNumberGenerator());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T SampleMedian(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{
var left = randomNumberGenerator.NextInt32(startIndex, endIndex);
var middle = randomNumberGenerator.NextInt32(startIndex, endIndex);
var right = randomNumberGenerator.NextInt32(startIndex, endIndex);
if (0 > comparer.Compare(values[right], values[left]))
{
Swap(values, right, left);
}
if (0 > comparer.Compare(values[middle], values[left]))
{
Swap(values, middle, left);
}
if (0 > comparer.Compare(values[right], values[middle]))
{
Swap(values, right, middle);
}
return values[middle];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(IList<T> values, int xIndex, int yIndex)
=> (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]);
}
}
@@ -1,6 +1,4 @@
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
namespace Recyclable.Collections
@@ -85,32 +83,6 @@ namespace Recyclable.Collections
}
}
public static void InsertionSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer)
{
var left = startIndex;
while (left < endIndex)
{
int right = left;
T? temp = values[++left];
while (right >= startIndex && 0 < comparer.Compare(values[right], temp))
{
values[right + 1] = values[right--];
}
values[right + 1] = temp;
}
}
public static void InsertionSort(IList<T> values, IComparer<T> comparer)
{
InsertionSort(values, 0, values.Count - 1, comparer);
}
public static void InsertionSort(IList<T> values)
{
InsertionSort(values, Comparer<T>.Default);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
{
@@ -130,90 +102,6 @@ namespace Recyclable.Collections
return -1;
}
// The source code was taken from https://codereview.stackexchange.com/questions/205407/quicksort-without-recursion.
// It was only beautified for .Net 6.0+.
public static void QuickSort(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{
var range = (startIndex, endIndex);
var stack = new Stack<(int, int)>();
do
{
startIndex = range.startIndex;
endIndex = range.endIndex;
if (endIndex - startIndex + 1 < 31)
{
InsertionSort(values, startIndex, endIndex, comparer);
continue;
}
var pivot = SampleMedian(values, startIndex, endIndex, comparer, randomNumberGenerator);
var left = startIndex;
var right = endIndex;
while (left <= right)
{
while (0 > comparer.Compare(values[left], pivot))
{
left++;
}
while (0 > comparer.Compare(pivot, values[right]))
{
right--;
}
if (left <= right)
{
Swap(values, left++, right--);
}
}
if (startIndex < right)
{
stack.Push((startIndex, right));
}
if (left < endIndex)
{
stack.Push((left, endIndex));
}
}
while (stack.TryPop(out range));
}
public static void QuickSort(IList<T> values, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
=> QuickSort(values, 0, values.Count - 1, comparer, randomNumberGenerator);
public static void QuickSort(IList<T> values)
=> QuickSort(values, Comparer<T>.Default, new SystemRandomNumberGenerator());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T SampleMedian(IList<T> values, int startIndex, int endIndex, IComparer<T> comparer, IRandomNumberGenerator randomNumberGenerator)
{
var left = randomNumberGenerator.NextInt32(startIndex, endIndex);
var middle = randomNumberGenerator.NextInt32(startIndex, endIndex);
var right = randomNumberGenerator.NextInt32(startIndex, endIndex);
if (0 > comparer.Compare(values[right], values[left]))
{
Swap(values, right, left);
}
if (0 > comparer.Compare(values[middle], values[left]))
{
Swap(values, middle, left);
}
if (0 > comparer.Compare(values[right], values[middle]))
{
Swap(values, right, middle);
}
return values[middle];
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void ResizeAndCopy(RecyclableList<T> list)
{
@@ -267,9 +155,5 @@ namespace Recyclable.Collections
return newSize;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(IList<T> values, int xIndex, int yIndex)
=> (values[yIndex], values[xIndex]) = (values[xIndex], values[yIndex]);
}
}
@@ -1,4 +1,6 @@
namespace Recyclable.Collections
using Recyclable.Collections.Linq;
namespace Recyclable.Collections
{
internal class RecyclableSortedList<TKey, TValue> : RecyclableLongList<(TKey Key, TValue Value)>
where TKey : notnull
@@ -68,7 +70,7 @@
BeginUpdate();
try
{
RecyclableListHelpers<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
QuickSortExtensions<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
}
finally
{
@@ -1,5 +1,6 @@
using FluentAssertions;
using Recyclable.Collections;
using Recyclable.Collections.Linq;
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
@@ -424,7 +425,7 @@ namespace Recyclable.CollectionsTests
// Act
using RecyclableList<long> list = new(testData);
RecyclableListHelpers<long>.QuickSort(list);
QuickSortExtensions<long>.QuickSort(list);
// Validate
_ = list.Count.Should().Be((int)itemsCount);
@@ -1,5 +1,6 @@
using FluentAssertions;
using Recyclable.Collections;
using Recyclable.Collections.Linq;
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
@@ -481,7 +482,7 @@ namespace Recyclable.CollectionsTests
// Act
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
RecyclableListHelpers<long>.QuickSort(list);
QuickSortExtensions<long>.QuickSort(list);
// Validate
_ = list.LongCount.Should().Be(itemsCount);