本文介绍了归并排序的基本思想,递归方法的一般写法,最后一步步手写归并排序,并对其性能进行了分析。
基本思想
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。即先使每个子序列有序,再将已有序的子序列合并,得到完全有序的序列。这里给出一种递归形式的归并排序实现。
递归方法的一般写法
递归方法的书写主要有三步:
- 明确递归方法的功能边界;
- 得到递归的递推关系;
- 给定递归的终止条件。
递归方法均可按照这三步进行,切忌不要陷入递归实现的细节中。下面以归并排序算法的书写为例,来谈一下递归方法的具体写法。
手写归并排序
首先,明确递归方法的功能,这里我们定义方法的功能为,给定一个数组及左右边界,方法完成数组边界内元素的排序,如下:
private static void mergeSort(int[] arr,int left,int right);
先假设我们已经有了这么一个方法,不用管具体的实现。 接着,寻找递推关系,什么是递推关系呢?就是如何由子问题的求解,来得到原问题的求解,还是举例说明,有如下的数组




/**
*
* @param arr 要合并的数组
* @param left 左边界
* @param mid 中间的分界
* @param right 右边界
*/
private static void merge(int[] arr,int left,int mid,int right){
int[] helpArr = new int[right - left + 1];//首先定义一个辅助数组
int lPoint = left;//左指针
int rPoint = mid + 1;//右指针
int i = 0;//辅助指针
while(lPoint <= mid && rPoint <= right){//比较并填充辅助数组
if(arr[lPoint] <= arr[rPoint])
helpArr[i++] = arr[lPoint++];
else
helpArr[i++] = arr[rPoint++];
}
while(lPoint <= mid){//将剩余元素填充至辅助数组
helpArr[i++] = arr[lPoint++];
}
while(rPoint <= right){
helpArr[i++] = arr[rPoint++];
}
for(int j = 0;j < helpArr.length;j ++){//将辅助数组中的元素回填至原数组
arr[left + j] = helpArr[j];
}
}
最后,确定终止条件,一般是数组为空或者数组中只有一个元素,返回即可。 现在我们可以写出整个归并排序的代码了,如下:
private static void mergeSort(int[] arr,int left,int right){
if(arr == null || right == left)//终止条件
return ;
int mid = left + (right - left) / 2;//确定分割的边界
mergeSort(arr,left,mid);//对左半部分调用递归方法,使其有序
mergeSort(arr,mid + 1,right);//对右半部分调用递归方法,使其有序
merge(arr,left,mid,right);//合并左右两部分,使整个数组有序
}
为了保证形式的统一,再对函数进行一下封装,如下,这就是我们的归并排序了。
/**
* 归并排序算法
* @param arr
*/
public static void mergeSort(int[] arr){
mergeSort(arr,0,arr.length - 1);//调用写好的递归版归并排序方法
}
至此,我们便完成了归并排序算法的代码实现。
性能分析
在分析归并排序算法性能之前,先介绍几个基础的概念。 时间复杂度:一个算法执行所消耗的时间; 空间复杂度:运行完一个算法所需的内存大小; 原地排序:在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。 非原地排序:需要利用额外的数组来辅助排序。 稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。 非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。 下面我们分析下归并排序算法的性能。 首先是时间复杂度。归并排序算法在排序时首先将问题进行分解,然后解决子问题,再合并,所以总时间=分解时间+解决子问题时间+合并时间。分解时间就是把一个数组分解为左右两部分,时间为一常数,即O(1);解决子问题时间是两个递归方法,把一个规模为n的问题分成两个规模分别为n/2的子问题,时间为2T(n/2);合并时间复杂度为O(n)。所以总时间T(n)=2T(n/2)+O(n)。这个递归问题的时间复杂度可以用下面的公式来计算

觉得文章有用的话,点赞+关注呗,好让更多的人看到这篇文章,也激励博主写出更多的好文章。 更多关于算法、数据结构和计算机基础知识的内容,欢迎扫码关注我的原创公众号「超悦编程」。
