1.如何衡量算法的好坏
算法效率分为两种:一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称作空间复杂度。
时间复杂度
用来衡量算法的运行速度(不以时间作为衡量单位,而是和一条基本语句的执行次数成正比,即算法中基本操作的执行次数,为算法的时间复杂度)
空间复杂度
用来描述一个算法在运行过程中临时占用存储空间的大小的度量(不是指占用了多少个字节,而是变量的个数,有限个常量的空间复杂度为1)
2.时间复杂度为什么不使用时间来衡量而使用基本语句的运行次数来衡量?
不同机器上执行一条语句的时间各不相同,以时间作为度量没有意义。
3.时间复杂度的O渐进表示法
大O符号是用于描述函数渐进行为的数学符号
1.用常数1取代运行时间中的所有加法常数
2.在修改后的运行次数函数中,只保留最高阶项
3.去除最高阶的项数,得到的结果为大O阶
4.时间复杂度的:最优、平均、最差情况,为什么时间复杂度看的是最差情况?
最差情况:任意输入规模的最大运行次数(上界)
最优情况:任意输入规模的最小运行次数(下界)
平均情况:任意输入规模的期望运行次数
但是对那些实时性要求非常高的地方,必须分析最坏情况的复杂度。
5.如何求解:二分查找、递归求阶乘、递归斐波那契的时间复杂度?
二分查找:
假设一个数列有n个元素,二分查找元素的个数最终为1
n, n/2, n/2/2, …,n/2/…/2=1;
假设 1 * 2m = n
则时间复杂度可以表示为:m = log2 n
代码如下:
int BinarySearch(int *a,int n,int x)
{
assert(a);
int begin = 0;
int end = n-1;
while(begin < end)
{
int mid = begin + ((end-begin)>>1);//防止溢出求平均;
if(a[mid] < x)
begin = mid +1;
else if(a[mid] > x)
end = mid; //mid不减1因为开区间end取不到
else
return mid;
}
return -1;
}
递归求阶乘:
递归的时间复杂度求解公式为:
(递归执行次数 * 每次函数运行过程中判断次数)
该公式不通用!!!
阶乘代码为:
long long Factroial(size_t N)
{
return N < 2 ? N: Factorial(N-1)*N ;
}
递归次数为(N+1) *(单步执行次数) 1
递归斐波那契数列:
递归形式以树的形式展开
递归次数为:20+21+22 …+2n-3
等比数列求和得到时间复杂度为O(N)
6.如何求空间复杂度? 普通函数&递归函数
普通函数使用常数个额外空间,空间复杂度为O(1);
递归函数:
递归调用了N次就会开辟N个栈,每个栈使用了常数个空间,空间复杂度为O(N);
斐波那契的为递归求法(伪递归:一个函数的中的递归部分只再一次调用递归,而不做其他运算,编译器会将其看成循环)
long long Fib(long long first , long long second , int N)
{
if (N<3)
{
return 1;
}
if (N==3)
{
return first + second;
}
return Fib(second,first+second,N-1);
}
伪递归在递归过程中,不开辟新的栈,而是在上一次开辟的栈中的变量上直接修改值,优化过后的空间复杂度为O(1),时间复杂度为O(N);
递归斐波那契的空间复杂度为O(N),以F;6)为例:F(6)->F(5)->F(4)->F(3)->F(2)->F(1),递归开辟了N个栈。
7.总结常见时间复杂度
O(1) 常数阶 < O(logn) 对数阶 < O(n) 线性阶 < O(nlogn) < O(n^2) 平方阶 < O(n^3) < { O(2^n) < O(n!) < O(n^n) }