-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add implementation for basic timsorter
- Loading branch information
Showing
3 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
87 changes: 87 additions & 0 deletions
87
Algorithms.Tests/Sorters/Comparison/BasicTeamSorterTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using Algorithms.Sorters.Comparison; | ||
using FluentAssertions; | ||
using NUnit.Framework; | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Algorithms.Tests.Sorters.Comparison | ||
{ | ||
[TestFixture] | ||
public class BasicTimSorterTests | ||
{ | ||
private readonly BasicTimSorter<int> sorter = new(Comparer<int>.Default); | ||
|
||
[Test] | ||
public void Sort_EmptyArray_DoesNotThrow() | ||
{ | ||
var array = Array.Empty<int>(); | ||
Assert.DoesNotThrow(() => sorter.Sort(array)); | ||
Assert.That(array, Is.Empty); | ||
} | ||
|
||
[Test] | ||
public void Sort_SingleElementArray_DoesNotChangeArray() | ||
{ | ||
var array = new[] { 1 }; | ||
sorter.Sort(array); | ||
Assert.That(array, Is.EqualTo(new[] { 1 })); | ||
} | ||
|
||
[Test] | ||
public void Sort_AlreadySortedArray_DoesNotChangeArray() | ||
{ | ||
var array = new[] { 1, 2, 3, 4, 5 }; | ||
sorter.Sort(array); | ||
Assert.That(array, Is.EqualTo(new[] { 1, 2, 3, 4, 5 })); | ||
} | ||
|
||
[Test] | ||
public void Sort_UnsortedArray_SortsCorrectly() | ||
{ | ||
var array = new[] { 5, 3, 1, 4, 2 }; | ||
sorter.Sort(array); | ||
Assert.That(array, Is.EqualTo(new[] { 1, 2, 3, 4, 5 })); | ||
} | ||
|
||
[Test] | ||
public void Sort_ReverseSortedArray_SortsCorrectly() | ||
{ | ||
var array = new[] { 5, 4, 3, 2, 1 }; | ||
sorter.Sort(array); | ||
Assert.That(array, Is.EqualTo(new[] { 1, 2, 3, 4, 5 })); | ||
} | ||
|
||
[Test] | ||
public void Sort_ArrayWithDuplicates_SortsCorrectly() | ||
{ | ||
var array = new[] { 3, 1, 2, 3, 1, 2 }; | ||
sorter.Sort(array); | ||
Assert.That(array, Is.EqualTo(new[] { 1, 1, 2, 2, 3, 3 })); | ||
} | ||
|
||
[Test] | ||
public void Sort_LargeArray_SortsCorrectly() | ||
{ | ||
var array = new int[1000]; | ||
for (var i = 0; i < 1000; i++) | ||
{ | ||
array[i] = 1000 - i; | ||
} | ||
sorter.Sort(array); | ||
array.Should().BeInAscendingOrder(); | ||
} | ||
|
||
[Test] | ||
public void Sort_LargeRandomArray_SortsCorrectly() | ||
{ | ||
var array = new int[1000]; | ||
var random = new Random(); | ||
for (var i = 0; i < 1000; i++) | ||
{ | ||
array[i] = random.Next(1, 1001); | ||
} | ||
sorter.Sort(array); | ||
array.Should().BeInAscendingOrder(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
|
||
namespace Algorithms.Sorters.Comparison | ||
{ | ||
/// <summary> | ||
/// A basic implementation of the TimSort algorithm for sorting arrays. | ||
/// </summary> | ||
/// <typeparam name="T">The type of elements in the array.</typeparam> | ||
public class BasicTimSorter<T> | ||
{ | ||
private readonly int minRuns = 32; | ||
private readonly IComparer<T> comparer; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BasicTimSorter{T}"/> class. | ||
/// </summary> | ||
/// <param name="comparer">The comparer to use for comparing elements.</param> | ||
public BasicTimSorter(IComparer<T> comparer) | ||
{ | ||
this.comparer = comparer ?? Comparer<T>.Default; | ||
} | ||
|
||
/// <summary> | ||
/// Sorts the specified array using the TimSort algorithm. | ||
/// </summary> | ||
/// <param name="array">The array to sort.</param> | ||
public void Sort(T[] array) | ||
{ | ||
var n = array.Length; | ||
|
||
// Step 1: Sort small pieces of the array using Insertion Sort | ||
for (var i = 0; i < n; i += minRuns) | ||
{ | ||
InsertionSort(array, i, Math.Min(i + minRuns - 1, n - 1)); | ||
} | ||
|
||
// Step 2: Merge sorted runs using Merge Sort | ||
for (var size = minRuns; size < n; size *= 2) | ||
{ | ||
for (var left = 0; left < n; left += 2 * size) | ||
{ | ||
var mid = left + size - 1; | ||
var right = Math.Min(left + 2 * size - 1, n - 1); | ||
|
||
if (mid < right) | ||
{ | ||
Merge(array, left, mid, right); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sorts a portion of the array using the Insertion Sort algorithm. | ||
/// </summary> | ||
/// <param name="array">The array to sort.</param> | ||
/// <param name="left">The starting index of the portion to sort.</param> | ||
/// <param name="right">The ending index of the portion to sort.</param> | ||
private void InsertionSort(T[] array, int left, int right) | ||
{ | ||
for (var i = left + 1; i <= right; i++) | ||
{ | ||
var key = array[i]; | ||
var j = i - 1; | ||
|
||
// Move elements of array[0..i-1], that are greater than key, | ||
// to one position ahead of their current position | ||
while (j >= left && comparer.Compare(array[j], key) > 0) | ||
{ | ||
array[j + 1] = array[j]; | ||
j--; | ||
} | ||
|
||
array[j + 1] = key; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Merges two sorted subarrays into a single sorted subarray. | ||
/// </summary> | ||
/// <param name="array">The array containing the subarrays to merge.</param> | ||
/// <param name="left">The starting index of the first subarray.</param> | ||
/// <param name="mid">The ending index of the first subarray.</param> | ||
/// <param name="right">The ending index of the second subarray.</param> | ||
private void Merge(T[] array, int left, int mid, int right) | ||
{ | ||
// Create segments for left and right subarrays | ||
var leftSegment = new ArraySegment<T>(array, left, mid - left + 1); | ||
var rightSegment = new ArraySegment<T>(array, mid + 1, right - mid); | ||
|
||
// Convert segments to arrays | ||
var leftArray = leftSegment.ToArray(); | ||
var rightArray = rightSegment.ToArray(); | ||
|
||
var i = 0; | ||
var j = 0; | ||
var k = left; | ||
|
||
// Merge the two subarrays back into the main array | ||
while (i < leftArray.Length && j < rightArray.Length) | ||
{ | ||
array[k++] = comparer.Compare(leftArray[i], rightArray[j]) <= 0 ? leftArray[i++] : rightArray[j++]; | ||
} | ||
|
||
// Copy remaining elements from leftArray, if any | ||
while (i < leftArray.Length) | ||
{ | ||
array[k++] = leftArray[i++]; | ||
} | ||
|
||
// Copy remaining elements from rightArray, if any | ||
while (j < rightArray.Length) | ||
{ | ||
array[k++] = rightArray[j++]; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters