我们先来了解归并的大致过程是什么
我们举一组例子:
我们设两组指针,start1和start2,end1和end2,start2=end+1我们首先让每组元素的个数为1,那么也就是说start1=end1,start2=end2.
我们另外开启一个数组来合并这两组数,定义一个临时数组tmpArr,
第一组数已经走完了,这下我们就可以直接把第二组数拉过来,放入tmpArr
现在e1和e2都没有动,所以我们根据e1,e2退出下两组要归并的数
同理可得,1和9分别放入tmpArr
4和7放入tmp
发现s2已经出了数组的范围了,所以我们现在只剩下一组数了,就是s1到e1(其实只有一个数,由于间隔为1)。现在我们就要重新考虑如何去解决一组数,因为我们的每组元素的个数是从1开始循环的,所以每次循环完毕后我们每组内数字都是有序的,这是我们的s1到e1肯定也是有序的,所以直接放在tmpArr数组中就可以了。
现在我们每组有两个数字,
s1与s2比较,把1放入tmpArr,s2往后走一步,s1继续和s2比较,把5放到tmpArr,s1往后走一步,s1和s2比较,把6放入tmpArr,,s1往后走一步,s1走完,直接把s2剩下的9放到tmpArr中,这两组就排序完毕了
s2与s1比较,把2放入tmpArr,s2往后走,第二组数字走完了,直接把第一组所有的数字都放到tmpArr中
接下来每组4个元素,同理可推最终就会完成排序。
代码实现:
public static void sort(int[] array) {
for (int i = 1; i < array.length; i*=2) {
mergesort(array,i);
}
}
public static void mergesort(int[] array,int gap) {
//定义临时数组
int[] tmpArr = new int[array.length];
int size = 0;
//定义两组指针
int s1 = 0;
int e1 = s1 + gap -1;
int s2 = e1 + 1;
int e2 = s2 + gap - 1 < array.length ? s2 + gap -1 : array.length-1;
//当有两组数时
while(s2 < array.length) {
while(s1 <= e1 && s2 <= e2) {
if(array[s1] < array[s2]) {
tmpArr[size++] = array[s1++];
} else {
tmpArr[size++] = array[s2++];
}
}
while(s1 <= e1) {
tmpArr[size++] = array[s1++];
}
while(s2 <= e2) {
tmpArr[size++] = array[s2++];
}
s1 = e2+1;
e1 = s1 + gap -1;
s2 = e1 + 1;
e2 = s2 + gap - 1 < array.length ? s2 + gap -1 : array.length-1;
}
//当有1组数
while(s1 < array.length) {
tmpArr[size++] = array[s1++];
}
for (int i = 0; i < array.length; i++) {
array[i] = tmpArr[i];
}
}
归并排序是两组数相当于两组数进行比较,然后排序,这样的交换是有间隔的,所以是不稳定的。
特点:不稳定
无论什么情况下,归并排序的时间复杂度为O(nlogn).
最优时间复杂度:
O(nlogn).
最坏时间复杂度:
O(nlogn).
平均时间复杂度:
O(nlogn).
我们可以清楚知道在该过程中我们借用了一个辅助数组tmpArr,所以空间复杂度为O(n)
空间复杂度:
O(n)