学习《算法导论》第二章 合并排序 总结
2015-08-29 21:59
302 查看
学习《算法导论》第二章 合并排序 总结
在上一节,学习了插入排序,插入排序使用的算法设计是增量方法. 而这一节我们学习另一种算法设计方法:分治法. 分治算法有一个优点:那就是很容易确定其运行时间.分治法
分治策略:将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解.分治模式一般有三个步骤:
分解:将原问题分解成一系列子问题
解决:递归地解决各子问题
合并:将子问题的结果合并成原问题的解
合并排序
合并排序直观地操作如下:分解:将n个元素分成n/2个元素的子序列
解决:用合并排序法对两个子序列递归地排序
合并:合并两个已排序的子序列以得到排序结果
单个元素被视为是已排好序的,这样就能退出递归. 合并排序的关键是合并步骤、合并两个已排好序的子序列. 为此,我们引入一个辅助过程MERGE(A, p, q, r),其中A是一个数组,p、q和r是下标,假设子数组A[p..q]和A[q+1..r]都已排好序,并将它们合并成一个已排好序的数组A[p..r].下面先给出合并过程的伪代码:
MERGE(A,p, q, r) 1 n1 <--- q-p+1 2 n2 <--- r-p 3 create arrays L[1..n1+1] and R[1..n2+1] 4 for i <--- 1 to n1 5 do L[i] <--- A[p+i-1] 6 for j <--- 1 to n2 7 do R[j] <--- A[q+1] 8 L[n1+1] <--- ∞ // 哨兵值 9 R[n2+1] <--- ∞ // 哨兵值 10 i <--- 1 11 j <--- 1 12 for k <--- p to r 13 do if L[i] <= R[j] 14 then A[k] <--- L[i] 15 i <--- i+1 16 else A[k] <--- R[j] 17 j <--- j+1
由以上的伪代码可知:MERGE过程的时间代价为Θ(n). 此处n = r – q+1.
下面就可以讲合并排序了,MERGE过程是合并排序的一个子程序. 下面的MERGE-SORT(A, p,r)对子数组A[p..r]进行排序. 伪代码如下:
MERGE-SORT(A, p, r) 1 if p < r 2 then q <--- (p + r) / 2 3 MERGE-SORT(A, p, q) 4 MERGE-SORT(A, q + 1, r) 5 MERGE(A, p, q, r)
下图自底向上地说明了当n为2的幂时,整个过程的操作:
合并排序算法分析
为问题分析方便,这里假设n为2的幂次方. 当n > 1时,将运行时间作如下的分解:分解:这一步就是计算出子数组的中间位置,常量时间,D(n) = Θ(1).
解决:递归地解两个规模为n/2的子问题,时间为2T(n/2).
合并:MERGE过程上面分析了,运行时间为C(n) = Θ(n).
所以,T(n) = 2T(n/2) +Θ(n). 那这个运行时间是多少呢?这是一个递归式,后面有专门章节介绍这个解法,可以用迭代法也可以用主定理得出:
T(n) = Θ(nlgn).
下面用另一种“递归树”来分析如何解递归式,假设n为2的幂次方.
如上a, b, c, d图,它被逐步地进行了扩展以形成递归树,在最后一图,它完全的扩展了的递归树有lgn+1层,而每一层的总代价为cn,因此总的运行时间代价为cnlgn+cn. 因此运行时间为Θ(nlgn).
合并排序的代码实现
// 合并子问题 void merge (int* num, int begin, int middle, int end) { int n1 = middle - begin + 1; int n2 = end - middle; int* left = (int*)malloc(n1 + 1); int* right = (int*)malloc(n2 + 1); int i = 0; int j = 0; // 将子问题1中的元素存入left数组 for (i = 0; i < n1; i++) { left[i] = num[begin + i]; } // 将子问题2中的元素存入right数组 for (i = 0; i < n2; i++) { right[i] = num[middle + i + 1]; } i = 0; j = 0; // 分别将哨兵存入left和right数组中,作为判断是否到达数组末尾 left[n1] = MAX_VALUE; right[n2] = MAX_VALUE; for (int k = begin; k <= end; k++) { if (left[i] < right[j]) { num[k] = left[i]; i = i + 1; } if (left[i] >= right[j]) { num[k] = right[j]; j = j + 1; } } free(left); free(right); return; } // 合并排序 void mergesort (int* num, int start, int end) { int middle = 0; if (start < end) { middle = (start + end) / 2; mergesort (num, start, middle); // 分解问题成子问题 mergesort (num, middle + 1, end); // 分解问题成子问题 merge (num, start, middle, end); // 合并 } return; }
相关文章推荐
- LeetCode题解---Plus One
- 排序算法 随笔
- 【NROS-00】自己实现一个简单操作系统
- ffmpeg编译
- C语言贪心算法
- Linux学习:Linux基础命令集(2)
- Java集合概述Set、List、Map
- Android异步处理之AsyncTaskLoader简单使用
- Android进阶网站分享(持续更新中……)
- context.Response.End()的用法和本质
- hdu5423
- poj 1821 Fence(单调队列+dp)
- PHP面向对象(oop)的学习笔记
- 链表的最基本实现
- 封装(自定义视图) Encapsulation
- 关于C++中覆盖,重载,隐藏的一点说明
- 中元夜观月
- [HDU 1505 City Game] HDU1506加强版
- WAMP运行原理
- BestCoder Round #53 (div.2)1003 Rikka with Graph II 哈密顿路径 dfs