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