先介绍一下效率吧
前言
进入数据结构和深剖C的状态了后续会掺杂着更新.
效率介绍
算法效率
**算法效率分析分为两种:第一种是时间效率,第二种是空间效率。**时间效率被称为时间复杂度,
而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主
要衡量一个算法所需要的额外空间,在计算机发展的早期,计算机的存储容量很小。所以对空间
复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。
所以我们如今已经不需要再特别关注一个算法的空间复杂度
时间效率
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。而运算时间又因为在不同硬件有所不同所以算法中的基本操作的执行次数,为算法的时间复杂度。
空间复杂度
常见时间复杂度的计算
以例题进行讲解
首先先介绍一些大O的渐进表达法
大O符号(Big O notation):是用于描述函数渐进行为的数学符号
**推导大O阶方法: **
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
总之就是保留对运行次数影响最大的那个并去除相乘的数一般就是时间复杂度内的表达式(执行次数为常数则为O(1)
下面以事例进行讲解
// 请计算一下Func1基本操作执行了多少次? 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); }
首先在第一个for循环里有嵌套了一个for循环执行了N*N次
再第二个for循环里执行了2*N次
在最后有执行了常数10次
总执行次数为N^2+2*N+10次
我们规则的第二条里要求只保留最高结束(及在N改变时对结果影响最大的表达式)及N^2所以Func1的时间复杂度为O(N^2)
另外有时我们的程序时间复杂度存在最好情况和最坏情况又要如何表达呢
如:
最好情况: 查找1次
最坏情况: 查找N次
我们在实际中一般情况关注的是算法的最坏运行情况.所以上例时间复杂度为O(N).
二分查找的复杂度就是通过以上原则得到的
// 计算BinarySearch的时间复杂度? int BinarySearch(int* a, int n, int x) { assert(a); int begin = 0; int end = n; while (begin < end) { int mid = begin + ((end - begin) >> 1); if (a[mid] < x) begin = mid + 1; else if (a[mid] > x) end = mid; else return mid; } return -1; }
二分查找有可能一次就得到我们想要的值也可能需要多次查找一直到最坏可能情况,哪最坏的情况次数又是多少呢?
我们设最大次数为X
每次查找失败都会减去一半的查找范围,我们反向推导一下就可以得到这样的表达式
2^X = N 所以X = logN(准确的应该是log以2为底的N但是因为需要公式所以在算法中我们将它简称为logN)
当有两个变量控制运算次数时又要如何表示时间复杂度呢?
如:
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); }
有M又有N , 可以统称为O(N+M)
当然如果给了条件比如M远大于N那就是O(M)
M和N差不多大就可以写成O(M)或O(N)
// 计算Func4的时间复杂度? void Func4(int N) { int count = 0; for (int k = 0; k < 100; ++k) { ++count; } printf("%d\n", count); }
执行次数固定及100所以时间复杂度为O(1)
时间复杂度对照图
空间复杂度的计算
所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法
大O符号(Big O notation):是用于描述函数渐进行为的数学符号
空间复杂度和时间复杂度计算方法类似,都是计算最坏(最大)情况下的复杂度情况.
上事例讲解:
// 计算BubbleSort的空间复杂度? void BubbleSort(int* a, int n) { assert(a); for (size_t end = n; end > 0; --end) { int exchange = 0; for (size_t i = 1; i < end; ++i) { if (a[i - 1] > a[i]) { Swap(&a[i - 1], &a[i]); exchange = 1; } } if (exchange == 0) break; } }
我们建立了int*a,int n,size_t end, int eschange,size_t i5个变量类似于时间复杂度常数的建立都为
O(1)
事例2:
// 计算Fibonacci的空间复杂度? long long* Fibonacci(size_t n) { if (n == 0) return NULL; long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long)); fibArray[0] = 0; fibArray[1] = 1; for (int i = 2; i <= n; ++i) { fibArray[i] = fibArray[i - 1] + fibArray[i - 2]; } return fibArray; }
我们看到long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long));
我们创建了longlong类型的N+1的变量和其他常数多少的变量
所以空间复杂度为O(N) (类似只使用对复杂度影响最大的那个)
事例3:
// 计算阶乘递归Factorial的空间复杂度? long long Factorial(size_t N) { return N < 2 ? N : Factorial(N - 1) * N; }
我们每次调用函数都会建立一个变量及size_t N但是我们调用了N次函数及建立了N个size_t N
所以空间复杂度为O(N)