一、基本思想
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之),而且充分利用了完全二叉树的深度是log2n + 1的特性,因此效率比较高。其算法原理如下:对于给定的一组记录,利用递归与分治技术将数据序列划分成为越来越小的子序列,再对子序列排序,最后再用递归方法将排好序的子序列合并成为越来越大的有序序列
二、复杂度分析
一趟归并需要将数组 a[]中相邻的长度为h的有序序列进行两两归并.并将结果放到temp[]中,这需要将待排序列中的所有记录扫描一遍,因此耗费O(n),而又完全二叉树的深度可知,整个归并排序需要进行log2n次。因此总的时间复杂度为O(nlogn),而且这是归并排序算法中最好、最坏、平均的时间性能。
由于归并排序在归并过程中需要与原始序列同样数量的存储空间存放归并结果以及递归时深度为log2n,其空间复杂度为O(n+logn).
三、图文分析
以数组a[] = {14,12,15,13,11,16}为例,
经归并排序后得到的期望值:a[] = {11, 12, 13, 14, 15, 16}
以下拆分与归并过程图解:
注:图文来自另一篇博客:点击打开链接
四、算法设计思路
分而治之(divide - conquer);每个递归过程涉及三个步骤
第一, 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素.
第二, 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
第三, 合并: 合并两个排好序的子序列,生成排序结果.
五、代码实现
1.java代码实现:
package merge;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] a = {14,12,15,13,11,16};
//归并排序
mergeSort(a,0,a.length - 1);
//打印数组
System.out.println(Arrays.toString(a));
}
//归并排序
public static void mergeSort(int[] a, int low, int high){
if(low < high){
int mid = low + (high - low)/2;//从中间拆分成左右两边
mergeSort(a, low, mid); //继续递归拆分左边的序列
mergeSort(a, mid + 1, high);//继续递归拆分右边边的序列
merge(a, low, mid, high); //将两边的数据归并
}
}
//归并算法
private static void merge(int[] a, int low, int mid, int high) {
int i = low, j = mid + 1,k = 0;
int[] temp = new int[high - low + 1];
//左右边比较,数值小的数放入temp数组
while(i <= mid && j <= high){
if(a[i] < a[j]) {
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i <= mid) temp[k++] = a[i++];
// 把右边边剩余的数移入数组
while(j <= high) temp[k++] = a[j++];
// 把temp数组中的数覆盖a数组
for (int k2 = 0; k2 < temp.length; k2++) {
a[k2 + low] = temp[k2];
}
}
}
2.python实现:
#归并排序
def mergeSort(a, low, high):
if low < high:
mid = (high + low) // 2 #取要排序的序列的中间位置
mergeSort(a, low, mid) #继续递归拆分左边序列
mergeSort(a, mid + 1, high) #继续递归拆分右边序列
merge(a, low, mid, high) #拆分后归并排序
#归并函数
def merge(a, low, mid, high):
i = low
j = mid + 1
temp = []
#左右边比较,数值小的数放入temp数组
while i <= mid and j <= high :
if a[i] < a[j] :
temp.append(a[i])
i += 1
else:
temp.append(a[j])
j += 1
#把左边剩余的数移入数组
while i <= mid:
temp.append(a[i])
i += 1
#把右边边剩余的数移入数组
while j <= high:
temp.append(a[j])
j += 1
#将temp列表中的数全部复制到原列表a中对应的索引位置
for k in range(len(temp)):
a[k + low] = temp[k]
a = [14,12,15,13,11,16] #要排序的列表
mergeSort(a, 0, len(a) - 1)
print(a)
3.C++实现(与java代码基本一致)
#include <iostream>
using namespace std;
//打印数组
void printArray(int a[], int len)
{
for(int i = 0;i < len;i++)
{
cout<<a[i]<<" ";
}
}
//归并算法
void merge(int a[], int low, int mid, int high)
{
int i = low, j = mid + 1, k = 0, len = high - low + 1;
int *temp = new int[len];
//左右边比较,数值小的数放入temp数组
while(i <= mid && j <= high)
{
if(a[i] < a[j]) {
temp[k++] = a[i++];
}
else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i <= mid) temp[k++] = a[i++];
// 把右边边剩余的数移入数组
while(j <= high) temp[k++] = a[j++];
// 把temp数组中的数覆盖a数组
for (int w = 0; w < len; w++)
{
a[w + low] = temp[w];
}
}
//归并排序
void mergeSort(int a[], int low, int high)
{
if(low < high)
{
int mid = low + (high - low)/2;//从中间拆分成左右两边
mergeSort(a, low, mid); //继续递归拆分左边的序列
mergeSort(a, mid + 1, high);//继续递归拆分右边边的序列
merge(a, low, mid, high); //将两边的数据归并
}
}
int main(int argc, char* argv[])
{
int a[] = {14,12,15,13,11,16};
int len = sizeof(a)/sizeof(int); //计算数组a的长度
mergeSort(a, 0 , len-1); //归并排序
printArray(a, len); //打印数组
return 0;
}