一、时间复杂度
算法中的基本操作的执行次数就叫做算法的时间复杂度,用 T(n) 表示。T(n) = O(f(n)),即函数时间复杂度的增长至多趋向于 f(n) 。
- 大“O”渐进表示法
(1)用常数1取代运行次数函数中的所有常数项。
(2)在修改后的运行次数函数中,只保留最高阶项。
(3)如果最高阶项存在且不是常数项,则令最高阶项的系数为1
(4)上一步得到的结果就是该算法的 f(n) 。
通过上面我们会发现大“O”渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
在实际中一般情况关注的是算法的最坏运行情况,所以我们一般提到时间复杂度都说得是最坏情况!
下面举例:
void A(int m, int n)
{
int count = 0;
for (int i = 0; i < n; ++i) //n次
{
++count;
}
for (int j = 0; j < m; ++j) //m次
{
++count;
}
printf("%d\n", count);
}
T(n) = O(m + n);
两个不同的未知数,都无法省略
void BubbleSort(int* arr, int n)
{
assert(arr);
int tmp = 0;
for (int i = 0; i < n; i++)
{
int flag = 0;//优化
for (int j = 1; j < i; j++)
{
if (arr[j - 1] > arr[j])
{
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
flag = 1;
}
}
if (flag == 0)
break;
}
}
T(n) = O(n^2);
最好情况:排序完成,只走外层循环遍历一遍,n次即可
最坏情况:与要排的顺序正好相反,外层循环n次,内层循环分别走(n-1)、(n-2)、(n-3)、…、1次。求和序列计算之后结果为n*(n+1)/2次,使用大“O”渐进表示法化解完之后就是上面的结果。
int BinarySearch(int* arr, int num, int c)
{
//arr已排过序
assert(arr);
int begin = 0;
int end = num - 1;
while (begin < end)
{
int mid = (begin + end) / 2;
if (arr[mid] < c)
begin = mid + 1;
else if (arr[mid] > c)
end = mid - 1;
else
return mid;
}
return -1;
}
T(n) = O(log2n); (对数,以2为底的n)
二分法就是看需要除多少次2可以找到,所以直接对数就好。
long long B(int n)
{
return n < 2 ? n : B(n - 1) * n;
}
T(n) = O(n);
B(n) = nB(n-1)
= n(n-1)B(n-2)
= …
= n(n-1)…2B(1)
所以调用n次。
long long C(int n)
{
return n < 2 ? n : C(n - 1) + C(n - 2);
}
T(n) = O(2^n);
二、空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少字节的空间,因为没有什么太大意义,它一般算的是变量的个数,用 S(n) 表示,S(n) = O(f(n))。类比时间复杂度,空间复杂度的计算方式也采用大“O”渐进表示法 ,将变量个数相加,再根据准则简化即可。
下面看举例:
void BubbleSort(int* arr, int n)
{
assert(arr);
int tmp = 0;
for (int i = 0; i < n; i++)
{
int flag = 0;//优化
for (int j = 1; j < i; j++)
{
if (arr[j - 1] > arr[j])
{
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
flag = 1;
}
}
if (flag == 0)
break;
}
}
S(n) = O(1)
共定义了6个变量,是常数。
long long D(int n)
{
//非递归斐波那契数列
long long* f =(long long*)malloc((n + 1) * sizeof(long long));
if (f == NULL)
{
perror("malloc-error:");
return NULL;
}
f[0] = 0;
f[1] = 1;
for (int i = 2; i <= n; i++)
{
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
S(n) = O(n);
主要 malloc 开辟了 n+1 个 long long
long long E(int n)
{
return n < 2 ? n : E(n - 1) * n;
}
S(n) = O(n);
递归调用了n次,开辟了n个函数栈帧,每个函数栈帧主要使用了1个 int 变量大小