/* Sorts an array using Selection sort algorithm. * Prints updates if printUpdates is true. * Doesn't redundantly swap the last remaining element with itself. * Time complexity: Worst = n^2, Best = n^2, Average = n^2. */ public static int[] Sort(int[] array, bool printUpdates) { ulong comparisons = 0; ulong swaps = 0; int n = array.Length; // The leftmost element will be skipped at each successive loop, since we know it's now sorted. // Also, the condition is ( < n-1 ) instead of ( < n ) to avoid swapping the last element with itself. for (int firstUnsortedIndex = 0; firstUnsortedIndex < n - 1; firstUnsortedIndex++) { if (printUpdates) { Console.WriteLine("Go through unsorted section to find minimum:"); } // Assume the first unsorted element is the smallest, store its index int min = array[firstUnsortedIndex]; int indexOfMin = firstUnsortedIndex; // Iterate through the unsorted array and find the smallest element for (int currentIndex = firstUnsortedIndex + 1; currentIndex < n; currentIndex++) { // Since the next line will compare elements, add 1 to comparisons comparisons++; if (printUpdates) { Console.WriteLine("^ Compare " + min.ToString().PadLeft(2) + " with " + array[currentIndex].ToString().PadLeft(2)); } if (array[currentIndex] < min) { min = array[currentIndex]; indexOfMin = currentIndex; } } // Swap the smallest element with the first unsorted element. Count 1 more swap. ArrayOperations.Swap(array, indexOfMin, firstUnsortedIndex); swaps++; // Since a swap was made, print array in current state and say what was swapped if (printUpdates) { Console.WriteLine("Minimum = {0}, swap with first unsorted element:", min); string message = "swapped " + array[indexOfMin].ToString().PadLeft(2) + " with " + array[firstUnsortedIndex].ToString().PadLeft(2); ArrayOperations.Print(array, message); } } // Print number of comparisons and swaps that were performed if (printUpdates) { Console.WriteLine("Since there's only one element left, it must be in the right place and we're done!"); Console.WriteLine("\nNumber of comparisons: " + comparisons + "\nNumber of swaps: " + swaps); } CountAndDisplay.totalComparisonsSelection += comparisons; CountAndDisplay.totalSwapsSelection += swaps; return(array); }
/* Returns the pivot for Quicksort's Partition method. * Choice of three methods of finding the pivot: end, random, and median-of-three * Choose by setting the value of 'pivotMethod' a few lines down. * * A note on median-of-three from Wikipedia: * "Although this approach optimizes quite well, it is typically outperformed in practice * by instead choosing random pivots, which has average linear time for selection and * average log-linear time for sorting, and avoids the overhead of computing the pivot." */ public static int GetPivot(int[] array, int start, int end) { int pivotMethod = 2; //Set here: 0 = end, 1 = random, 2 = median-of-three // Method 1: Pivot = end // This gives Quicksort a running time O(n^2) in the worst case (already sorted) if (pivotMethod == 0) { return(end); } // Method 2: Pivot = random else if (pivotMethod == 1) { // Pivot is the element at a random index in this part of the array Random rnd = new Random(); int i = rnd.Next(start, end + 1); // Swap the pivot with the end (end is now pivot) ArrayOperations.Swap(array, i, end); CountAndDisplay.totalSwapsQuick++; return(end); } // Method 3: Pivot = 'median of three', change the array as required else { int mid = (start + end) / 2; // Ensure start < mid < end if (array[start] > array[end]) { ArrayOperations.Swap(array, start, end); CountAndDisplay.totalSwapsQuick++; } if (array[start] > array[mid]) { ArrayOperations.Swap(array, start, mid); CountAndDisplay.totalSwapsQuick++; } if (array[mid] > array[end]) { ArrayOperations.Swap(array, mid, end); CountAndDisplay.totalSwapsQuick++; } // Swap mid and end (end is now pivot) ArrayOperations.Swap(array, end, mid); CountAndDisplay.totalSwapsQuick++; return(end); } }
/* Sets pivot to some element in the given array, and partitionIndex to the first. * Iterates through each element and compares against the pivot. * If it's less than or equal to the pivot, swap it with partitionIndex++. * If it's greater than pivot, leave it alone. * Finally, swap the pivot with the partitionIndex. * Now, all elements <= pivot are on its left, and elements > pivot are on the right. */ public static int Partition(int[] array, int start, int end, bool printUpdates) { ulong comparisons = 0; ulong swaps = 0; // Set pivot (one of three methods, see GetPivot method) int pivotIndex = GetPivot(array, start, end); int pivot = array[pivotIndex]; // Set partitionIndex to start int partitionIndex = start; if (printUpdates) { Console.WriteLine("Pivot is {0}, move < elements to left and >= to right", pivot); } // Iterate from start to end - 1 (because end is always the pivot) for (int i = start; i < end; i++) { // Compare each element to the pivot comparisons++; if (array[i] < pivot) { // If it's < pivot, swap to left of partition ArrayOperations.Swap(array, i, partitionIndex); partitionIndex++; swaps++; } } // Move the pivot to the partition. Now, elements left are <= pivot, right are > pivot ArrayOperations.Swap(array, end, partitionIndex); swaps++; CountAndDisplay.totalComparisonsQuick += comparisons; CountAndDisplay.totalSwapsQuick += swaps; if (printUpdates) { string message = "Pivot was " + pivot.ToString().PadLeft(2) + ", moved to index " + partitionIndex.ToString().PadLeft(2); ArrayOperations.Print(array, message); } return(partitionIndex); }
/* Sorts an array using Bubble sort algorithm. * Prints updates if printUpdates is true. * Optimised: will only loop once through an array once after it's sorted, * and distinguishes between 'sorted' and 'unsorted' parts. * Time complexity: Worst = n^2, Best = n, Average = n^2. */ public static int[] Sort(int[] array, bool printUpdates) { ulong comparisons = 0; ulong swaps = 0; // Get (length - 1) because we compare with (j + 1) int n = array.Length - 1; bool swapped = false; /* Loop through the array, checking that each element is smaller than the next. * If not, swap those two elements and mark 'swapped' to true. * Stop looping when the whole array has been checked without making a swap. */ do { swapped = false; for (int j = 0; j < n; j++) { // Since the next line will compare elements, add 1 to comparisons comparisons++; if (printUpdates) { Console.Write("^ Compare " + array[j].ToString().PadLeft(2) + " with " + array[j + 1].ToString().PadLeft(2)); } if ((array[j] > array[j + 1])) { // Swap them, set 'swapped' to true, count 1 more swap ArrayOperations.Swap(array, j, j + 1); swapped = true; swaps++; // Since a swap was made, print array in current state and say what was swapped if (printUpdates) { Console.WriteLine(", it's bigger so swap:"); string message = "swapped " + array[j + 1].ToString().PadLeft(2) + " with " + array[j].ToString().PadLeft(2); ArrayOperations.Print(array, message); } } else if (printUpdates) { Console.WriteLine(", it's not bigger so don't swap"); } } // After each loop, we know the biggest element must be sorted // So, it can be ignored on the next pass n--; } while (swapped); // Print number of comparisons and swaps that were performed if (printUpdates) { Console.WriteLine("Since no swaps were made this pass, we're done!"); Console.WriteLine("\nNumber of comparisons: " + comparisons + "\nNumber of swaps: " + swaps); } CountAndDisplay.totalComparisonsBubble += comparisons; CountAndDisplay.totalSwapsBubble += swaps; return(array); }