排序算法之归并排序

一、基本思想

归并排序,和快排一样同样采用了分治的思想,将两个(或以上)有序表合并成一个新的有序表。

归并排序步骤如下:
  1. 把N个记录看成 N个长度为 1 的有序子表;
  2. 进行两两归并使记录关键字有序,得到 N/2 个长度为 2 的有序子表; 
  3. 重复第2步直到所有记录归并成一个长度为N的有序表为止。

二、算法实现

下面是归并排序算法的 递归实现

#include <iostream>
#include <malloc.h>
using namespace std;

// 合并两个有序部分
void mergeArray(int a[], int first, int mid, int last, int temp[]) {
	if (first >= last) {
		return;
	}
	int i = first, j = mid + 1;
	int k = first;
	while (i <= mid && j <= last) {
		if (a[i] > a[j]) {
			temp[k++] = a[j++];
		} else {
			temp[k++] = a[i++];
		}
	}
	while (i <= mid) {
		temp[k++] = a[i++];
	}
	while (j <= last) {
		temp[k++] = a[j++];
	}

	// 回写到原来数组中
	for (k = first; k <= last; k++) {
		a[k] = temp[k];
	}
}

// 递归调用归并
void mSort(int* a, int first, int last, int *temp) {
	if (first < last) {
		int mid = (first + last) >> 1;
		mSort(a, first, mid, temp);
		mSort(a, mid + 1, last, temp);
		mergeArray(a, first, mid, last, temp);
	}
}

// 归并排序算法
void mergeSort(int *a, int len) {
	int* temp = (int*) malloc(len * sizeof(int));
	mSort(a, 0, len - 1, temp);
	free(temp);
}

int main() {
	int arr[] = { 213, 43, 43, 123, 45, 52, 67, 234, 452, 5, 67 };
	int len = 11;

	cout << "Before sorting:" << endl;
	for (int i = 0; i < len; i++) {
		cout << arr[i] << "\t";
	}

	mergeSort(arr, len);

	cout << endl << "After merge sorting:" << endl;
	for (int i = 0; i < len; i++) {
		cout << arr[i] << "\t";
	}
	cout << endl;

	return 0;
}

三、算法分析

归并排序最好、平均、最坏时间复杂度都是:O(n*log2(n))

归并排序需要额外的存储空间,其空间复杂度为:O(n).

归并排序和快排、堆排序一样是一种高效的排序算法,在数据规模较大而且存储空间要求足够的情况下是非常好的选择。

四、算法改进

归并排序改进和优化的方向如下:
  • 当问题分割很小到某个规模的时候停止递归,采用简单插入排序;
  • 消除递归调用
  • 消除反复回写
  • ...

下面是改进的一个消除递归算法:

#include <iostream>
#include <malloc.h>
using namespace std;

// 合并数组中连续的两个有序部分
void mergeArray(int a[], int first, int mid, int last, int temp[]) {
	int i = first, j = mid + 1;
	int k = first;
	while (i <= mid && j <= last) {
		if (a[i] > a[j]) {
			temp[k++] = a[j++];
		} else {
			temp[k++] = a[i++];
		}
	}
	while (i <= mid) {
		temp[k++] = a[i++];
	}
	while (j <= last) {
		temp[k++] = a[j++];
	}
}

// 根据设定的步长来顺序归并
void mergeStep(int a[], int step, int len, int temp[]) {
	int first, mid, last;
	first = 0;
	last = first + step + step - 1;
	mid = first + step - 1;
	while (last < len) {
		mergeArray(a, first, mid, last, temp);
		first = last + 1;
		last = first + step + step - 1;
		mid = first + step - 1;
	}
	// 末端注意数组边界
	if (mid > len) {
		for (int i = first; i < len; i++) {
			temp[i] = a[i];
		}
	} else {
		mergeArray(a, first, mid, len - 1, temp);
	}
}

void mergeSort(int a[], int len) {

	cout << "Before sorting:" << endl;
	for (int i = 0; i < len; i++) {
		cout << a[i] << "\t";
	}
	cout << endl;
	//
	int flag = 0; // 写入方向标识
	int* temp = (int*) malloc(len * sizeof(int));
	// 消除递归
	for (int step = 1; step < len; step = step << 1) {
		// 避免返回回写
		if (flag++ % 2) {
			// flag初始为奇数,向a写入排序结果,执行后flag为偶数
			mergeStep(temp, step, len, a);
		} else {
			// flag初始为偶数,向temp写入即可,执行后flag为奇数
			mergeStep(a, step, len, temp);
		}
	}
	// 若flag为奇数,则表明排序结果存在于temp中,需要回写
	if (flag % 2) {
		for (int i = 0; i < len; i++) {
			a[i] = temp[i];
		}
	}
	//
	free(temp);
	//
	cout << "After merge sorting:" << endl;
	for (int i = 0; i < len; i++) {
		cout << a[i] << "\t";
	}
	cout << endl;

}

int main() {
	int arr[17] = { 213, 67, 89, 10, 23, 9, 23, 45, 12, 456, 234, 67, 12, 0 };
	int len = 17;
	mergeSort(arr, len);
	return 0;
}



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
归并排序(Merge Sort)是一种稳定的、基于比较的排序算法,最坏时间复杂度为 O(nlogn)。其基本思想是将待排序序列分成若干个子序列,每个子序列都是有序的,然后将子序列合并成整体有序的序列。 归并排序实现方法有两种:自顶向下和自底向上。 自顶向下的归并排序算法实现: 1. 将待排序序列分成两个子序列,分别对这两个子序列进行递归排序。 2. 将两个已经排好序的子序列合并为一个有序序列。 自底向上的归并排序算法实现: 1. 将待排序序列每个元素看成一个独立的有序序列,进行两两合并。 2. 得到 n/2 个长度为 2 的有序序列,再两两合并。 3. 重复步骤 2,直到得到一个长度为 n 的有序序列。 下面是自顶向下的归并排序算法的实现代码(使用了递归): ``` void MergeSort(int arr[], int left, int right) { if (left >= right) return; int mid = left + (right - left) / 2; MergeSort(arr, left, mid); MergeSort(arr, mid + 1, right); int* temp = new int[right - left + 1]; int 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 (int p = 0; p < k; p++) arr[left + p] = temp[p]; delete[] temp; } ``` 下面是自底向上的归并排序算法的实现代码(使用了迭代): ``` void MergeSort(int arr[], int n) { int* temp = new int[n]; for (int len = 1; len < n; len *= 2) { for (int left = 0; left < n - len; left += len * 2) { int mid = left + len - 1; int right = min(left + len * 2 - 1, n - 1); int 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 (int p = 0; p < k; p++) arr[left + p] = temp[p]; } } delete[] temp; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值