双路归并排序法(自上向下)

83 篇文章 1 订阅
10 篇文章 0 订阅

算法原理

    归并排序(Merge Sort)是利用“归并” 技术来进行排序。归并是指将若干个已排序的 子 文件 合并 成 一个 有序 的 文件。

    两路 归并 算法 的 基本 思路: 设 两个 有序 的 子 文件( 相当于 输入 堆) 放在 同一 向量 中 相邻 的 位置 上: A[ low… m], a[ m+ 1… high], 先 将它 们 合并 到 一个 局部 的 暂存 向量 Temp( 相当于 输出 堆) 中, 待 合并 完成 后 将 Temp 复制 回 A[ low... high] 中。

    归并 排序 有两 种 实现 方法: 自 底 向上 和 自 顶 向下。

    自 底 向上 方法 的 基本 思想:

    (1) 第 1 趟 归并 排序 时, 将 待 排序 的 文件 A[ 1... n] 看作 n 个 长度 为 1 的 有序 子 文件, 将 这些 子 文件 两两 归并。 若 n 为 偶数, 则 得到 n/ 2 个 长度 为 2 的 有序 子 文件; 若 n 为 奇数, 则 最后 一个 子 文件 不 参与 归并。 故 本 趟 归并 完成 后, 前 n/ 2 个 有序 子 文件 长度 为 2, 但 最后 一个 子 文件 长度 仍 为 1。

    (2) 第 2 趟 归并 则 是将 第 1 趟 归并 所 得到 的 n/ 2 个 有序 的 子 文件 两两 归并, 如此 反复, 直到 最后 得到 一个 长度 为 n 的 有序 文件 为止。

    (3) 上述 每次 归并 操作, 均 是将 两个 有序 的 子 文件 合并 成 一个 有序 的 子 文件, 故称 其为“ 二路 归并 排序”。 类似 地, 有 k( k> 2) 路 归并 排序。

    自 顶 向下 算法 的 设计, 形式 更为 简洁。 设 归并 排序 的 当前 区间 是 A[ low... high], 步骤 如下。

    (1) 分解: 将 当前 区间 一分为二, 即 求 分裂 点。

    (2) 求解: 递归 地 对 两个 子 区间 A[ low... mid] 和 A[ mid+ 1... high] 进行 归并 排序。

    (3) 组合: 将 已 排序 的 两个 子 区间 A[ low... mid] 和 A[ mid+ 1... high] 归并 为 一个 有序 的 区间 R[ low… high]。

    (4) 递归 的 终结 条件: 子 区间 长度 为 1( 一个 记录 自然 有序)。

算法草稿

 

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define SUCCESS		0
#define PARAM_ERR	-1
#define ALLOC_ERR	-2

/*
 * 合并两个子数组 [lstart, lend] [rstart, rend]
 */
int MergeImpl(int * array, int * temp,  int lstart, int lend, int rstart, int rend){
	if(NULL == array || NULL == temp){
		printf("%s para error\n", __func__);
		return PARAM_ERR;
	}

#if 1
	int size = 0; /* 两个待合并的素组总长度 */
	int i = 0, j = 0, k = 0;

	size = (lend - lstart +  1) + (rend - rstart + 1);
	i = lstart;
	j = rstart;
	k = 0;
	
#ifdef DEBUG
	int m = 0;
	printf("Befor %s: size = %d \n", __func__, size);	
	for(m = lstart; m <= rend ; m++){
		printf("array[%d] = %d ", m, array[m]); 	
	}
	printf("\n");
#endif

	/*清空待合并数组*/
	memset(temp, 0x0, size * sizeof(int));

	/* 升序 */
	while ( (i <= lend) && (j <= rend)){
		if(array[i] <= array[j]){
			temp[k++] = array[i++];
		} else {
			temp[k++] = array[j++];
		}
	}

	/* 左子组还有 */
	while (i <= lend){
		temp[k++] = array[i++];
	}
	
	/* 右子组还有 */
	while (j <= rend){
		temp[k++] = array[j++];
	}	

	/*
	 * 合并后的数组,复制回array 
	 * 因为左右两个子组是相邻的,所以从lstart顺序复制如array
	 */
	i = lstart;
	for(k = 0; k < size; k++){
		array[i++] = temp[k];		
	}

#ifdef DEBUG
	printf("After: %s \n", __func__);
	for(m = 0; m < size ; m++){
		printf("temp[%d] = %d ", m, temp[m]);		
	}
	printf("\n");

	for(m = lstart; m <= rend ; m++){
		printf("array[%d] = %d ", m, array[m]);		
	}

	printf("\n\n");
#endif


#else
	int size = 0; /* 两个待合并的素组总长度 */
	int lpos = 0, rpos = 0, tpos = 0, k = 0;

	size = rend - lstart + 1;
	lpos = lstart;
	rpos = rstart;
	tpos = lstart;

	/* 升序 */
	while ( (lpos <= lend) && (rpos <= rend)){
		if(array[lpos] <= array[rpos]){
			temp[tpos++] = array[lpos++];
		} else {
			temp[tpos++] = array[rpos++];
		}
	}


	/* 左子组还有 */
	while (lpos <= lend){
		temp[tpos++] = array[lpos++];
	}
	
	/* 右子组还有 */
	while (rpos <= rend){
		temp[tpos++] = array[rpos++];
	}	

	/*
	 * 合并后的数组,复制回array 
	 * 因为左右两个子组是相邻的,所以从lstart顺序复制如array
	 */
	for(k = 0; k < size; k++, rend--){
		array[rend] = temp[rend];	
	}

#endif
	return SUCCESS;
	
}

