一:概念和分类
概念:
一个程序或算法的时间效率,也称时间复杂度,复杂度
计算复杂度的秩统计执行次数最多的那种固定操作的次数。
分类:平均复杂度和坏复杂度
注意:如果复杂度时多个n的函数之和,则只关心随n的增长增长最快的那个函数
| 名称|复杂度 |
|--|--|
| 常数复杂度 |o(1) |
| 对数复杂度|o(log(n))|
|线性复杂度 |o(n) |
|多项式复杂度|o(n的k次方) |
|指数复杂度|o(a的n次方)|
|阶乘复杂度|o(n!)|
|插入排序,选择排序,冒泡排序|o(n方)|
|快速排序|o(n*log(n))|
|二分查找|o(log(n))|
二:关于复杂度的计算
问题
A心里想一个1-10000之间的数,B来猜,可以问问题,A只能回答
是或者否。
怎么猜才能问的问题次数最少?
非二分
是1吗?是2吗?。。。。是999那? 平均要问500次
二分法
大于500?大于750?大于625? 每次缩小猜测范围到上次的一半,只需要10次
二分查找函数
写一个函数BinarySeach,在包含size个元素的,从小到大排序的int数组a里查找元素p,如果找到则返回元素下标,如果找不到则返回-1.要求复杂度o(log(n))
BinarySearch
int BinarySearch(int a[],int size,int p)
{
int L=0;//查找区间的左端点
int R=size-1;//查找区间的右端点
while(l<R){//如果查找区间不为空就继续查找
int mid=L+(R-L)/2;//取查找区间正中元素的下标
if (p==a[mid])
return mid;
else if(p>a[mid])
L=mid+1;//设置新的查找区间的左端点
else
R=mid-1;//设置新的查找区间的右端点
}
return -1;
}
写一个函数LowerBound,在包含size个元素的,从小到大排序的int数组a里查找比给的那个整数小的,下表达的元素。
int LowerBound(int a[],int size,int p)//复杂度o(log(n))
{
int L=0;//查找区间的左端点
int R=size-1;//查找区间的右端点
int lastPos=-1;//到目前为止找到的最优解
while(L<R){//如果查找区间不为空就继续查找
int mid=L+(R-L)/2;//取查找区间中元素的下表
if(a[mid]>=p)
R=mid-1;
else{
lastPos=mid;
L=mid+1;
}
}
return lastPos;
}
注意
注意:int mid=(L+R)/2;//取查找区间正中元素的下标
为了防止(L+R)过大溢出 int mid=L+(R-L)/2;
三:二分法求方程的根
问题:
求下面一个方程的一个根:f(x)=x^3-5x^2+10x-80=0;
求出的根位a,则要求|f(a)|<=10^-6;
解法:对f(x)求导,由一元二次方程求根公式知方程f(x)
的导函数=0无解,因此f(x)的导函数恒大于0.故f(x)单调增
f(x)在[0.100]内有一个根,所所以考虑二分法。
二元法求方程的根
double EPS=1e-6;
double f(double x){return x*x*x-5*x*x+10*x-80;}
int main(){
double root,x1=0,x2=100,y;
root=x1+(x2-x1)/2;
int triedTimes=1;//记录一共尝试多少次
y=f(root);
while(fabs(y)>EPs){
if(y>0) x2=root;
else x1=root;
root=x1+(x2-x1)/2;
y=f(root);
triedTimes++;
}
printf("%0.8f\n",root);
printf("%d",triedTimes);
return 0;
}
四:案例:复杂度与使用算法
问题
输入n(n<=100000)个整数,找出其中的两个数,他们之和
等于整数m(假定肯定有解)。题中所有整数都能用int表示
解法一
用两重循环,枚举所有的取数方法,复杂度是0(n2)的。
for(int i=0;i<n-1;++i)
for(int j=i+1;j<n;++j)
if(a[i]+a[j]==m)
break;
100000*100000=100亿,在各种OJ上提交或参加程序
设计竞赛,这样的复杂度肯定会超时!
解法二
1)将数组排序,复杂度o(nlog(n))
2)对数组的每个元素a[i],在数组中二分查找m-a[i],看能否
找到。复杂度log(n),最坏也要查找n-2次,所以查找这部分的复杂度也是o(nlog(n))
这种解法的复杂度是o(n*log(n))
解法三
1)将数组排序,复杂度o(nlog(n))
2)查找的时候,设置两个变量i和j,i的初值为0,j的初值为n-1看a[i]+a[j],如果大于m,就让j减1,如果小于m就让i加1,直至a[i]+a[j]=m;
这种解法的复杂度为o(nlog(n))
问题
农夫john建造了一座很长的畜栏,它包括N(2<=N<=100000)
个隔间这些小隔间的位置为x0,x1.....xN1(0=<xi<1000000000,均为整数,各不相同)
John的C(2<=C<=N)头牛每头分到一个隔间,牛希望互相离得远点,省的互相打扰,怎样才能使任意两头牛之间的最小距离尽可能的大,这个最大的最小距离是多少呢。
解法一
先得到排序后的隔间坐标x0....xN-1;
从1000000000/C到1依次尝试这个"最大的最近距离D",找到的第一个可行的就是答案。尝试方法:
1)第一头牛放在x0
2)若第k头牛放在xi,则找到xi+1到xN-1中第一个位于[xi+D,1000000000]中的xj
第k+1头牛放在xj.中不到这样的xj,则D=D-1)再试
若所有的牛都能放下,则D即答案
复杂度:10000000000/C*N,即1000000000超时
解法二:
先得到排序后的隔间坐标x0....xN-1
在[L,R]内用二分法尝试"最大最近距离"D=(L+R)/2,(L,R初值为[1,1000000/C])
若D可行,则记住该D,然后再新的[L+R]中继续尝试(L=D+1)
若D不可行,则在新[L,R]中继续尝试(R=D-1)
复杂度 log(1000000000/C)*N