一.时间复杂度
1.什么是时间复杂度
时间复杂度是计算机科学中的一个基础概念,它帮助我们理解和评估算法的效率,对于算法设计和优化至关重要。算法的时间复杂度是一个函数,算法中的基本操作的执行次数,为算法的时间复杂度。
时间复杂度是衡量算法随输入数据规模增长而执行时间增长趋势的一种度量。它可以反应算法的优劣,时间复杂度越小则算法越优越更容易被实际应用,反之则难以被采用。
时间复杂度通常采用大O的渐近表示法来描述。注意这里是渐近,这就代表了时间复杂度是一种粗略的估算而非准确的描述,它通常是反应的算法速率的量级,而非一个准确值。这样的作法不仅是为了方便描述,更是为了在数据较大时,更准确的反映一个算法的量级。
时间复杂度是以最环的结果去计算的。即我们应该以最坏的情况去计算相应的时间复杂度。这样能保证程序的最低运行效率,能度量一个算法的好坏。
时间复杂度通常计算的是执行的次数,往往是循环和递归的次数,我们计算时不必过于细究,因为时间复杂度本来就是渐近的去表示,所以我们只需要大概预估其量级就行了。
在实际操作中比起空间复杂度我们一般更加注重时间复杂度,因为随着硬件的发展内存是越来越大,其实对空间消耗是没有那么的重的。而随着时代的发展为了满足人们对各种功能的需求,程序被越写越大这就更能体现时间复杂度的重要性了。并且你可以想象你没点击一次都要等很久,用户体验很不好,这就体现了时间复杂度的重要性了。
2.一些常用理解
从中我们可以看到,常数次的时间复杂度为O(1),还可以知道计算式只需保留最高阶项,这是因为它更能反应效率的一个量级,指数函数>幂函数>一次函数>对数函数,这可以帮助我们计算时间复杂度。还有就是最高项前的系数也可以省略。具体的不在赘述。
3.一些例子
例1.
void Func2(int N)
{
int count = 0;
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
例2.
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;
}
}
例3.
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;
}
例4.
long long Fib(size_t N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}
答案:
实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)
实例2基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)
实例3基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN) ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN。
实例4通过计算分析发现基本操作递归了2^N次,时间复杂度为O(2^N).
还有更多的例子需要掌握才能更好的理解时间复杂度,请大家自行了解哦
二.空间复杂度
1.什么是空间复杂度
空间复杂度是评估算法内存使用效率的重要指标,它帮助我们理解算法在处理数据时需要多少额外的存储空间。通过优化空间复杂度,可以使算法更加高效,尤其是在处理大规模数据时。它是衡量算法运行过程中所需内存空间的度量。
空间复杂度关注的是算法执行过程中需要的存储空间量,这包括了算法中的变量、数据结构以及递归调用等占用的空间。
为了确定一个算法的空间复杂度,我们需要考虑算法中所有占用空间的元素,包括临时变量、数据结构、递归堆栈等,并根据这些元素随输入规模增长的趋势来确定空间复杂度的级别。
空间复杂度也是用大O的渐近表示法来描述,也是反应的是一个量级,并不是一个准确值。这一点同时间复杂度基本一致。
空间复杂度的常见量级包括常数级别O(1),线性级别O(n),以及平方级别O(n²)等。常数级别表示空间需求不随输入规模变化,线性级别表示空间需求与输入规模成正比,平方级别则表示空间需求与输入规模的平方成正比。
空间复杂度和时间复杂度是评估算法效率的两个不同维度。有时候,为了减少算法的执行时间,可能需要增加额外的空间,反之亦然。因此,在设计算法时,需要根据实际需求平衡时间和空间的使用。
2.一些实例
例1.
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;
}
}
例2.
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;
}
例3.
long long Fac(size_t N)
{
if(N == 0)
return 1;
return Fac(N-1)*N;
}
答案:
实例1使用了常数个额外空间,所以空间复杂度为 O(1)
实例2动态开辟了N个空间,空间复杂度为 O(N)
实例3递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)
还有更多的例子需要掌握才能更好的理解时间复杂度,请大家自行了解哦
三.综述
在很多在线OJ题中会要求空间和时间复杂度,所以我们需要知道他们分别是什么意思。当然还有很多的知识需要大家去了解,我这里就真的只是简述。
在实际应用中,我们往往需要在时间效率和空间效率之间做出权衡。某些算法可能具有较低的时间复杂度,但需要更多的内存空间,反之亦然。
对于资源有限的环境,如嵌入式系统或移动设备,可能更倾向于使用空间复杂度较低的算法。而在处理时间敏感的任务时,如实时系统,则可能需要选择时间复杂度较低的算法。
根据具体的应用场景和需求,我们可能需要优先考虑时间效率或空间效率,或者寻找两者之间的最佳折中方案。总之就是具体情况具体分析。
希望大家真的能每天都能进步一点点。