上一篇博文排序1主要讲的是关于评定算法性能的一些指标,以及时间复杂为O(n2)的排序算法。这一篇博文我重点讲述世家复杂度为O(nlogn)的两个经典算法—归并排序 和快排
关于这两个算法 网上的资料很多 在面试中也会经常会被问到(特别是快排),这里我结合自己的理解和总结,写的不好的地方 请多指教。
归并排序(MergeSort)
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
归并排序的核心思想:如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
我们可以看到归并排序的数据用书的结构来表示后,分和治都是一颗满二叉树,高度为O(log2n)。
分:采用递归的方法进行分解。
将待排序数组中的n个数据分解为n个有序的子数组,采用递归的方法。
首先,我们写出其递推公式及其终止条件:
递推公式:merge_sort(p…r) = merge(merge_sort(p...q),merger_sort(q+1,...r))
终止条件:p>=r
由递推公式写出"分"的代码:
public static void merge_sort(int[] a,int p,int r){
//递归终止条件
if(p>=r) return;
//取p到r之间的中间位置q
int q=(p+r)/2;
//分治递归
merge_sort(a,p,q);
merge_sort(a,q+1,r);
//将a[p,...,q]和a[q+1,..,r]合并为a[p,..,r]
merge(a,p,q,r);
}
治:合并相邻有序子序列
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
在归并排序中最耗时的就是治(将两个小数组合并为大数组的过程)
治的java代码:
private static void merge(int[] arr,int left,int mid,int right){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
int[] temp=new int[right-left+1]; //申请一个大小和a[left,..,right]一样大的数组
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
归并排序的完整代码
package SortAlgorithm;
/**
* 归并排序(体现分治的思想 利用到递归)
* 归并排序是一个稳定的算法(值相同的元素,再合并前后的先后顺序不变)
归并排序的时间复杂度是O(nlogn),这里值得注意的是,归并排序的执行效率和要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不顾那是最好情况,最坏情况,还是平均情况,时间复杂度都是O(nlogn)
归并排序的空间复杂度是O(n),归并排序是非原地排序算法
* @author xjh 2018.10.22
*/
public class Sort02ToMergeSort {
public static void main(String[] args) {
int[] a={12,2,3,10,4,8,13,3};
int n=a.length;
mergeSort(a,n);
for(int i=0;i<n;i++){
System.out.print(a[i]+" ");
}
System.out.println();
}
/**
* 归并排序
* @param a
* @param n
*/
public static void mergeSort(int[] a,int n){
merge_sort(a,0,n-1);
}
public static void merge_sort(int[] a,int p,int r){
//递归终止条件
if(p>=r) return;
//取p到r之间的中间位置q
int q=(p+r)/2;
//分治递归
merge_sort(a,p,q);
merge_sort(a,q+1,r);
//将a