|
| 1 | +/* |
| 2 | + * Merge Sorter |
| 3 | + * @author Samy Rezzag |
| 4 | + * Pseudocode adaptation from Thomas H. Cormens' Introduction to Algoirthms Third Edition. |
| 5 | + */ |
| 6 | + |
| 7 | +/* |
| 8 | + * The MergeSorter class demonstrates the divide-and-conquer MergeSort algorithm. |
| 9 | + */ |
| 10 | + |
| 11 | +public class MergeSorter<T> { |
| 12 | + |
| 13 | + /* |
| 14 | + * Initial point of entry. Public Sort of type T takes an array parameter of |
| 15 | + * type Comparable<? extends T>[]. This checks for null and base cases. If |
| 16 | + * neither are found it will pass it to the private overloaded method for |
| 17 | + * further sorting. |
| 18 | + * |
| 19 | + * @param Comparable<? extends T>[] items |
| 20 | + * |
| 21 | + * @throws NullPointerException if items is a null array, or has null value. |
| 22 | + */ |
| 23 | + |
| 24 | + public static <T> void sort(Comparable<? extends T>[] items) { |
| 25 | + // Test if items is null. |
| 26 | + if (items == null) { |
| 27 | + throw new NullPointerException("Invalid Array. Array cannot be null."); |
| 28 | + // Test if array is empty, or of size 1 and not [null]. |
| 29 | + } else if (items.length == 0 || (items.length == 1 && items[0] != null)) { |
| 30 | + return; |
| 31 | + } else { |
| 32 | + // Scan array for any additional null values. |
| 33 | + for (int i = 0; i < items.length; i++) { |
| 34 | + if (items[i] == null) { |
| 35 | + throw new NullPointerException("Invalid Array. Array values cannot be null."); |
| 36 | + } |
| 37 | + } |
| 38 | + // Pass valid array for sorting. |
| 39 | + sort(items, 0, items.length - 1); |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + /* |
| 44 | + * The private sort method calculates low, middle, and high indices of the |
| 45 | + * Comparable array. If the start index is lower than the high index, it will |
| 46 | + * continue to divide the array recursively and merge as needed. |
| 47 | + * |
| 48 | + * @param Comparable<? extends T>[] items, int low, int high |
| 49 | + * |
| 50 | + */ |
| 51 | + |
| 52 | + private static <T> void sort(Comparable<? extends T>[] items, int low, int high) { |
| 53 | + if (low < high) { |
| 54 | + int mid = (low + high) / 2; |
| 55 | + sort(items, low, mid); |
| 56 | + sort(items, mid + 1, high); |
| 57 | + merge(items, low, mid, high); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + /* |
| 62 | + * The merge method needed some tweaking in terms the of the parameter. Java |
| 63 | + * doesn't allow for generic arrays to be created. Type casting is used as a |
| 64 | + * workaround to this. Items has to be truncated to a raw type of comparable. |
| 65 | + * Instead of creating left and right subarrays to merge, indices of the index |
| 66 | + * array are used to traverse the array and compare one side to the other. A |
| 67 | + * temporary Array is used to collect and store the sorted values, and are then |
| 68 | + * casted in order to the items array. We use the temporary array to avoid |
| 69 | + * overwriting the items array which we are referencing. |
| 70 | + * |
| 71 | + * @param Comparable items, int low, int medium, int high |
| 72 | + */ |
| 73 | + |
| 74 | + private static <T> void merge(Comparable[] items, int low, int mid, int high) { |
| 75 | + Object[] tempArr = new Object[high - low + 1]; |
| 76 | + int i = low; |
| 77 | + int j = mid + 1; |
| 78 | + int k = 0; |
| 79 | + while (i <= mid && j <= high) { |
| 80 | + if (items[i].compareTo(items[j]) <= 0) |
| 81 | + tempArr[k] = items[i++]; |
| 82 | + else |
| 83 | + tempArr[k] = items[j++]; |
| 84 | + k++; |
| 85 | + } |
| 86 | + if (i <= mid && j > high) { |
| 87 | + while (i <= mid) |
| 88 | + tempArr[k++] = items[i++]; |
| 89 | + } else { |
| 90 | + while (j <= high) |
| 91 | + tempArr[k++] = items[j++]; |
| 92 | + } |
| 93 | + for (k = 0; k < tempArr.length; k++) { |
| 94 | + items[k + low] = (Comparable<?>) (tempArr[k]); // cast |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + /* |
| 99 | + * Main method used for testing various types of arrays to account for any |
| 100 | + * issues in live/production environment. |
| 101 | + */ |
| 102 | + |
| 103 | + public static void main(String[] args) { |
| 104 | + |
| 105 | + // Test Sort of Strings |
| 106 | + String[] sortWords = new String[] { "orange", "green", "blue", "red", "yellow", "purple" }; |
| 107 | + MergeSorter.sort(sortWords); |
| 108 | + |
| 109 | + // Test Sort of Integers |
| 110 | + Integer[] sortNumbers = new Integer[] { 5, 7, 3, 9, 4, 0, 1, 2, 8, 6 }; |
| 111 | + MergeSorter.sort(sortNumbers); |
| 112 | + |
| 113 | + // Test Sort of Empty Array |
| 114 | + Integer[] emptyArray = new Integer[0]; |
| 115 | + MergeSorter.sort(emptyArray); |
| 116 | + |
| 117 | + // Test Sort of Null Array |
| 118 | + Integer[] nullArray = null; |
| 119 | + MergeSorter.sort(nullArray); |
| 120 | + |
| 121 | + // Test Sort of Null value in Array |
| 122 | + Integer[] nullValueInArray = new Integer[] { 1, 5, 4, null, 3, 2 }; |
| 123 | + MergeSorter.sort(nullValueInArray); |
| 124 | + |
| 125 | + // Test Sort of 1-Value Array |
| 126 | + Integer[] oneNull = new Integer[] { null }; |
| 127 | + MergeSorter.sort(oneNull); |
| 128 | + |
| 129 | + // Test Sort of 1-Value Array |
| 130 | + Integer[] oneArray = new Integer[] { 1 }; |
| 131 | + MergeSorter.sort(oneArray); |
| 132 | + |
| 133 | + // Test Sort of Strings + Integers |
| 134 | + String[] mixedArray = new String[] { "chicken", "5", "cow", "3", "horse", "1", "goat", "4", "sheep", "2" }; |
| 135 | + MergeSorter.sort(mixedArray); |
| 136 | + |
| 137 | + // Test Sort of Negative + Positive Integers. |
| 138 | + Integer[] negativeIntegers = new Integer[] { -5, 5, -4, 4, -3, 3, -2, 2, -1, 1, 0 }; |
| 139 | + MergeSorter.sort(negativeIntegers); |
| 140 | + } |
| 141 | +} |
0 commit comments