算法分析基础
一、效率
效率的定义(1):当实现一个算法时,如果它在真实的输入实例上运行的快,那么这个算法是有效的。
研究算法的目的:快速求解真实的问题。
该定义缺少的因素:1.在哪里实现算法,这个算法有多好。2.什么是真实的输入实例。3.算法的好坏没有定义
最坏情况运行时间:给定规模为N的输入上所有可能的最大运行时间的一个界。
效率的定义(2):在分析的层次上,如果一个算法与蛮力搜索比较,最坏情况下达到质量上更好的性能,就说它是有效的。
该定义的不足之处:质量上更好的性能意味着什么。
存在绝对常数c>0和d>0使得对每个规模为N的输入实例,它的运行时间是不超过
c
N
d
\ cN^d
cNd 个基本运算步,即他的运行时间至多与
N
d
\ N^d
Nd 成正比。
在任何情况下,运行时间的界对某个c和d保持不变,那么我们就说这个算法有一个多项式的运行时间,或者它是一个多项式时间的算法。
效率的定义(3):一个算法有多项式运行时间,它就是有效的。
二、增长的渐近阶
渐近的上界:T(n) = O(f(n))
渐近的下界:T(n) = Ω(f(n))
渐近的紧的界:f(n) = θ (g(n))
命题2.1 :设f和g是两个函数,
存在,并且等于某个常数c>0,那么f(n) = θ (g(n)).
渐近增长率的性质:
传递性 :
(a)如果f = O(g)且 g = O(h),那么f = O(h).
(b)如果f = Ω(g)且 g = Ω(h),那么f = Ω(h).
(c)如果f = θ(g)且 g = θ(h),那么f = θ(h).
函数的和:
命题2.4:设f和g是两个函数,若对某个其他的函数h,我们有f = O(h)和g = O(h),那么f+g = O(h).
命题2.5:令k定常数,f1,f2,……,fk 和h是函数,且对所有的i,fi = O(h),那么f1 + f2 +……+fk =
O(h).
命题2.6:假设f和g是两个函数(取非负的值),满足g = O(f),那么f+g = θ(f),换句话说,f是一个关于组合函数f+g的渐近的紧的界。
普通函数的渐近界:
1.多项式:
f = O(n^d)
f = Ω(n^d)
f = θ(n^d)
2.对数:
logbn = O(n^x) (b>1,x>0)
3.指数:
n^d= O(r^n)
三、用表和数组实现稳定匹配算法
目标:得到稳定匹配算法的界
1、数组和表
数组:不利于动态维护元素随时间变化的表
链表:查找第i个元素需要用到O(i)的时间
2、稳定匹配算法的实现
1)定义两个数组
ManPref[m,i]——表示男人m的优先表上的第i个女人
WomenPref[w,i]——表示女人w的优先表上的第i个男人
2)定义链表
存放自由男人的集合——m变为约会状态,删除m,变为自由状态,插入m
3)数组Next
存放男人m下一个要求婚的女人的为止,初始置Next[m]=1.
4)数组Current
Current[w]存放女人w目前的伴侣,初始置Current[w] = null.
5)定义n*n的数组Ranking,存放女人的优先表。
定理2.10 上面描述的数据结构使得我们能用O(n^2)时间实现G-S算法。
四、一般运行时间的概述
1 线性时间
两个简单的线性时间的算法:
1.计算最大数 计算n个数的最大数(在线算法和数据流算法)
max = ai
for i = 2 to n
if ai>max then
置max = ai
endif
endfor
2.归并两个排好序的数表
为归并排好序的表A=a1,a2,……,an和B = b1,b2,……,bn:
在每个表中维护一个Current指针,初始指向首元素
while (A=null && B!=null)
设ai 和 bj时Current指针指向的元素
if(ai<bj)
输出ai
Current = a(i+1)
else(ai>bj)
输出bj
Current = b(j+1)
endif
endwhile
2 O(nlogn)时间
归并排序:将输入分成相同的两块,递归的对每块求解,然后用线性时间将这两个解组合起来
3 平方时间
例子:假设平面上给定n个点,每个点由(x,y)坐标指定,找最接近的点对。
嵌套循环
4 立方时间
例子:给定集合S1,S2,……,Sn,他们都是{1,2,……,n}的自己,求这些子集中是否有某对子集是不相交的
复杂的嵌套循环
5 O(n^k)时间
独立集问题
6 超出多项式时间
找最大规模的独立集
巡回售货员
二分匹配问题
7 亚线性时间
渐近的小于线性时间
二分检索算法
五、更复杂的数据结构:优先队列
优先队列中每个元素v有一个相关的值key(v) 表示元素v的优先权,较小的关键字代表较高的权。
应用:管理实时事件
命题2.11 : O(n)个优先队列操作的序列可以用来对n个数的集合排序
1.堆——实现优先队列
堆:一棵平衡的二叉树
堆的表示方式:用指针来表示它,堆的每个结点可以保持它存储的元素、他的关键字以及指向这个堆结点的两个孩子和父亲的三个指针
实现堆操作:
插入元素:Heapify-up
Heapify-up(H,i):
if i>1 then
令j=parent(i) = [i/2]
if key[H[i]]<key[H[j]] then
交换数组元素H[i]与H[j]
Heapify-up(H,j)
endif
endif
命题2.12 假定数组H差不多是一个堆,知识H【i】的关键字太小,过程Heapify-up(H,i)时间修整堆性质,使用Heapify-up,我们可以在n个元素的堆中用O(logn)时间插入一个新元素。
删除元素:Heapify-down
Heapify-down(H,i):
令n=length(H)
if 2i>n then
程序终止,H不变
else if 2i<n then
令 left = 2i 且right = 2i+1
令j 是使得key[H[left]]与key[H[right]]中较小的序标
else if 2i= n
令 j=2i
endif
if key[H[j]]<key[H[i]] then
交换数组元素H[i]与H[j]
Heapify-down(H,j)
endif
*
命题2.13 假定数组H差不多是一个堆,只是H【i】的关键字的值太大,过程Heapify-down(H,i)用O(logi)时间修整堆的性质,使用Heapify-up或Heapify-down我们可以用O(logn)时间在n个元素的堆中删除一个元素。
2.用堆实现优先队列
使用堆数据结构可以有效实现优先队列,优先队列在任何时刻将限定至多保持N个元素。
常用操作:
StartHeap(N),Insert(H,v),FindMin(H),Delete(H,i),ExtractMin(H)