归并排序和快速排序一样,都采用了分治的思想。将数组不断缩小,并行排序(递归),这样可以提高速度。那么归并又是怎样分治的呢?和快排一样,分为两个步骤:
1. 写一个函数,输入为两个有序的数组,经过函数后要求这两个数组合并成一个,并且有序。
2. 将原数组不断二分,将分开的两个数组作为参数传入1步奏的函数中。递归完毕函数就已经排序成功。
首先我们来看步骤1的函数:
void mergeArray(int *arr, int left, int mid, int right, int *temp)
{
int i, j, k;
i = left;
j = mid + 1;
k = 0;
while (i <= mid && j <= right)
{
if (arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
{
temp[k++] = arr[j++];
}
}
while (i <= mid)
{
temp[k++] = arr[i++];
}
while (j <= right)
{
temp[k++] = arr[j++];
}
for (i = 0; i != k; ++i)
{
arr[left + i] = temp[i];
}
}
函数很明确,arr为要排序的数组,left为要排序数组的首元素,mid为中点,right为要排序数组的末元素,temp为暂存数组。注意两点:
1. 我们步奏1提到的输入为两个数组,这里我们用一个数组,然后 left 至 mid 和 mid+1 至 right作为两个数组
2. 两个数原本是有序的,所以排序只需要比较两个数组的头元素,然后依次插入到temp中,最后将temp中保留的有序数组依次覆盖到arr的相应位置。
步骤2代码:
void mergeSort(int *arr, int left, int right, int *temp)
{
if (left < right)
{
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
mergeArray(arr, left, mid, right, temp);
}
}
步骤2很简单了,判断left是否小于right,如果小于则先再二分数组,然后再做合并的操作。为什么要先二分再合并呢?我们又要注意我们要合并的数组是两个有序数组,但是用户输入的数组肯定是没有顺序的,由于我们将二分递归放到了合并的前面,所以数组首先会被不停二分,直到只有两个元素,那么这两个元素是两个数组,一定是有序的,通过合并之后这两个数有序,第一层递归出来,数组元素变为两个而且有序,所以可以继续合并。大家要自己去领会这个过程。
这里附上百度上归并排序的示意图,比较好理解:
归并排序的时间复杂度与快排是一样的O(nlog2(n)),使用递归的排序方法一定要注意,由于递归调用,所以可能堆栈溢出,大家可以不停调整数组的大小测一下。
原文地址:http://blog.csdn.net/u013647382/article/details/46308751