Here's your original algorithm with some corrections and stylistic improvements:
public class MergeSort {
public static void main(String[]args) {
int[] nums = {12,9,4,99,120,1,3,10};
mergeSort(nums);
System.out.println(java.util.Arrays.toString(nums));
// "[1, 3, 4, 9, 10, 12, 99, 120]"
}
static void mergeSort(int[] arr) {
mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
}
static void mergeSort(int[] arr, int low, int high, int[] buff){
if (low >= high) {
return;
}
int mid = (low + high) >>> 1;
mergeSort(arr, low, mid, buff);
mergeSort(arr, mid+1, high, buff);
for (int left = low, right = mid + 1, i = low; i <= high; i++) {
if (right > high || left <= mid && arr[left] <= arr[right]) {
buff[i] = arr[left++];
} else {
buff[i] = arr[right++];
}
}
for (int i = low; i <= high; i++) {
arr[i] = buff[i];
}
}
}
Unlike Eyal's implementation, where the role of src
and dst
are swapped back and forth through the levels of recursion, here we always sort to the same array object arr
, and the array object buff
is always used only as a temporary buffer for merging (and consequently, there's a copy phase after the merge phase). This is still O(N log N)
, but Eyal's more advanced implementation will be a constant-factor improvement.
On the merge loop
Essentially you have a left
index for the left subarray, and right
index for the right subarray, and you pick the right element from either the left
or right
to put into buff
.
The valid range of elements are (inclusive bounds):
left = low...mid
for left subarray
right = mid+1...high
for right subarray
To evaluate which element to pick, consider the condition under which the left
element is picked. It happens when:
- There are no more element to pick from the right subarray (i.e.
right > high
)
- OR (conditionally) there's still an element to pick from the left subarray (i.e.
left <= mid
) and (conditionally) that element is less than or equal to the element from the right subarray (i.e. arr[left] <= arr[right]
).
It's important to use short-circuiting conditional-and &&
(JLS 15.23) and conditional-or ||
(JLS 15.24) operators here, and to order these conditions accordingly. Otherwise you'll get an ArrayIndexOutOfBoundsException
.
Related questions
On finding average between two numbers
It's common to see the following:
int mid = (low + high) / 2; // BROKEN! Can result in negative!
The problem is that nowadays, arrays/lists etc can easily exceed 230 elements, and the above would cause an overflow and results in a negative number.
The new idiom, as advocated by Josh Bloch, is the following:
int mid = (low + high) >>> 1; // WORKS PERFECTLY!
This uses the unsigned right shift operator (JLS 15.19); it handles any overflow on the addition correctly for our need.
References
Related questions
On array declarations
Do not make a habit of declaring arrays like this:
int x[];
You should instead put the brackets with the type, rather than with the identifier:
int[] x;
Related questions