/*
 * 本函数真正的合并排序实现
 */
int MergeSortImpl(int * array, int * temp,  int low, int high){
	if(NULL == array || NULL == temp){
		printf("%s para error\n", __func__);
		return PARAM_ERR;
	}

	int i = 0, j = 0;
	int size = 0; /* 排序分组大小 */
	int mid = 0;
	
	size =  high - low + 1;
	/* 递归终止条件,数组中只有一个元素*/
	if(size <= 1){
		goto out;		
	}

	/*
	 * 两路合并排序,自定向下,取得中间点 
	 * 注意 low 和 high 的中间的不是 size /2 而是 (low + high) / 2 ! 千万注意
	 */
	mid = (low + high) / 2; /*左子组:[low, mid], 右子组:[mid+1, high]*/
	
	/* 对左子组两路合并排序*/
	MergeSortImpl(array, temp, low, mid);

	/* 对右子组两路合并排序*/
	MergeSortImpl(array, temp, mid + 1 , high);

	/* 对已排序的两个子组进行合并 */
	MergeImpl(array, temp, low, mid, mid + 1, high);

out:
	return SUCCESS;
}


int MergeSort(int * array, int size){
	if(NULL == array){
		printf("%s para error\n", __func__);
		return PARAM_ERR;
	}

	int * temp = NULL;

	/*这里是合并排序的接口,完成准备工作*/
	temp = (int *) malloc (size * sizeof(int));
	if(NULL == temp){
		printf("%s alloc memory error\n",__func__);
		return ALLOC_ERR;
	}

	MergeSortImpl(array, temp, 0, size - 1);

	/* 删除已分配空间 */
	free(temp);
	temp = NULL;

	return SUCCESS;	
}


int main(int argc, char ** argv){
	int array[10] = {7,3,5,8,0,9,1,2,4,6};
	int i = 0;

	printf("Before sort: \n");
	for(i = 0; i < 10; i++){
		printf("  %d  ", array[i]);
	}
	printf("\n");

	MergeSort(array, 10);

	printf("after sort: \n");
	for(i = 0; i < 10; i++){
		printf("  %d  ", array[i]);
	}
	printf("\n");
	
	return 0;
}

调试编译

gcc MergeSort.c -DDEBUG

调试输出

Before sort:
  7    3    5    8    0    9    1    2    4    6
Befor MergeImpl: size = 2
array[0] = 7 array[1] = 3
After: MergeImpl
temp[0] = 3 temp[1] = 7
array[0] = 3 array[1] = 7

Befor MergeImpl: size = 3
array[0] = 3 array[1] = 7 array[2] = 5
After: MergeImpl
temp[0] = 3 temp[1] = 5 temp[2] = 7
array[0] = 3 array[1] = 5 array[2] = 7

Befor MergeImpl: size = 2
array[3] = 8 array[4] = 0
After: MergeImpl
temp[0] = 0 temp[1] = 8
array[3] = 0 array[4] = 8

Befor MergeImpl: size = 5
array[0] = 3 array[1] = 5 array[2] = 7 array[3] = 0 array[4] = 8
After: MergeImpl
temp[0] = 0 temp[1] = 3 temp[2] = 5 temp[3] = 7 temp[4] = 8
array[0] = 0 array[1] = 3 array[2] = 5 array[3] = 7 array[4] = 8

Befor MergeImpl: size = 2
array[5] = 9 array[6] = 1
After: MergeImpl
temp[0] = 1 temp[1] = 9
array[5] = 1 array[6] = 9

Befor MergeImpl: size = 3
array[5] = 1 array[6] = 9 array[7] = 2
After: MergeImpl
temp[0] = 1 temp[1] = 2 temp[2] = 9
array[5] = 1 array[6] = 2 array[7] = 9

