时间复杂度和空间复杂度
为什么要有时间复杂度和空间复杂度?它们能干什么?
我们都知道,一台计算机是有固定的内存空间和计算速度的,如果这里有两个程序,一个只需要2KB的内存空间,并且只需要cpu计算100次就能达到目的,而另一个需要5KB的内存空间,并且需要CPU计算1000次才能得出结果,那么一般人都会选择使用第一个程序,因为它占用资源少,计算次数少能节省很多时间。
可是问题来了,如果这里有两个程序,你并没有运行,你也不知道两个程序的运行效率,但你只能选择一个程序,这时候怎么办呢?总不能拿到电脑上运行一下吧!万一这两个程序都是大项目,每个运行的时间都超过了一天,你还打算运行吗?所以此时就引入了程序云松效率的两个制指标—-时间复杂度和空间复杂度
时间复杂度
时间复杂度是描述一个程序在CPU中运行时需要的计算工作量
- 时间复杂度用O()表示,括号里面就是复杂度的数值
- 认定一个表达式的计算的时间复杂度的为O(1)
例如:
int a = 1+2*3/4;//时间复杂度为O(1)
修改后的运行次数函数中,只保留最高项
for(int i=0; i<n; i++)//n为任意数 { printf("hehe\n"); } printf("haha\n"); //看上去时间复杂度为O(n+1),因为只保留最高项,所以为O(n)
去除最高阶项的系数。
for(int i=0; i<n; i++)//n为任意数 { printf("hehe\n"); } for(int i=0; i<n; i++)//n为任意数 { printf("hehe\n"); } //看上去时间复杂度为O(2n),但是去掉最高阶项的系数之后为O(n)
- 常见的时间复杂度有:
- O(1)
- O(n)
- O(logn)
- O(nlogn)
- O(n^2)
- O(n^3)
虽然在计算的时候,n可以为具体的数值,但是,因为时间复杂度时一个大概的数值,并且它是描述以这种方式编写的程序的时间复杂度,所以一般就用n来表示,不用具体的数值。
for(int i=0; i<5; i++) { printf("hehe\n"); } //时间复杂度为O(n),并不是O(5) for(int i=0; i<5; i++) { for(int j=0; j<10; i++) { printf("hehe\n"); } } //时间复杂度为O(n^2) int i = 0 while(i < 100) { i*=2; } //时间复杂度为O(logn),因为i是以倍数增长的,设运行次数为x,则2^x = n(100用n表示),x = logn
程序的时间复杂度如果没有特殊说明都是指的最坏情况的时间复杂度,这样保证了向用户呈现出程序运行的最糟糕时所要的CPU的运行此时,根据CPU的运算速率,自然也就估算出运行所需要的时间。
时间复杂度可以间接的体现出程序运行所需要的时间,但是只有时间时完全不够的,因为当一台计算机在内存很有限的情况下,一般只考虑时间快时不行的,所以就引出了空间复杂度
空间复杂度
空间复杂度是描述程序在运行时,对临时空间的占用大小的描述
- 空间复杂度也用O()方式表示,括号里面就是复杂度的数值。
- 可以这样理解,一个程序在运行中,初始的空间复杂度为O(1),如果要在它的栈空间需要递归的调用自己,那么此时空间复杂度就会为O(n)
例如:
int Fib(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } return Fib(n - 1) + Fib(n-2); } int main() { printf("%d\n", Fib(3)); system("pause"); return 0; }
这时一个求斐波那契数列的第n项的递归写法,从代码中可以看出,递归的时候,每递归一个函数,执行的语句都可以认为是O(1),但是递归了2^n次,因为每次执行函数的时候,都会生成两个递归。所以时间复杂度为O(2^n),但是空间复杂度为O(n),因为每次递归都会在栈中调用一个函数栈空间,所以最后认为其空间复杂度为O(n)
时间复杂度和空间复杂度的关系
- 一个程序在运行中,时间复杂度和空间复杂度就得以确定,但是,这两个量永远都可以认为是互补的,时间复杂度降低,其空间复杂度就会提高,空间复杂度降低,其时间复杂度就会提高
- 一般来说,递归的方式可以减小程序的时间复杂度,但是会增加程序的空间复杂度,循环可以减少程序的空间复杂度,但是会增加程序的时间复杂度,所以一般针对计算机的硬件资源来选择用什么方式来开发程序。
- 虽然程序的时间复杂度和空间复杂度是互补的,但是我们在开发程序的过程中是将两者综合考虑,以至于两者的大小都能接收。
二分查找的复杂度
循环版
int BinSearch(int arr[], int size, int des)
{
int left = 0;
int right = size - 1;
int mid;
while (left <= right)
{
mid = left + (right - left) / 2;
if (arr[mid] == des)
{
return mid;
}
else if (des > arr[mid])
{
left = mid + 1;
}
else if (des < arr[mid])
{
right = mid - 1;
}
}
return -1;
}
时间复杂度为:O(logn)
空间复杂度为:O(1)递归版:
int BinSearchRecursion(int arr[], int left, int right, int dest)
{
if (left > right)
{
return -1;
}
int mid = left + (right - left) / 2;
if (arr[mid] == dest)
{
return mid;
}
else if (arr[mid] < dest)
{
return BinSearchRecursion(arr, mid+1, right, dest);
}
else if (arr[mid] > dest)
{
return BinSearchRecursion(arr, left, mid-1, dest);
}
}
时间复杂度为:O(logn)
空间复杂度为:O(logn)
注:此篇文章所有的log都指的是以2为底log