-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMergeSort.java
More file actions
213 lines (189 loc) · 8.53 KB
/
MergeSort.java
File metadata and controls
213 lines (189 loc) · 8.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import java.util.Arrays;
import java.util.Comparator;
/**
* This class is a collection of merge sorts, with each sort method and its helper methods
* another implementation
*
* Merge Sort is a divide and conquer algorithm known for its stability with worst, average,
* and even best case of O(nlogn)
*
* 1. Divide: If input size is smaller than a certain threshold (1 or 2 items), solve and
* return the solution. Otherwise, divide the input into two or more subsets
* 2. Conquer: Recursively solve the subproblems with the given subsets
* 3. Combine: Take the solutions to the subproblems and merge them into a larger solution
* to the original problem
*
*/
public class MergeSort {
/**
* Merges the contents of first half of subarray withh second half of another subarray
* into a properly sized array
* @param <K>
* @param x - First subarray
* @param y - Second subarray
* @param a - Main Array to sort into
* @param C - The Comparator
*/
public static <K> void merge(K[] x, K[] y, K[]a, Comparator<K> C){
int i = 0, j = 0; // Will represent the index for each subarray, i for x and j for y
//Keep looping until each contents of both subarray is still not the size of the main array
while(i + j < a.length){
/**
* compare(x,y) returns -1, 0, 1 for the cases respectively:
* I) x < y II) x == y III) x > y
*
* So if x < y, or x[i] < y[j], then -1, which is -1 < 0 so x[i] will be copied
* into main array. Otherwise, y[j] was a lower value, so it will be copied into
* main array
*/
if(j == y.length || (i < x.length && C.compare(x[i], y[j]) < 0)){
a[i+j] = x[i++]; //Copy ith element of x into main array a, increment i
} else {
a[i+j] = y[j++]; //Copy jth element of y into main array a, increment j
}
}
}
public static <K> void mergeSort(K[] arr, Comparator<K> C){
int l = arr.length;
// Case 1: Array is Trivially sorted
if ( l < 2) { return; }
/** Divide **/
int mid = l/2; // Mid point of the array
K[] arr1 = Arrays.copyOfRange(arr,0,mid); // Copy First Half of Array
K[] arr2 = Arrays.copyOfRange(arr,mid,l); // Copy of Second Half of Array
/** Conquer **/
//Using recursion, sort the halves of the array
mergeSort(arr1,C);
mergeSort(arr2,C);
/** Merge **/
merge(arr1, arr2, arr, C); //Merge the sorted halves back into thhe original
}
/******************** Second MergeSort Implementation ********************/
/**
* One way to solve a problem is to divide it into independent parts, conquer them independently,
* and then use the solutions for the parts to develop a solution for the full problem
*
* Base Case: If The subarray length is 0 or 1, it is already sorted
* Reduction Step: Otherwise, compute mid = lo + (hi - lo)/2, recursively sort the two sub arrays
* a[lo,mid) and a[mid, hi) and merge them
*
* Source: Sedgewick, R., & Wayne, K. (2017).
* Introduction to programming in java: An interdisciplinary approach. Addison-Wesley.
* @author Sedgewick, R.
* @author Wayne, K.
*
* Private static helper Method
* @param <K> The generic type
* @param a The main array to sort
* @param aux The auxiliay array that temporarily holds the results
* @param lo The smallest index of the array to sort for the first half
* @param hi The largest index of the array to sort for the second half
*/
@SuppressWarnings("unchecked")
private static <K> void sort(Comparable<K>[] a, Comparable<K>[] aux, int lo, int hi){
//Sorting a[lo, hi) which is the subarray to sort
if(hi - lo <= 1) return; //Array is Trivially sorted
int mid = lo + (hi-lo)/2; //Store the middle index
sort(a,aux,lo,mid); // sort subarray a[lo, mid)
sort(a,aux,mid,hi); // sort subarray a[mid, hi)
//Set aux[k] to either a[i] or a[j] then increments both k and index of subarray used
/**
* The first 2 cases involves whether i or j has reached the end of its subarray, then
* set aux[k] from the other in these edge cases. Otherwise, compare the values with
* each subarray and set aux[k] to the smaller of the two, either a[i] or a[j].
*
* After all this, the sorted result auxiliary array, aux[], is copied back to original
* array. This recursive method ensures that two subarrays are sorted before the merge.
*/
int i = lo, j = mid;
for(int k = lo; k < hi; k++){
if (i == mid) { //Index i has reached the end of its subarray
aux[k] = a[j++]; //aux[k] is set from the other a[j]
}
else if (j == hi) { //Index j has reached the end of its subarray
aux[k] = a[i++]; //aux[k] is set from the other a[i]
}
else if(a[j].compareTo((K)a[i]) < 0) { //Which subarray has the smaller value?
// a[j] < a[i] returned either -1 or 0, so a[j] is the smaller value
aux[k] = a[j++]; //Set aux[k] as subarray a[j] since it is smaller
} else{
// a[j] < a[i] returned 1, so 1 < 0 is false, so a[j] is the greater value
aux[k] = a[i++]; //Set aux[k] as subarray a[i] since it is smaller
}
}
//Repopulate the Original Array with the sorted result of auxiliary array
for(int k = lo; k < hi; k++){
a[k] = aux[k];
}
}
/**
* The main recursive call
* @param <K>
* @param a
*/
@SuppressWarnings("unchecked") //Small change from original, since applying generics
public static <K> void sort(Comparable<K>[] a){
Comparable<K>[] aux = (Comparable<K>[]) new Object[a.length]; //auxiliary array
sort(a, aux, 0, a.length);
}
/******************** Third MergeSort Implementation ********************/
/**
* The Bread and Butter of the mergesort. Here we conquer and merge
* @param arr Incoming caller array
* @param aux Auxliary array to store sorted values to
* @param low The lowest index
* @param mid Mid point
* @param high The highest index
*/
private void merge(int[] arr, int[] aux, int low, int mid, int high){
/* Copy Both halves into auxiliary array */
for(int i = low; i <= high; i++){
aux[i] = arr[i];
}
//Create runners to keep track of the start of the left and right halves
int left = low;
int right = mid + 1;
int curr = low;
/**
* Iterate through auxiliary array and compare left and right halves, copying
* the smaller element of the two halves into the original array.
*/
while(left <= mid && right <= high){ //While runners are not at end of their subarray
arr[curr] = (aux[left] <= aux[right]) ? aux[left++] : aux[right++];
// The equivalent of the ternary operator statement is below
// if(aux[left] <= aux[right]){
// arr[curr] = aux[left];
// left++;
// } else {
// arr[curr] = aux[right];
// right++;
// }
curr++;
}
/** Copy the rest of the left side of the auxiliary array into the target array
* The right side does not need to be copied since it is already there.
*/
int restOfArray = mid - left;
for(int i = 0; i <= restOfArray; i++){
arr[curr + i] = aux[left +i];
}
}
/** Divide **/
private void mergesort(int[] arr, int[] aux, int low, int high) {
if (low < high) {
int mid = (low + high)/2;
mergesort(arr, aux, low, mid); //Sort the left half
mergesort(arr,aux, mid+1, high); //Sort the right half
merge(arr, aux, low, mid, high); //Merge them
}
}
/**
* Starts the mergesort with only incoming parameter is the integer array.
* @param arr The original integer array to sort
*/
public void mergesort(int[] arr) {
int[] aux = new int[arr.length];
mergesort(arr, aux, 0, arr.length-1);
}
/******************** Fourth MergeSort Implementation ********************/
} //EOF