算法设计与分析-分治法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
f (n) O(nlogb a ) f (n) (nlogb a lgk n) f (n) (nlogb a )
ε>0,则
T (n) (nlogb a ) T (n) (nlogb a lgk 1 n)
T (n) ( f (n))
k≥0,则 ε>0,则
Ex:
2 M=3时,类似的可以推出
n
M=4时,A(n,4)的增长速度非常快,以至于没有适当的数学式子来 表示这一函数。
2
2 2
优点:结构清晰,可读性强,而且容易用数学归纳 法来证明算法的正确性,因此它为设计算法、调试 程序带来很大方便。 缺点:递归算法的运行效率较低,无论是耗费的计 算时间还是占用的存储空间都比非递归算法要多。
,可以用递归的方法求解各个子问题,有时递归处理也可以用循 环来实现。 三、合并:把各个子问题的解合并起来,合并的代价因情况不同 有很大差异,分治算法的有效性很大程度上依赖于合并的实现。
DivideConquer(P) { if (P的规模足够小) 直接求解P; else 分解为k个子问题P1, P2, …, Pk; for (i=1; i<=k; i++) yi=DivideConquer(Pi); return Merge(y1, …, yk); }
该算法的递推式为: 当n=1时,M(1)=1; 当n>1时,M(n)=M(n-1)+1+M(n-1) 渐近时间复杂度为Θ (2n)
问题描述:设计一个递归算法生成n个元素{r1,r2,…,rn}的全排列。
设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}。
集合X中元素的全排列记为perm(X)。 (ri)perm(X)表示在全排列perm(X)的每一个排列前加上前缀得到的排 列。R的全排列可归纳定义如下:
1 n0 F ( n) 1 n 1 F (n 1) F (n 2) n 1
//输入:非负整数n //输出:第n个Fibonacci的值 public static int fibonacci(int n){ if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2); }
例如:n!的递归定义
1 n! n (n 1)! n0 n0
递归边界
递归函数
public void myPrint(int w) { int i; if ( w!=0) { myPrint(w-1); for(i=1;i<=w;++i) System.out.print(w+”,”); System.out.print(“\n”); } }
为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍 按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则1:每次只能移动1个圆盘;
规则2:任何时刻都不允
许将较大的圆盘压在较 小的圆盘之上;
规则3:在满足移动规则
1和2的前提下,可将圆盘 移至a,b,c中任一塔座上。
}
//输入:非负整数n //输出:移动圆盘的步骤 public static void hanoi(int n, int a, int b, int c) { if (n > 0){ hanoi(n-1, a, c, b); move(a,b); hanoi(n-1, c, b, a); }
1
5 low 1 5 low 1 5
2
13
3
19
4
21
5
37
6
56 mid
7
64
8
75
9
80
10
88
11
92 high
2 13
3 19 mid
4 21
5 37 high
6 56
7 64
8 75
9 80
10 88
11 92
2 13
3 19
4 21
5 37
6 56
7 64
8 75
9 80
10 88
11 92
• 递归树方法
该算法的基本操作是加法,可表示为S(n). 当n=0,1时,S(0)=0,S(1)=0; 当n≥2时,S(n)=S(n-1)+S(n-2)+1 对应递归树为:
渐近时间复杂度为Θ (2n)
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这
些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号
结束
将返回地址 和调用参数 入栈
top top
top
top top
top
(1 ) w=3 3 (1 )
((2) 2)w=2 2 ((1) 1)w=3 3
(3) 1 ( 3)w=1 (2) 2 ( 2)w=2 3 ((1) 1)w=3
(4 )w=0 (4) 0 (3 )w=1 (3) 1 (2 )w=2 (2) 2 (1 )w=3 (1) 3
low mid high
找70 例 1 5 low 1 2 13 3 19 4 21 5 37 6 56 7 64 8 75 9 80 10 88 11 92 high 11
mid
2 3 4 5 6 7 8 9 10
5
13
19
21
37
56
64
low
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以 便各个击破,分而治之。
一、划分:既然是分治,当然需要把规模为n的原问题划分为k个
规模较小的子问题,并尽量使这k个子问题的规模大致相同,且 相互独立,即子问题之间不包含公共的子问题。
二、求解子问题:各子问题的解法与原问题的解法通常是相同的
当n=1时,无需排序操作,运行时间为常数可记为Θ(1)。 当n>1时,我们假设n是2的k次幂,将原问题分为规模为n/2的2个 子问题,消耗时间为2T(n/2);分割问题和合并子问题所消耗的 时间是n的线性函数,可记为Θ(n)。 得如下运行时间的递归函数:
(1) n 1 T (n) 2T (n / 2) O(n) n 1
如何解这样的递归函数?
Master-method用于求解如下递归式:
T (n) aT (n / b) f (n)
其中
a 1 和b 1
是常数,f (n) 是一个渐近正的函数。
log a
我们将比较 f (n) 和 n b 的数量级,直观地,递归解是由两个函 数中数量级较大的一个决定。 case 1: case 2: case 3:
• 反向替换法
M(n) = M(n-1)+1 //替换M(n-1)=M(n2)+1 = [M(n-2)+1]+1=M(n-2)+2 //替换M(n-2)=M(n3)+1 = [M(n-3)+1]+2=M(n-3)+2 = …… = [M(n-n)+1]+(n-1)=M(0)+n=n
无穷数列1,1,2,3,5,8,13,21,34,55,…,被称为 Fibonacci数列。它可以递归地定义为:
A(n,m)的自变量m的每一个值都定义了一个单变量函数: m=0时,A(n,0)=n+2 m=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,和A(1,1)=2故 A(n,1)=2*n m=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2),和 A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2n 。
public static void merge(Comparable[] c,Comparable[] d,int l,int m,int r){ //合并c[l~m]和c[m+1~r]到d[l~r] int i=l,j=m+1,k=l; while((i<=m)&&(j<=r)) if(c[i].compareTo(c[j])<=0) d[k++]=c[i++]; else d[k++]=c[j++]; if(i>m) for(int q=j;q<=r;q++) d[k++]=c[q]; 有很大差异,分治算法的有效性很大程度上依赖于合并的实现。 else for(int q=i;q<=m;q++) d[k++]=c[q]; }
当n=1时,perm(R)=(r),其中r是集合R中唯一的元素; 当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),…, (rn)perm(Rn)构成。 其递归函数为: (1) n 1 T (n) n T (n 1) n n 1 渐近时间复杂度为Θ(nn!)
当一个函数及它的一个变量是由函数自身定义时,称这个函数是双递 归函数。 Ackerman函数A(n,m)定义如下:
A(1,0) 2 A(0, m) 1 m0 A(n,0) n 2 n2 A(n, m) A( A(n 1, m), m 1) n, m 1
//输入:非负整数n //输出:n!的值 public static int factorial(int n){ if(n==0) return 1; return n*factorial(n-1); 该算法的基本操作 是乘法! }
我们把基本操作的执行次数记为M(n) 当 n=0 时,M(0)=0; 当 n>0 时,M(n)=M(n-1)+1;
基本思想:将待排序元素分成大小大致相同的2个子集合,分别 对2个子集合进行排序,最终将排好序的子集合合并成为所要求 的排好序的集合。
只含一个记录 的序列显然是 有序的,即为 已求解的子问 题。
//输入:可比较大小的待排序序列 //输出:原数据项构成的有序序列 public static void mergeSort(Comparable a[], int left, int right) { if (left<right) { //至少有2个元素 int i=(left+right)/2; //取中点 mergeSort(a, left, i); mergeSort(a, i+1, right); merge(a, b, left, i, right); //合并到数组b copy(a, b, left, right); //复制回数组a } 有很大差异,分治算法的有效性很大程度上依赖于合并的实现。 }
第二章 递归与分治策略
课时:6学时
本章主要知识点
• 递归的概念 • 递归算法的数学分析 • 分治法 • 分治法的应用
• 递归定义
用自身的更简单情况(所涉及的问题规模更小),来定义自身, 称为递归定义。 最简单的情况称为递归出口,或递归边界,本身不再使用递归定 义。 算法中,直接或间接地调用自身的算法称为递归算法。
T (n) 2T (n / 2) n
nlogb a n
f (n) (nlogb a lgk n)
符合case 2,所以 T (n) (n lg n)
Ex:
T (n) 2T (n / 2) n2
f (n) (nlogb a )
符合case 3,所以 T (n) (n2 )
相邻有序表 C[l] ~ C[m]、C[m+1]~C[r] 结果得到有序表 C[l] ~ C[r] 算法: 1. 从数组C[l]~C[m]和C[m+1]~C[r]中各取一最小数; 2. 比较取出的两个数,将较小数按顺序放入数组D; 3. 从较小数对应的数组中取出下一个最小数; 4. 重复步骤2、3直到两个序列中的数据全部取走; 5. 如果C[l]~C[m]或C[m+1]~C[r]有未取走的数据,则将剩下的 数全部按顺序拷贝到D中已有数据之后; 6. 将D中数据逐一拷贝回C中;
运行结果: 1, 2,2, 3,3,3,
w 2 主程序 w 3 print(2); print(1) ; ( (3)3输出: ) 2, 2
w 1
print(0); ( 4 ) (4) 输出: 1
w 0
返回
w=3; print(w) (1)
ห้องสมุดไป่ตู้
(2输出: ) (2) 3, 3, 3
被调用过程结束后, 根据栈顶的返回地址 ,返回到调用者。
问题描述:应用折半查找方法在一个有序序列中查找值为
k的记录。若查找成功,返回记录k在序列中的位置,若查找失
败,返回失败信息。
k
[ r1 … … … rmid-1 ] rmid [ rmid+1 … … … rn ]
如果k<rmid查找这里 如果k>rmid查找这里
mid=(1+n)/2
找21 例