算法设计与分析-分治法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
r适用条件
1. 问题的规模缩小到一定的程度就可以容易地解决; 2. 问题可以分解为若干个规模较小的相同问题,即该
问题具有最优子结构性质; 3. 问题分解出的子问题的解可以合并为该问题的解; 4. 问题所分解出的各个子问题是相互独立的,即子问
题之间不包含公共的子问题。
r启发式规则
1. 平衡子问题:最好使子问题的规模大致相同。也就是将一 个问题划分成大小相等的k个子问题(通常k=2),这种使子 问题规模大致相等的做法是出自一种平衡(Balancing)子问 题的思想,它几乎总是比子问题规模不等的做法要好。
if (first<end) { pivot=Partition(r, first, end); //问题分解,pivot是轴值在序列中的位置 QuickSort(r, first, pivot-1); //递归地对左侧子序列进行快速排序 QuickSort(r, pivot+1, end); //递归地对右侧子序列进行快速排序
2. 独立子问题:各子问题之间相互独立,这涉及到分治法的 效率,如果各子问题不是独立的,则分治法需要重复地解公 共的子问题。
r分治法典型情况
原问题 的规模是n
子问题1 的规模是n/2
子问题2 的规模是n/2
子问题1的解
子问题2的解
原问题的解
r分治法求解过程
一般来说,由三个阶段组成:
(1)划分:既然是分治,当然需要把规模为n的原问题划分为k 个规模较小的子问题,并尽量使这k个子问题的规模大致相同。
if(P的规模足够小) 直接求解P;
分解为k个子问题P1,P2,…,Pk; for(i=1;i<=k;i++)
//解各子问题,通常采用递归 (Pi);
yi=DivideConquer
return Merge(y1,…,yk); //将各子问题的解合并为原问题的解
}
r时间复杂度
子问题的输入规模大致相等且一分为二,则分 治法的计算时间可表示为:
13 27 38 50 55 49 65 ij j j
以第一个记录作为轴值,对待排序序列进行划分的过程为:
(1)初始化:取第一个记录作为基准,设置两个参数i,j分 别用来指示将要与基准记录进行比较的左侧记录位置和右侧 记录位置,也就是本次划分的区间;
(2)右侧扫描过程:将基准记录与j指向的记录进行比较, 如果j指向记录的关键码大,则j前移一个记录位置。重复右 侧扫描过程,直到右侧的记录小(即反序),若i<j,则将 基准记录与j指向的记录进行交换;
else r1[k++]=r[j++]; } if (i<=m) while (i<=m) //若第一个子序列没处理完,则进行收尾处理
r1[k++]=r[i++]; else while (j<=t) //若第二个子序列没处理完,则进行收尾处理 注:参r数1[中k+,+]第=r一[j+个+数]; 组存放的是原始数据,第二个数组存放的 } 是已处理完毕的数据。
A、B、C、D 四个区域
Ø想法
Ø 用二维数组data[N][N]表示N×N的方阵,观察方阵中数
字的规律,可以从外层向里层填数。 Ø 设变量size表示方阵的大小,则初始时size = N,填完一
层则size = size - 2;
Ø想法
Ø 设变量begin表示每一层的起始位置,变量i和j分别表示
3.2.1 归并排序
思考:对49,38,65,97,76,13,49 ’进行二路归 并排序,写出排序的过程。
49,38,65,97,76,13,49’
MergeSort(r,r1,0,6)
MergeSort(r,r1,0,3)
MergeSort(r,r1,0,1)
MergeSort(r,r1,0,0) r1[0]=r[0]
MergeSort(r,r1,1,1) r1[1]=r[1]
Merge(r1,r,0,0,1)
MergeSort(r,r1,2,3)
MergeSort(r,r1,2,2) r1[2]=r[2]
MergeSort(r,r1,3,3) r1[3]=r[3]
Merge(r1,r,2,2,3)
Merge(r1,r,0,1,3)
算法3.2——合并有序子序列
void Merge(int r[ ], int r1[ ], int s, int m, int t) {
i=s; j=m+1; k=s; while (i<=m && j<=t) {
if (r[i]<=r[j]) r1[k++]=r[i++]; //取r[i]和r[j]中较小者放入 r1[k]
r[j]←→r[i];
//将较大记录交换到后面
j--;
}
}
retutn i; // i为轴值记录的最终位置
}
以轴值为基准将待排序序列划分为两个子序 列后,对每一个子序列分别递归进行排序。
13
27
38 50
55
65 49
i
j
i
j
13 27 38 49 50 55 65
算法3.4——快速排序
void QuickSort(int r[ ], int first, int end) {
• 分治思想 • 归并排序 • 快速排序 • 折半查找 • 选择问题 • 最大子段和问题 • 棋盘覆盖问题 • 循环赛日程安排问题
3.1 基本思想 3.2 排序问题中的分治算法 3.3 查找问题中的分治算法 3.4 组合问题中的分治算法 3.5 典型问题的C++程序(略)
• 实验项目——选择问题
3.2.2 快速排序
[ r1 … … ri-1 ] ri [ ri+1 … … rn ]
均≤ri 轴值 均≥ri 位于最终位置
v归并排序按照记录在序列中的位置对序列进行划分 v快速排序按照记录的值对序列进行划分
一次划分示例
38
55 27
i
50 13 49 65
jj
j
13 27 55
i
ii
50 38 49 65 j
Mergesort(r, r1, s, m);
MergeSort(r,r1,4,6)
Mergesort(r, r1, m+1, t);
…
Merge(r1, r, s, m, t);
Merge(r1,r,0,3,6)
注意每次都要划分到仅剩下一个元素
3.2.1 归并排序
二路归并排序的合并步的时间复杂性为O(n), 所以,二路归并排序算法存在如下递推式:
{
i=first; j=end; //初始化
while (i<j)
{
while (i<j && r[i]<= r[j]) j--; //右侧扫描
if (i<j) {
r[i]←→r[j];
//将较小记录交换到前面
i++;
}
while (i<j && r[i]<= r[j]) i++; //左侧扫描
if (i<j) {
} }
3.2.2 快速排序
思考:对49,38,65,97,76,13,27,49’进行快 速排序,写出排序的过程。
第4章 分治法
一 趟 划 分
Page 33
3.2.2 快速排序
思考:对49,38,65,97,76,13,27,49’进行快速 排序,写出排序的过程。 (1)27 38 13 49 76 97 65 49’ (2)分别考察27 38 13和76 97 65 49’
g(n) n足够小 T(n)=
2T(n/2)+f(n)
说明:T(n)是输入规模为 n 的分治法的计算时间; g(n)是对足够小的 n 直接求解的时间; f(n)是Merge的计算时间。
例:计算an,应用分治技术得到如下计算方法:
an
=
ì
í îa
ën
a 2û ×
a én
2ù
如果 n = 1 如果 n > 1
3.2.1 归并排序
算法3.1——归并排序
void MergeSort(int r[ ], int r1[ ], int s, int t) {
if (s= =t) r1[s]=r[s]; //只有一个元素,直接赋值 else {
m=(s+t)/2; Mergesort(r, r1, s, m); //归并排序前半个子序列 Mergesort(r, r1, m+1, t); //归并排序后半个子序列 Merge(r1, r, s, m, t); //合并两个已排序的子序列 } }
(3)合并:将这两个有序子序列合并成一个有序序列。
3.2.1 归并排序
r0 … … r(n-1) /2 r(n-1)/2+1 … … rn-1
划分
r'0<… …<r' (n-1)/2 r' (n-1)/2+1<… …<r'n-1 递归处理
r''0<……<r'' (n-1)/2<r'' (n-1)/2+1 <……<r ''n-1 合并解
(2)求解子问题:各子问题的解法与原问题的解法通常是相同 的,可以用递归的方法求解各个子问题,有时递归处理也可以 用循环来实现。
(3)合并:把各个子问题的解合并起来,合并的代价因情况不 同有很大差异,分治算法的有效性很大程度上依赖于合并的实 现。
r分治法程序框架
DivideConquer(P){ //求解规模为n的问题P
3.1 基本思想
凡治众如治寡,分数是也; 斗众如斗寡,形名是也。
r基本思想
将一个难以直接解决的大问题,划分成一些规模较小的 子问题 ,以便各个击破,分而治之。
更一般地说,将要求解的原问题划分成k个较小规模的子 问题,对这k个子问题分别求解。如果子问题的规模仍然不够 小,则再将每个子问题划分为k个规模更小的子问题,如此分 解下去,直到问题规模足够小,很容易求出其解为止,再将 子问题的解合并为一个更大规模的问题的解,自底向上逐步 求出原问题的解。
分析时 间性能
34
32
×
32
分Leabharlann Baidu问题
31 × 31 3 ×3
31 × 31 3 ×3
求解每个子问题
9
×
9
合并子问题的解
81
v其时间复杂度为O(n)
一个简单的例子—数字选择方阵
输出下图所示N×N(1≤N≤10)的数字旋转方阵。
1 20 19 18 17 16 2 21 32 31 30 15 3 22 33 36 29 14 4 23 34 35 28 13 5 24 25 26 27 12 6 7 8 9 10 11
T (n)
=
ì1 í î2T (n 2)
+n
n =1 n >1
根据主定理,二路归并排序的时间代价是O(nlog2n)。
二路归并排序在合并过程中需要与原始记录序列同样 数量的存储空间,因此其空间复杂性为O(n)。
3.2.2 快速排序
快速排序的分治策略是: (1)划分:选定一个记录作为轴值,以轴值为基准 将整个序列划分为两个子序列r1 … ri-1和ri+1 … rn,前 一个子序列中记录的值均小于或等于轴值,后一个 子序列中记录的值均大于或等于轴值; (2)求解子问题:分别对划分后的每一个子序列递 归处理; (3)合并:由于对子序列r1 … ri-1和ri+1 … rn的排序 是就地进行的,所以合并不需要执行任何操作。
注:参数中,第一个数组存放的是原始数据,第二个数组 存放的是已处理完毕的数据。
3.2.1 归并排序
关键问题:如何将两个有序序列合成一个有序序列?
60 20 31 5 44 55 65 60 20 31 5 44 55 65
20 60
ii
5 31
44 55 65
j jj
5 20 31 60
k kkk
(3)左侧扫描过程:将基准记录与i指向的记录进行比较, 如果i指向记录的关键码小,则i后移一个记录位置。重复左 侧扫描过程,直到左侧的记录大(即反序),若i<j,则将 基准记录与i指向的记录交换;
(4)重复(2)(3)步,直到i与j指向同一位置,即基准记 录最终的位置。
算法3.3——一次划分
int Partition(int r[ ], int first, int end)
行号和列号,则每一层初始时i = begin,j = begin。 Ø 将每一层的填数过程分为A、B、C、D四个区域,则每
个区域需要填写size – 1个数字,填写区域A时列号不变
行号加1,填写区域B时行号不变列号加1,填写区域C时
列号不变行号减1,填写区域D时行号不变列号减1。 Ø 显然,递归的结束条件是size等于0或size等于1。
3.2 排序问题中的分治法
3.2.1 归并排序 3.2.2 快速排序
3.2.1 归并排序
将两个有序序列合并为一个有序序列的过程 称为二路归并。
二路归并排序的分治策略是:
(1)划分:将待排序序列r0, r1, …, rn-1划分为两个长度 相等的子序列r0, …, r(n-1)/2和r(n-1)/2+1, …, rn-1; (2)求解子问题:分别对这两个子序列进行排序,得 到两个有序子序列;