private static void AssertQuickSortResult <TItem>(QuickselectSortOrder order, TItem[] items, int i, TItem result) { var cmp = Comparer <TItem> .Default; if (order == QuickselectSortOrder.Ascending) { var minimum = items.Skip(i).Min(); foreach (var item in items.Take(i)) { cmp.Compare(item, minimum).Should().BeLessOrEqualTo(0); } cmp.Compare(minimum, result).Should().Be(0); } else { var maximum = items.Skip(i).Max(); foreach (var item in items.Take(i)) { cmp.Compare(item, maximum).Should().BeGreaterOrEqualTo(0); } cmp.Compare(maximum, result).Should().Be(0); } }
public void Should_work_in_trivial_cases(QuickselectSortOrder order) { var items = new [] { 1, 7, 4, 15, 34, -54, 5 }; items.QuickSelect(0, order: order).Should().Be(order == QuickselectSortOrder.Ascending ? items.Min() : items.Max()); items.QuickSelect(items.Length, order: order).Should().Be(order == QuickselectSortOrder.Ascending ? items.Max() : items.Min()); items.QuickSelect(2, order: order).Should().Be(order == QuickselectSortOrder.Ascending ? 4 : 7); }
public void Should_select_fast_on_sameData(QuickselectSortOrder order) { var itemsCount = 8000; var items = Enumerable.Repeat(new string('c', 10), itemsCount).ToArray(); var comparer = new CountedComparer <string>(Comparer <string> .Default); var result = items.QuickSelect(6000, order: order, comparer: comparer); comparer.Called.Should().BeLessThan(itemsCount * 7); // Avg Performace O(n) AssertQuickSortResult(order, items, 6000, result); }
public void Should_select_fast_on_similarData(QuickselectSortOrder order) { var itemsCount = 800; var items = new[] { "c", "a", "b", "zf", "e", "f", "w", "we", "z", "gg" } .SelectMany(prefix => Enumerable.Repeat(prefix + new string('c', 10), itemsCount)) .ToArray(); var comparer = new CountedComparer <string>(Comparer <string> .Default); var result = items.QuickSelect(6000, order: order, comparer: comparer); comparer.Called.Should().BeLessThan(items.Length * 7); // Avg Performace O(n) AssertQuickSortResult(order, items, 6000, result); }
private static int IndexOfMin <T>(this T[] list, IComparer <T> comparer, QuickselectSortOrder order) { var minIndex = 0; for (var i = 0; i < list.Length; i++) { if (comparer.CompareWithOrder(list[i], list[minIndex], order) < 0) { minIndex = i; } } return(minIndex); }
public void Should_move_top_items_to_the_beginning_of_given_array(QuickselectSortOrder order) { var random = new Random(); var itemsCount = random.Next(100, 200); var items = new int[itemsCount]; for (var i = 1; i <= itemsCount - 1; i++) { for (var j = 0; j < itemsCount; j++) { items[j] = random.Next(10); } var result = items.QuickSelect(i, order: order); AssertQuickSortResult(order, items, i, result); } }
private static int Partition <T>(this T[] list, int startIndex, int endIndex, int pivotIndex, IComparer <T> comparer, QuickselectSortOrder order) { Swap(list, pivotIndex, startIndex); var pivot = list[startIndex]; var i = startIndex; var j = endIndex + 1; while (true) { do { i++; } while (i <= endIndex && comparer.CompareWithOrder(list[i], pivot, order) < 0); do { j--; } while (comparer.CompareWithOrder(list[j], pivot, order) > 0); if (i >= j) { Swap(list, startIndex, j); return(j); } Swap(list, i, j); } }
private static int CompareWithOrder <T>(this IComparer <T> comparer, T first, T second, QuickselectSortOrder order) => comparer.Compare(first, second) * (int)order;
public static T QuickSelect <T>(this T[] list, int top, IComparer <T> comparer = null, QuickselectSortOrder order = QuickselectSortOrder.Ascending) { if (top > list.Length || list.Length == 0) { throw new InvalidOperationException($"Requested to select top {top} items from array with size {list.Length}."); } if (comparer == null) { comparer = Comparer <T> .Default; } if (top == list.Length) { list.Swap(list.IndexOfMax(comparer, order), top - 1); return(list[top - 1]); } if (top <= 0) { list.Swap(list.IndexOfMin(comparer, order), 0); return(list[0]); } var startIndex = 0; var endIndex = list.Length - 1; var pivotIndex = top; while (endIndex > startIndex) { pivotIndex = list.Partition(startIndex, endIndex, pivotIndex, comparer, order); if (pivotIndex == top) { break; } if (pivotIndex > top) { endIndex = pivotIndex - 1; } else { startIndex = pivotIndex + 1; } pivotIndex = Next(startIndex, endIndex); } return(list[pivotIndex]); }
private static int IndexOfMax <T>(this T[] list, IComparer <T> comparer, QuickselectSortOrder order) => list.IndexOfMin(comparer, (QuickselectSortOrder)((int)order * -1));