1.算法复杂度——O记号,Ω符号,θ符号
T(n)=O(f(n)) iff c>0,当 n足够大时,有T(n)<c*f(n)
T(n)=Ω(f(n)) iff c>0,当 n足够大时,有T(n)>c*f(n)
T(n)=θ(f(n)) iff c1c2>0,当 n足够大时,有c1*f(n)>T(n)>c2*f(n)
2.复杂度类型
1)常数复杂度——O(1):效率最高
2)对数复杂度——O(logn):lnN,lgN,log,这类算法非常有效,复杂度无限接近于常数,logN=O(N^c)
3)多项式复杂度——O(n^c):
4)线性复杂度——O(n):所有O(n)类函数
5)指数复杂度——O(a^n):这类算法计算成本增长极快,通常被认为不可忍受
3.复杂度分析的主要方法:
迭代:级数求和
递归:递归跟踪+递推方程
猜测+验证
4.封底估算
1天=24hr*60min*60sec=25*4000=10^5sec
一生=一世纪=100yr*365=3*10^4day=3*10^9sec
三生三世=10^10sec
5.迭代和递归
计算任意n个整数之和
迭代:
int sum=0;//O(1)
for(int i=0;i<n;i++)//O(n) sum+=A[i];O(1) return sum;O(1)
递归:
return (n<1)?0:sum(A,n-1) +A[n-1];
在分析复杂度的时候都会把递归调用语句抹除
递归跟踪分析:检查每个递归实例sum(A,n-1),累计所需时间,其总和即算法执行时间,面对更加复杂的函数时,应用范围有限:T(n)=O(1)*(n+1)
递推方程分析:为求解sum(A,n),需递归求解规模为n-1的问题sum(A,n-1)//T(n-1)
再累加上A[n-1]//O(1)
递归基:sum(A,0)//O(1)
递推方程:T(n)=T(n-1)+O(1)
T(0)=O(1)
推导:T(n)-n=T(n-1)-(n-1)
6.减而治之(decrease and conquer)
求解一个大规模的问题,可以将其划分为两个子问题:其一平凡,另一规模缩减
分别求解子问题,由子问题的解得到原问题的解
当程序到达递归基,则返回
递归基:判断递归是否继续的条件
7.分而治之
为求解一个大规模的问题,可以将其划分为若干个规模大体相当的子问题分别求解,由子问题的解得到原问题的解
数组求和:二分递归 sum(int A[], int lo, int hi){ if(lo==hi) return A[lo]; int mid=(lo+hi)/2; return sum(A, lo, mid)+sum(A, mid+1, hi); }
递归跟踪分析:T(n)=各层递归实例所需时间之和(将递归调用的语句去掉)
=O(1)*(2^0+2^1+2^2+...+2^logn)=O(n)
从递推的角度看:
为求解sum(A,lo,hi),需递归求解sum(A,lo,mid)和sum(A,mid+1,hi) //2*T(n/2)
进而将子问题的解累加 //O(1)
递归基:sum(A,lo,lo) //O(1)
递推关系:T(n)=2T(n/2)+O(1)
T(1)=O(1)
8.动态规划(由自顶向下的递归,改为由自底向上的迭代)
make it work,make is right(递归)
make it fast(迭代)
通过递归找出了算法的本质,并且给出一个初步的解之后,再将其等效的转化为迭代的形式
斐波那契数:fib(n)=fib(n-1)+fib(n-2)
复杂度:T(0)=T(1)=1,T(n)=T(n-1)+T(n-2)+1
T(n)=O(n^2)
public int fibonacci(int n) { // write your code here int f=0; int g=1; while(--n>0){ g=g+f; f=g-f; } return f; }
最长公共子序列(LCS):
对于序列A[0, n]和B[0, m],LCS(A, B)无非三种情况:
1)若n=-1或m=-1,则取作空序列(“”) //递归基
2)若A[n]='X'=B[m],则取作LCS(A[0,n),B[0,m))+'X' //减而治之
3)若A[n]!=B[m],则在LCS(A[0,n],B[0,m))与LCS(A[0,n),B[0,m])中取更长者 //分而治之
9.算法的有穷性
程序不一定是算法
10.
S包含n个正整数,求和为2m,S是否有子集满足该子集的和为m?
np-complete问题
11.