算法笔记练习 4.6 two pointers 问题 B: 基础排序III:归并排序

算法笔记练习 题解合集

题目链接

题目

题目描述
归并排序是一个时间复杂度为O(nlogn)的算法,对于大量数据远远优于冒泡排序与插入排序。
这是一道排序练习题,数据量较大,请使用归并排序完成。

输入
第一行一个数字n,代表输入的组数
其后每组第一行输入一个数字m,代表待排序数字的个数
其后m行每行一个数据,大小在1~100000之间,互不相等,最多有10万个数据。

输出
升序输出排好序的数据,每行一个数字

样例输入

1
10
10
9
8
7
6
5
4
3
2
1

样例输出

1
2
3
4
5
6
7
8
9
10

思路

思路参考算法笔记P141。


注意:mergeSort的写法和书上有微小的差异,改成了数组下标从 0 开始的写法,有三处改动:

  1. for (i = 0; i < n; i += step),原本是<=
  2. if (middle + 1 < n),原本是<=,这里一开始没注意到,debug 了很久
  3. merge(a, i, middle, middle + 1, min(i + step - 1, n - 1)),原本是min(i + step - 1, n)(感谢评论区指正)

思考:

  • 相比较归并排序的递归写法,从写代码的角度来看,递归写法是自顶向下的,而迭代写法更符合程序实际运行时的“归并”情况;
  • 迭代写法在每次合并前要判断右子区间是否存在元素,这个判断实际上就是递归写法里的递归边界。

代码

#include 

int min(int a, int b){
	return a < b ? a : b;
}

// 把数组 a 中分别递增的两半合并 
void merge(int *a, int L1, int R1, int L2, int R2) {
	int temp[R2 - L1 + 1];
	int *ptemp = temp;
	int *pa = a + L1;
	int *pb = a + L2;
	while(pa <= a + R1 && pb <= a + R2) {
		if (*pa <= *pb)
			*ptemp++ = *pa++;
		else
			*ptemp++ = *pb++;
	}
	while (pa <= a + R1)
		*ptemp++ = *pa++;
	while (pb <= a + R2)
		*ptemp++ = *pb++;
	int i;
	for (i = 0; i < R2 - L1 + 1; ++i)
		a[L1 + i] = temp[i];
} 

// 二路归并排序,迭代写法
void mergeSort(int n, int *a) {
	if (n < 2)
		return; 
	int step;
	for (step = 2; step / 2 <= n; step *= 2) {
		int i;
		for (i = 0; i < n; i += step) {
			int middle = i + step / 2 - 1;
			if (middle + 1 < n)
				merge(a, i, middle, middle + 1, min(i + step - 1, n - 1));
		} 
	} 
} 

int main(){
	int n, m;
	while (scanf("%d", &n) != EOF) {
		while (n--) {
			scanf("%d", &m);
			int nums[m], i;
			for (i = 0; i < m; ++i)
				scanf("%d", &nums[i]);
			mergeSort(m, nums);
			for (i = 0; i < m; ++i)
				printf("%d\n", nums[i]);
		} 
	} 
	return 0;
} 

你可能感兴趣的:(算法笔记)