在学习归并排序之前,要先了解一下分治思想:
1.分治思想:即分而治之,将一个大问题分解成若干的小问题进行求解。
2.归并排序:
- 算法思路:将要排序的元素进行分组,直到不能分组为止;然后每部分进行排序,从少到多直到数组全部合并,此时数组有序
- 代码实现:
package www.first;
public class MergeSortTest {
public static void mergeSort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
public static void mergeSortInternal(int[] arr,int l,int r){
if(l>=r){
return;
}
int mid = (l+r)/2;
//分治
mergeSortInternal(arr,l,mid);
mergeSortInternal(arr,mid+1,r);
//合并
merge(arr,l,mid,r);
}
public static void merge(int[] arr,int l,int mid,int r){
int i = l;
int j = mid+1;
int[] temp = new int[r-l+1];
int k = 0;
while(i<=mid&&j<=r){
if(arr[i]<=arr[j]){
temp[k++] = arr[i++];
}else{
temp[k++] = arr[j++];
}
}
while(i<=mid){
temp[k++] = arr[i++];
}
while(j<=r){
temp[k++] = arr[j++];
}
//将排序后的数组拷贝回原来的数组
for(int n = 0;n<temp.length;n++){
arr[l+n] = temp[n];
}
}
public static void main(String[] args) {
int[] data = new int[]{1,2,3,1,54,3,4,4,6,5,64,7};
mergeSort(data);
for(int a:data){
System.out.print(a+" ");
}
}
}
结果如下:
3.归并排序总结:
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n),因为要开辟临时空间存储要排序的n个元素
- 稳定性:排序算法的稳定性其实取决于我们merge部分的代码,如下图:
在合并有序元素时,arr[i]<=arr[j]表示将小于和等于的数放到数组的前面,因为我们是从前往后进行比较遍历,所以相等元素的顺序不会发生改变,此时是稳定的排序算法。
4.归并排序的优化:
- 优化点:我们可以想一下,当合并有序数组的时候,如果前一个数组的最后一个元素小于等于后一个数组的第一个元素,即:arr[mid]<=arr[mid+1]。说明此时数组已经有序,不用再从前往后遍历比较,只需要直接将两部分拷贝回原数组就行了。
- 部分改动如下:
if(arr[mid]<=arr[mid+1]){
while(i<=mid){
temp[k++] = arr[i++];
}
while(j<=r) {
temp[k++] = arr[j++];
}
for(int n = 0;n<temp.length;n++){
arr[l+n] = temp[n];
}
return;
}