1.基本时间复杂度的算法
算法的时间复杂度是一个函数,算法中的基本操作的执行次数,为算法的时间复杂度。
(即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。)
// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N ; ++ i)
{
for (int j = 0; j < N ; ++ j)
{
++count;
}
}
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
Func1 执行的基本操作次数 :F(N)=N*N+2*N+10
用大O的渐进表示法,只取其最高阶项,可表示为O(N²)
大O渐进表示法
1.如果F(N)为常数,则用1表示
例:F(N)=1000表示为O(1)
2.如果最高阶前有系数,则去除该系数
例:F(N)=5N²表示为O(N²)
3.如果算法有多种情况(最好情况,最坏情况,期望情况),则取最坏情况
例:strchr的时间复杂度为O(N)
2.log型的时间复杂度
二分查找的时间复杂度
// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n-1;
// [begin, end]:begin和end是左闭右闭区间,因此有=号
while (begin <= end)
{
int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid-1;
else
return mid;
}
return -1;
}
二分查找的最坏情况为2^n=N,通过化简可以得到n=log2N
此时二分查找的时间复杂度可以表示为O(logN)
3.多重变量的时间复杂度
// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
int count = 0;
for (int k = 0; k < M; ++ k)
{
++count;
}
for (int k = 0; k < N ; ++ k)
{
++count;
}
printf("%d\n", count);
}
此时有两个变量N,M,由于不知道N和M之间的数量级关系,故无法舍去任何一个变量
则Func3的时间复杂度表示为O(N+M)
4.递归函数的时间复杂度
4.1简单的递归函数
// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
if(0 == N)
return 1;
return Fac(N-1)*N;
}
计算递归函数的时间复杂度方法为将所有递归展开,然后把每一个递归执行次数相加
例如该程序,递归了N次,每次递归执行一次,故时间复杂度为O(N)
4.2复杂的递归函数
例1
// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
if(0 == N)
return 1;
for(int i=0;i<N;i++)
printf("%d",N);
return Fac(N-1)*N;
}
将4.1中的例子修改,每层递归中有了不同的执行次数
此时将每层递归展开,第N层递归执行N次,故所有递归相加可表示为等差数列求和
表示为O(N²)
例2
// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}
将该递归展开,最终可以形成一个树状图
不难发现,最终可以写为等比数列求和
即斐波那契递归的时间复杂度为