From 65cecbc66c61ccb5ac4d462dd5431f17b3a3db46 Mon Sep 17 00:00:00 2001 From: Konstantinos Kalafatis Date: Mon, 23 Sep 2024 12:18:32 +0300 Subject: [PATCH] Add implementation for basic timsorter --- .../Comparison/BasicTeamSorterTests.cs | 87 +++++++++++++ .../Sorters/Comparison/BasicTimSorter.cs | 120 ++++++++++++++++++ README.md | 1 + 3 files changed, 208 insertions(+) create mode 100644 Algorithms.Tests/Sorters/Comparison/BasicTeamSorterTests.cs create mode 100644 Algorithms/Sorters/Comparison/BasicTimSorter.cs diff --git a/Algorithms.Tests/Sorters/Comparison/BasicTeamSorterTests.cs b/Algorithms.Tests/Sorters/Comparison/BasicTeamSorterTests.cs new file mode 100644 index 00000000..f699202d --- /dev/null +++ b/Algorithms.Tests/Sorters/Comparison/BasicTeamSorterTests.cs @@ -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 sorter = new(Comparer.Default); + + [Test] + public void Sort_EmptyArray_DoesNotThrow() + { + var array = Array.Empty(); + 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(); + } + } +} diff --git a/Algorithms/Sorters/Comparison/BasicTimSorter.cs b/Algorithms/Sorters/Comparison/BasicTimSorter.cs new file mode 100644 index 00000000..2ca6fb33 --- /dev/null +++ b/Algorithms/Sorters/Comparison/BasicTimSorter.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Algorithms.Sorters.Comparison +{ + /// + /// A basic implementation of the TimSort algorithm for sorting arrays. + /// + /// The type of elements in the array. + public class BasicTimSorter + { + private readonly int minRuns = 32; + private readonly IComparer comparer; + + /// + /// Initializes a new instance of the class. + /// + /// The comparer to use for comparing elements. + public BasicTimSorter(IComparer comparer) + { + this.comparer = comparer ?? Comparer.Default; + } + + /// + /// Sorts the specified array using the TimSort algorithm. + /// + /// The array to sort. + 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); + } + } + } + } + + /// + /// Sorts a portion of the array using the Insertion Sort algorithm. + /// + /// The array to sort. + /// The starting index of the portion to sort. + /// The ending index of the portion to sort. + 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; + } + } + + /// + /// Merges two sorted subarrays into a single sorted subarray. + /// + /// The array containing the subarrays to merge. + /// The starting index of the first subarray. + /// The ending index of the first subarray. + /// The ending index of the second subarray. + private void Merge(T[] array, int left, int mid, int right) + { + // Create segments for left and right subarrays + var leftSegment = new ArraySegment(array, left, mid - left + 1); + var rightSegment = new ArraySegment(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++]; + } + } + } +} diff --git a/README.md b/README.md index c66e20eb..2208ec72 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ find more than one implementation for the same objective but using different alg * [Selection Sort](./Algorithms/Sorters/Comparison/SelectionSorter.cs) * [Shell Sort](./Algorithms/Sorters/Comparison/ShellSorter.cs) * [Tim Sort](./Algorithms/Sorters/Comparison/TimSorter.cs) + * [Simplified Tim Sort](./Algorithms/Sorters/Comparison/BasicTimSorter.cs) * [External](./Algorithms/Sorters/External) * [Merge Sort](./Algorithms/Sorters/External/ExternalMergeSorter.cs) * [Integer](./Algorithms/Sorters/Integer)