QuickSort methods extracted to their own extension class.
This commit is contained in:
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.Runtime.CompilerServices;
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Pools;
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
public static long LongIndexOf(RecyclableList<T[]> arrays, int blockSize, T item, IEqualityComparer<T> comparer)
|
||||||
{
|
{
|
||||||
@@ -130,90 +102,6 @@ namespace Recyclable.Collections
|
|||||||
return -1;
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void ResizeAndCopy(RecyclableList<T> list)
|
public static void ResizeAndCopy(RecyclableList<T> list)
|
||||||
{
|
{
|
||||||
@@ -267,9 +155,5 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
return newSize;
|
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)>
|
internal class RecyclableSortedList<TKey, TValue> : RecyclableLongList<(TKey Key, TValue Value)>
|
||||||
where TKey : notnull
|
where TKey : notnull
|
||||||
@@ -68,7 +70,7 @@
|
|||||||
BeginUpdate();
|
BeginUpdate();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RecyclableListHelpers<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
|
QuickSortExtensions<(TKey, TValue)>.QuickSort(this, _comparer, _randomNumberGenerator);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Recyclable.Collections;
|
using Recyclable.Collections;
|
||||||
|
using Recyclable.Collections.Linq;
|
||||||
|
|
||||||
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
||||||
|
|
||||||
@@ -424,7 +425,7 @@ namespace Recyclable.CollectionsTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
using RecyclableList<long> list = new(testData);
|
using RecyclableList<long> list = new(testData);
|
||||||
RecyclableListHelpers<long>.QuickSort(list);
|
QuickSortExtensions<long>.QuickSort(list);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
_ = list.Count.Should().Be((int)itemsCount);
|
_ = list.Count.Should().Be((int)itemsCount);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Recyclable.Collections;
|
using Recyclable.Collections;
|
||||||
|
using Recyclable.Collections.Linq;
|
||||||
|
|
||||||
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
#pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235
|
||||||
|
|
||||||
@@ -481,7 +482,7 @@ namespace Recyclable.CollectionsTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
using RecyclableLongList<long> list = new(testData, minBlockSize: targetBlockSize);
|
||||||
RecyclableListHelpers<long>.QuickSort(list);
|
QuickSortExtensions<long>.QuickSort(list);
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
_ = list.LongCount.Should().Be(itemsCount);
|
_ = list.LongCount.Should().Be(itemsCount);
|
||||||
|
|||||||
Reference in new issue
Block a user