归并排序核心思想:
合并两个有序数组为一个有序数组是很轻松的。
步骤:
- 将一个数组不断的从中间分成两个数组
- 对分后的两个数组不断重复步骤一,直到数组的长度为1
- 对分成的两个数组进行合并,此时就是分成的两个数组都已经是有序的
这段时间复习了一下数据结构的知识,对归并排序进行了重新的编写,采用的java语言,但通过测试20万个随机数据发现,归并排序的效率和选择排序的效率竟然差不多,对此我陷入了深深的怀疑,究竟是哪个地方出错了呢?
在此,先贴出原代码:
public int[] mergeSort(int[] array) {
//采取先分后治的思想
if(array.length > 1){
//分
int[] left = mergeSort(leftArray(array));
int[] right = mergeSort(rightArray(array));
//合
array = mergeArray(left,right);
}
return array;
}
//获取数组的左半边
private int[] leftArray(int[] a){
int[] b = new int[(a.length + 1)/2];
for (int i = 0; i < b.length; i++) {
b[i] = a[i];
}
return b;
}
//获取数组的右半边
private int[] rightArray(int[] a){
int[] b = new int[a.length/2];
int lenA = (a.length + 1) / 2;
for (int i = 0; i < b.length; i++) {
b[i] = a[i + lenA];
}
return b;
}
//合并两有序数组
private int[] mergeArray(int[] left, int[] right){
int[] a = new int[left.length + right.length];
int indexL = 0;
int indexR = 0;
int lenL = left.length;
int lenR = right.length;
for (int i = 0; i < a.length; i++) {
a[i] = left[indexL] < right[indexR] ? left[indexL++] : right[indexR++];
if(indexL == lenL || indexR == lenR){
//满足条件跳出循环
break;
}
}
if(indexL == lenL){
//将右侧数组依次装进合并数组内
for (int i = indexL + indexR; i < a.length; i++) {
a[i] = right[indexR++];
}
}else if(indexL == indexR){
//将左侧数组依次装进合并数组内
for (int i = indexL + indexR; i < a.length; i++) {
a[i] = left[indexL++];
}
}
return a;
}
经过思索可能造成速度缓慢的因素(打死我都不认为是归并的思想出了问题),在共产党的带领下,我揪出了那个问题的凶手,没错,凶手只有一个,那就是不断的申请新数组,开辟空间是很耗时的,难怪效率那么慢了,原来大部分的时间都用来开矿了!惭愧,惭愧,这就是当代程序员滥用空间的血淋淋的例子,怀念远古时期,前辈们用个变量都要思考着个变量是否必要使用,生怕造成一点空间的浪费,额,扯远了。。。
知道了问题的原因,那就要着手去解决这个问题,既然开辟空间耗时,那就不开辟空间了,尽量用原来的数组空间,顶多加个临时数组,那这样问题能得到解决吗?说实话,我现在也不知道运行起来效率能提升多少,但提升是肯定的,期待中…
修改后的代码,如下:
public int[] mergeSort(int[] array) {
if(array == null || array.length == 0){
return null;
}
//该temp为了减少doMerge中的创建临时数组造成的性能下降而提前设置的
int[] temp = new int[array.length];
merge(array, 0, array.length - 1, temp);
return array;
}
private void merge(int[] array, int start, int end, int[] temp){
if(start < end){
//分
int mid = (start + end) >> 1;
merge(array, start, mid, temp);
merge(array, mid + 1, end, temp);
//治
doMerge(array,start,mid,end, temp);
}
}
private void doMerge(int[] array, int start, int mid, int end, int[] temp){
int left = start;
int right = mid + 1;
int index = start;
while(left <= mid && right <= end){
temp[index++] = array[left] > array[right] ? array[left++] : array[right++];
}
//将剩余一边的数组全部依次导入
while(left <= mid){
temp[index++] = array[left++];
}
while (right <= end){
temp[index++] = array[right++];
}
//将临时数组导入array
for (int i = start; i <= end; i++) {
array[i] = temp[i];
}
}
话不多说,有什么问题跑起来再说,当当当,结果如下:
OMG!怎么会这样,啊啊啊啊啊啊啊啊!
我一定是看错了,不行我要去看眼科,什么情况啊,我改动了那么大,怎么会一点变化都没有,而且跟选择排序时间基本一样。
我好像,发现了什么,跟选择排序的时间基本一样=.=
呵呵,归并排序调用了选择排序
辣眼睛,=.=
改回来吧,看看改后的归并排序效率有多高
再对比下,改之前的归并排序时间
唔,证明我的改动还是相当赞的,20完个数据快了80%,现在加大数据量,看看效果如何。
先看老版的一亿个随机数的排序时间:
在看新版的一亿个随机数的排序时间
唔,大体这样了,超大数据的时候快了25%吧,还是没达到我的预期,我觉得起码要快两倍呢,桑心