Befor MergeImpl: size = 2
array[8] = 4 array[9] = 6
After: MergeImpl
temp[0] = 4 temp[1] = 6
array[8] = 4 array[9] = 6

Befor MergeImpl: size = 5
array[5] = 1 array[6] = 2 array[7] = 9 array[8] = 4 array[9] = 6
After: MergeImpl
temp[0] = 1 temp[1] = 2 temp[2] = 4 temp[3] = 6 temp[4] = 9
array[5] = 1 array[6] = 2 array[7] = 4 array[8] = 6 array[9] = 9

Befor MergeImpl: size = 10
array[0] = 0 array[1] = 3 array[2] = 5 array[3] = 7 array[4] = 8 array[5] = 1 array[6] = 2 array[7] = 4 array[8] = 6 array[9] = 9
After: MergeImpl
temp[0] = 0 temp[1] = 1 temp[2] = 2 temp[3] = 3 temp[4] = 4 temp[5] = 5 temp[6] = 6 temp[7] = 7 temp[8] = 8 temp[9] = 9
array[0] = 0 array[1] = 1 array[2] = 2 array[3] = 3 array[4] = 4 array[5] = 5 array[6] = 6 array[7] = 7 array[8] = 8 array[9] = 9

after sort:
  0    1    2    3    4    5    6    7    8    9

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
几种常见排序 基于比较的排序算法: 下界是 nlgn 1.1 SelectionSort:每次选出最下的元素,放在当前循环最左边的位置。 1.2 BubbleSort:每次比较相邻的两个数,使得最大的数像气泡一样冒到最右边。 1. 3 InsertionSort:每次拿起一个数,插入到它左边数组的正确位置。 1.4 QuickSort:选择一个数,作为标准,小于它的放在左边,大于它的放在右边。并把它放在中间;递归地对左右子数组进行排序。 实现时:1. 确定递归结束条件,初始化左右游标, 选择标准数; 2. while循环,do while实现两个游标同时向中间移动,置换; 3. 置换标准数和右边游标所指的数; 4. 递归调用,对左右子数组进行排序。 1. 5 HeapSort:用最大堆实现。 实现时:建堆:置换堆顶元素和最后一个元素,堆大小减少,保持新的堆为最大堆; 保持最大堆: 从底向上依次保持最大堆,从第一个父节点到根部。 1.6 MergeSort:拆分数组,递归实现排序,二路归并。用哨兵来阻止游标的越界。 线性时间运行的算法: 1.7 CountingSort: 假设数据分布在0到k之间的。对于每个输入x,确定出小于x的数的个数。假设小于x的数有17个,那么x就应该在第18个输出位置。 1. 8 Radix sort(基数排序):从最低位开始,每位采用稳定的排序算法(如计数排序)。 1.9 Bucket sort:当输入数据比较均匀时采用。 先将数据区间分为n个桶,把数据分放到对应的桶中;对桶内的数据采用插入排序;再把各个桶的排序结果串起来。
本书带完整书签 第1章 基础 1 1.1 基础编程模型 4 1.1.1 Java程序的基本结构 4 1.1.2 原始数据类塑与表达式 6 1.1.3 语句 8 1.1.4 简便记法 9 1.1.5 数组 10 1.1.6 静态方法 12 1.1.7 API 16 1.1.8 字符串 20 1.1.9 输入输出 21 1.1.10 二分査找 28 1.1.11 展望 30 1.2 数据抽象 38 1.2.1 使用抽象数据类型 38 1.2.2 抽象数据类型举例 45 1.2.3 抽象教据类型的实现 52 1.2.4 更多抽象数据类型的实现 55 1.2.5 数据类型的设计 60 1.3 背包、队列和栈 74 1.3.1 API 74 1.3.2 集合类數据类型的实现 81 1.3.3 链表 89 1.3.4 综述 98 1.4 算法分析 108 1.4.1 科学方法 108 1.4.2 观察 108 1.4.3 数学模型 112 1.4.4 增长数量级的分类 117 1.4.5 设计更快的算法 118 1.4.6 倍率实验 121 1.4.7 注意事项 123 1.4.8 处理对于输入的依赖 124 1.4.9 内存 126 1.4.10 展望 129 1.5 案例研究:union-find算法 136 1.5.1 动态连通性 136 1.5.2 实现 140 1.5.3 展望 148 第2章 排序 152 2.1 初级排序算法 153 2.1.1 游戏规则 153 2.1.2 选择排序 155 2.1.3 插入排序 157 2.1.4 排序算法的可视化 159 2.1.5 比较两种排序算法 159 2.1.6 希尔排序 162 2.2 归并排序 170 2.2.1 原地归并的抽象方法 170 2.2.2 自顶向下的归并排序 171 2.2.3 自底向上的归并排序 175 2.2.4 排序算法的复杂度 177 2.3 快速排序 182 2.3.1 基本算法 182 2.3.2 性能特点 185 2.3.3 算法改进 187 2.4 优先队列 195 2.4.1 API 195 2.4.2 初级实现 197 2.4.3 堆的定义 198 2.4.4 堆的算法 199 2.4.5 堆排序 205 2.5 应用 214 2.5.1 将各种數据排序 214 2.5.2 我应该使用啷种排序算法 218 2.5.3 问题的归约 219 2.5.4 排序应用一览 221 第3章查找 227 3.1 符号表 228 3.1.1 API 228 3.1.2 有序符号表 230 3.1.3 用例举例 233 3.1.4 无序链表中的顺序查找 235 3.1.5 有序數组中的二分查找 238 3.1.6 对二分査找的分析 242 3.1.7 预览 244 3.2 二叉查找树 250 3.2.1 基本实现 250 3.2.2 分析 255 3.2.3 有序性相关的方法与删除操作 257 3.3 平衡査找树 269 3.3.1 2-3査找树 269 3.3.2 红黑二叉查找树 275 3.3.3 实现 280 3.3.4 删除操作 282 3.3.5 红黑树的性质 284 3.4 散列表 293 3.4.1 散列函数 293 3.4.2 基于拉链法的散列表 297 3.4.3 基于线性探测法的散列表 300 3.4.4 调整教组大小 304 3.4.5 内存使用 306 3.5 应用 312 3.5.1 我应该使用符号表的哪种实现 312 3.5.2 集合的API 313 3.5.3 字典类用例 315 3.5.4 索引类用例 318 3.5.5 稀疏向量 322 第4章 图 329 4.1 无向图 331 4.1.1 术语表 331 4.1.2 表示无向图的数据类型 333 4.1.3 深度优先搜索 338 4.1.4 寻找路径 342 4.1.5 广度优先搜索 344 4.1.6 连通分量 349 4.1.7 符号图 352 4.1.8 总结 358 4.2 有向图 364 4.2.1 术语 364 4.2.2 有向图的数据类型 365 4.2.3 有向图中的可达性 367 4.2.4 环和有向无环图 369 4.2.5 有向图中的强连通性 378 4.2.6 总结 385 4.3 最小生成树 390 4.3.1 原理- 391 4.3.2 加权无向图的数据类型 393 4.3.3 最小生成树的API和测试用例 396 4.3.4 Prim算法 398 4.3.5 Prim算法的即时实现 401 4.3.6 Kruskal算法 404 4.3.7 展望 407 4.4 最短路径 412 4.4.1 最短路径的性质 413 4.4.2 加权有向图的数据结构 414 4.4.3 最短路径算法的理论基础 420 4.4.4 Dijkstra算法 421 4.4.5 无环加权有向图中的最短路径算法 425 4.4.6 一般加权有向图中的最短路径问题 433 4.4.7 展望 445 第5章 字符串 451 5.1 字符串排序 455 5.1.1 键索引计数法 455 5.1.2 低位优先的字符串排序 458 5.1.3 高位优先的字符串排序 461 5.1.4 三向字符串快速排序 467 5.1.5 字符串排序算法的选择 470 5.2 单词查找树 474 5.2.1 单词查找树 475 5.2.2 单词查找树的性质 483 5.2.3 三向单词查找树 485 5.2.4 三向单词查找树的性质 487 5.2.5 应该使用字符串符号表的哪种实现 489 5.3 子字符串查找 493 5.3.1 历史简介 493 5.3.2 暴力子字符串査找算法 494 5.3.3 Knuth-Morris-Pratt子字符串查找算法 496 5.3.4 Boyer-Moore字符串查找算法 502 5.3.5 Rabin-Karp指纹字符串查找算法 505 5.3.6 总结 509 5.4 正则表达式 514 5.4.1 使用正则表达式描述模式 514 5.4.2 缩略写法 516 5.4.3 正则表达式的实际应用 517 5.4.4 非确定有限状态自动机 518 5.4.5 模拟NFA的运行 520 5.4.6 构造与正则表达式对应的NFA 522 5.5 数据压缩 529 5.5.1 游戏规则 529 5.5.2 读写二进制数据 530 5.5.3 局限 533 5.5.4 热身运动:基因组 534 5.5.5 游程编码 537 5.5.6 霍夫曼压缩 540 第6章背景 558 索引 611

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值