归并排序是另一种运用分治法排序算法。与快速排序一样,它依赖于元素之间的比较来排序。但是,归并排序需要额外的存储空间来完成排序过程。
同样可以用分治法的思想将排序分为三个步骤。
- 分:将数据集等分为两半。
- 治:分别在两个部分用递归的方式继续使用归并排序法。
- 合:将分开的两个部分合并成一个有序的数据集。
归并排序与其他排序最大的不同在于,它的归并过程。这个过程就是将两个有序的数据集合并成一个有序的数据集。正如我们看到的,合并两个有序数据集的过程是高效的,因为我们只需要遍历一次即可。
归并排序本质上是将无序元素分割成许多个包含一个元素的集,然后不断地将这些小集合合并,直到一个新的大有序数据集生成。
根据上图实现DEBUG DEMO如下:
import java.util.Arrays;
/**
* 归并排序
*
* @author kevin LUAN
*
*/
public class GBSortDemo {
public static void main(String[] args) {
Integer[] arrays = new Integer[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
Integer[] dest = new Integer[arrays.length];
mgSort(arrays, dest, 0, arrays.length);
System.out.println("Sort result:"+Arrays.toString(dest));
}
private static void mgSort(Integer[] arr, Integer[] dest, int low, int high) {
System.out.println(Arrays.toString(arr));
int middle = getMiddle(low, high);
if (high - low < 3) {
copyData(arr, dest, low, high);
return;
}
Integer[] left = new Integer[middle - low];
if (left.length > 1) {
System.arraycopy(arr, low, left, 0, left.length);
mgSort(left, dest, 0, left.length);
}
Integer[] right = new Integer[high - middle];
if (right.length > 0) {
System.arraycopy(arr, middle, right, 0, right.length);
mgSort(right, dest, 0, right.length);
}
}
private static void sort(Integer[] left) {
for (int i = 0; i < left.length; i++) {
for (int j = i + 1; j < left.length; j++) {
if (left[j] < left[i]) {
left[j] = swap(left, i, left[j]);
}
}
}
}
private static void copyData(Integer[] left, Integer[] dest, int low, int high) {
sort(left);
Integer start = 0;
boolean isStart = false;
for (int l = low; l < high; l++) {
int val = left[l];
for (int i = 0 + start; i < dest.length; i++) {
if (dest[i] == null) {
dest[i] = val;
break;
} else if (dest[i] > val) {
if (isStart == false) {
start = i;
}
val = swap(dest, i, val);
}
}
}
}
private static int swap(Integer[] dest, int i, int val) {
try {
return dest[i];
} finally {
dest[i] = val;
}
}
public static int getMiddle(int low, int high) {
int total = high - low;
if (total % 2 == 0) {
return total / 2;
} else {
return total / 2 + 1;
}
}
}
运行Main函数:
[1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
[1, 3, 5, 7, 9]
[1, 3, 5]
[1, 3]
[5]
[7, 9]
[2, 4, 6, 8, 0]
[2, 4, 6]
[2, 4]
[6]
[8, 0]
Sort result:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
进一步优化,去掉System.arraycopy()代码如下:
import java.util.Random;
/**
* 归并排序
*
* @author kevin
*
*/
public class GBSort {
public static Integer[] randomArray(int total) {
Random random = new Random();
Integer[] arrays = new Integer[total];
for (int i = 0; i < total; i++) {
arrays[i] = random.nextInt(10);
}
return arrays;
}
public static void main(String[] args) {
Integer[] arrays = randomArray(10000);
long start = System.currentTimeMillis();
arrays = sort(arrays);
System.out.println("use time:" + (System.currentTimeMillis() - start));
}
private static Integer[] sort(Integer[] arr) {
Integer[] dest = new Integer[arr.length];
mgSort(arr, dest, 0, arr.length);
return dest;
}
private static void mgSort(Integer[] arr, Integer[] dest, int low, int high) {
int middle = getMiddle(low, high);
if (high - low < 3) {
copyData(arr, dest, low, high);
return;
}
Integer left = middle - low;
if (left > 1) {
mgSort(arr, dest, low, low + left);
}
Integer right = high - middle;
if (right > 0) {
mgSort(arr, dest, middle, high);
}
}
private static void sort(Integer[] left, int low, int high) {
for (int i = low; i < high; i++) {
for (int j = i + 1; j < high; j++) {
if (left[j] < left[i]) {
left[j] = swap(left, i, left[j]);
}
}
}
}
/**
*
* @param left
* @param dest
* @param low
* 低位下标
* @param high
* 高位+1
*/
private static void copyData(Integer[] src, Integer[] left, int low, int high) {
if (high - low > 1) {
sort(src, low, high);
}
int startIndex = 0;
for (int l = low; l < high; l++) {
int val = src[l];
for (int i = startIndex = calAddIndex(left, startIndex, low, val); i < high; i++) {
if (left[i] == null) {
left[i] = val;
break;
} else if (left[i] > val) {
val = swap(left, i, val);
}
}
}
}
public static int calAddIndex(Integer ascArray[], int startIndex, int endIndex, Integer val) {
int start = startIndex;
int end = endIndex - 1;
int oldStart = -1;
int oldEnd = -1;
if (end == 0) {
return 0;
}
while (true) {
if (start == oldStart && end == oldEnd || end - start <= 1) {
return start;
}
oldEnd = end;
oldStart = start;
int middleIndex = (end - start) / 2;
if (ascArray[start + middleIndex] > val) {// 仍然大于改值需要向前继续二分查找
end = start + middleIndex;
} else if (ascArray[start + middleIndex] < val) {// 小于该值需要往后继续二分查找
start = middleIndex + start;
} else {
return middleIndex + start;
}
}
}
private static int swap(Integer[] dest, int i, int val) {
try {
return dest[i];
} finally {
dest[i] = val;
}
}
public static int getMiddle(int low, int high) {
int total = high - low;
int middle;
if (total % 2 == 0) {
middle = total >>> 1;
} else {
middle = (total >>> 1) + 1;
}
if (low == 0) {
return middle;
} else {
return middle + low;
}
}
}