- 简介
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
- 算法描述
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
归并排序(Merge Sort)就是利用归并思想对数列进行排序。根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式
(一、)从上往下
- C语言实现
/*
* 将一个数组中的两个相邻有序区间合并成一个
*
* 参数说明:
* a -- 包含两个有序区间的数组
* start -- 第1个有序区间的起始地址。
* mid -- 第1个有序区间的结束地址。
* end -- 第2个有序区间的结束地址。
*/
void merge(int a[], int start, int mid, int end) {
int *temp = (int *)malloc((end - start + 1) * sizeof(int)); // tmp是汇总2个有序区的临时区域
int i = start; // 第1个有序区的起始索引
int j = mid + 1; // 第2个有序区的起始索引
int k = 0; // 临时区域的索引
while(i <= mid && j <= end) {
if (a[i] <= a[j]) {
temp[k++] = a[i++];
}else {
temp[k++] = a[j++];
}
}
while(i <= mid) {
temp[k++] = a[i++];
}
while(j <= end) {
temp[k++] = a[j++];
}
// 将排序后的元素,全部都整合到数组a中。
for (i = 0; i < k; i++) {
a[start + i] = temp[i];
}
free(temp);
}
/*
* 归并排序(从上往下)
*
* 参数说明:
* a -- 待排序的数组
* start -- 数组的起始地址
* endi -- 数组的结束地址
*/
void mergeSortUpToDown(int a[], int start, int end) {
if(a == NULL || start >= end) {
return ;
}
int mid = (end + start) / 2;
mergeSortUpToDown(a, start, mid); //递归排序a[start...mid]
mergeSortUpToDown(a, mid + 1, end); //递归排序a[mid + 1...end]
// a[start...mid] 和 a[mid...end]是两个有序空间,
// 将它们排序成一个有序空间a[start...end]
merge(a, start, mid, end);
}
//调用
int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
mergeSortUpToDown(a, 0, 7);
for(i=0; i<8; i++)
printf("%d ", a[i]);
printf("\n");
- OC实现
- (void)mergeWithNumbers:(NSMutableArray *)numbers startIndex:(int)startIndex midIndex:(int)midIndex endIndex:(int)endIndex {
NSMutableArray *temp = [NSMutableArray array];
int i = startIndex, j = midIndex + 1, k = 0;
while (i <= midIndex && j <= endIndex) {
if ([numbers[i] intValue] <= [numbers[j] intValue]) {
temp[k++] = numbers[i++];
}else {
temp[k++] = numbers[j++];
}
}
while (i <= midIndex) {
temp[k++] = numbers[i++];
}
while (j <= endIndex) {
temp[k++] = numbers[j++];
}
for (int i=0; i < k; i++) {
numbers[startIndex + i] = temp[i];
}
temp = nil;
}
- (void)mergeSortUpToDownWithNumbers:(NSMutableArray *)numbers startIndex:(int)startIndex endIndex:(int)endIndex {
if (numbers.count == 0 || startIndex >= endIndex) {
return;
}
int midIndex = (startIndex + endIndex) / 2;
[self mergeSortUpToDownWithNumbers:numbers startIndex:startIndex endIndex:midIndex];
[self mergeSortUpToDownWithNumbers:numbers startIndex:midIndex + 1 endIndex:endIndex];
[self mergeWithNumbers:numbers startIndex:startIndex midIndex:midIndex endIndex:endIndex];
}
//调用
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(10),@(4),@(8),@(99),@(16),@(19),@(3), nil];
[self mergeSortUpToDownWithNumbers:array startIndex:0 endIndex:6];
NSLog(@"%@",array);
(二、)从下往上
- C语言实现
/*
* 将一个数组中的两个相邻有序区间合并成一个
*
* 参数说明:
* a -- 包含两个有序区间的数组
* start -- 第1个有序区间的起始地址。
* mid -- 第1个有序区间的结束地址。
* end -- 第2个有序区间的结束地址。
*/
void merge(int a[], int start, int mid, int end) {
int *temp = (int *)malloc((end - start + 1) * sizeof(int)); // tmp是汇总2个有序区的临时区域
int i = start; // 第1个有序区的起始索引
int j = mid + 1; // 第2个有序区的起始索引
int k = 0; // 临时区域的索引
while(i <= mid && j <= end) {
if (a[i] <= a[j]) {
temp[k++] = a[i++];
}else {
temp[k++] = a[j++];
}
}
while(i <= mid) {
temp[k++] = a[i++];
}
while(j <= end) {
temp[k++] = a[j++];
}
// 将排序后的元素,全部都整合到数组a中。
for (i = 0; i < k; i++) {
a[start + i] = temp[i];
}
free(temp);
}
/*
* 对数组a做若干次合并:数组a的总长度为len,将它分为若干个长度为gap的子数组;
* 将"每两个相邻的子数组" 进行合并排序。
*
* 参数说明:
* a -- 待排序的数组
* len -- 数组的长度
* gap -- 子数组的长度
*/
void mergeGroups(int a[], int len, int gap) {
int i;
int doubleLen = 2 * gap; // 两个相邻的子数组的长度
// 将"每2个相邻的子数组" 进行合并排序。
for(i = 0; i + doubleLen - 1 < len; i += (doubleLen)) {
merge(a, i, i + gap - 1, i + doubleLen - 1);
}
// 若 i + gap - 1 < len - 1,则剩余一个子数组没有配对。
// 将该子数组合并到已排序的数组中。
if (i + gap - 1 < len - 1) {
merge(a, i, i + gap - 1, len - 1);
}
}
/*
* 归并排序(从下往上)
*
* 参数说明:
* a -- 待排序的数组
* len -- 数组的长度
*/
void mergeSortDownToUp(int a[], int len) {
int n;
if (a == NULL || len <= 0) {
return ;
}
for(n = 1; n < len; n *= 2) {
mergeGroups(a, len, n);
}
}
//调用
int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
mergeSortDownToUp(a, 8);
for(i=0; i<8; i++)
printf("%d ", a[i]);
printf("\n");
- OC实现
- (void)mergeWithNumbers:(NSMutableArray *)numbers startIndex:(int)startIndex midIndex:(int)midIndex endIndex:(int)endIndex {
NSMutableArray *temp = [NSMutableArray array];
int i = startIndex, j = midIndex + 1, k = 0;
while (i <= midIndex && j <= endIndex) {
if ([numbers[i] intValue] <= [numbers[j] intValue]) {
temp[k++] = numbers[i++];
}else {
temp[k++] = numbers[j++];
}
}
while (i <= midIndex) {
temp[k++] = numbers[i++];
}
while (j <= endIndex) {
temp[k++] = numbers[j++];
}
for (int i=0; i < k; i++) {
numbers[startIndex + i] = temp[i];
}
temp = nil;
}
- (void)mergeGroupWithNumbers:(NSMutableArray *)numbers length:(int)length gap:(int)gap {
int i;
int doubleLen = gap * 2;
for (i=0; i + doubleLen - 1 < length; i += (doubleLen)) {
[self mergeWithNumbers:numbers startIndex:i midIndex:i + gap - 1 endIndex:i + doubleLen - 1];
}
if (i + gap - 1 < length - 1) {
[self mergeWithNumbers:numbers startIndex:i midIndex:i + gap - 1 endIndex:length - 1];
}
}
- (void)mergeSortDownToUpWithNumbers:(NSMutableArray *)numbers length:(int)length {
if (numbers.count == 0 || length <= 0) {
return;
}
for (int n=1; n < length; n *= 2) {
[self mergeGroupWithNumbers:numbers length:length gap:n];
}
}
//调用
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(10),@(4),@(8),@(99),@(16),@(19),@(3), nil];
[self mergeSortDownToUpWithNumbers:array length:7];
NSLog(@"%@",array);
- 时间复杂度
最好、最坏和平均时间复杂度均为O(nlogn)。
- 算法稳定性
是一种稳定算法