初入时间,空间复杂度

本文探讨了时间复杂度和空间复杂度的概念,通过实例分析了冒泡排序、快速排序和斐波那契数列,强调了在评估算法效率时考虑主要项的重要性。同时,以斐波那契数列为例,解释了递归在空间复杂度中的作用。
摘要由CSDN通过智能技术生成

目录

时间复杂度

时间复杂度实例

 空间复杂度

空间复杂度实例


我们在写代码时,尤其是在工程中开发程序时,如何来评判一个项目的效率高低?这需要引入时间和空间复杂度。

时间复杂度

 时间复杂度用来计算执行操作的次数,比如通俗的讲一个for循环就是一个O(N)的复杂度,例如:

int func1(int n)
{
	int count = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			count++;
		}
	}
	for (int i =0; i < 2 * n; i++)
	{
		count++;
	}
	int M = 20;
	while (M--)
	{
		count++;
	}
	return count;
}

时间复杂度是一个函数,自变量是传入的参数,在这段代码中,时间复杂度F(n)=n*n+2*n+20。用大O的渐进表示法就是O(n^2)。

相较于n方,2*n+20在时间上的影响远比n方小,为了方便比较,采用大O的渐进表示法,只保留影响最大的项,即n方

 需要注意的是:

  1. 最大项的常数系数要忽略。比如将上面那段代码双重循环再写一遍,时间复杂度F(N)=2*N*N+2*N+20,O(n^2),而不是O(2*n^2)。原因是当无限大时,系数可以忽略,也就是数据很多时,不会因为常数系数的影响使运行效率大幅减慢。
  2. 程序接受到的参数不论大小,只要全部执行一次,就是O(n)的复杂度,如果只执行了常数次,不论大小,就是O(1)的复杂度。例子如下:
    int func2(int N)
    {
    	for (int i = 0; i < 10000; i++)
    	{
    		N -= i;
    	}return N;
    }
    时间复杂度是O(1),就算终止条件是一亿也是O(1)。
  3. 当有两个项时间复杂度相加时,不清楚两项的关系,取影响较大的一项,和取影响最大的一项原理相同:
    ​int func3(int N,int M)
    {
    	int count = 0;
    	for (int i = 0; i < M; i++)
    	{
    		count++;
    	}
    	for (int i = 0; i < N; i++)
    	{
    		count++;
    	}
    	return count;
    }
    
    ​

    是O(max(M,N))的复杂度;

时间复杂度实例

首先我们分析一下冒泡排序的时间复杂度

void BubbleSort(int* a, int n)
{
	//Swap(int *a,int *b);
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] < a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
	return;
}

 

我们再来看看这段快排代码:

int PartSort(int *a,int left,int right)
{
	//void Swap(int *a,int *b);
	//int midi=GetMidi(a,left,right);
	int key = left;
	while(left<right)
	{
		while (left<right && a[right]>=a[key])
		{
			right--;
		}//找大
		while (left < right && a[left] <= a[key])
		{
			left++;
		}//找小
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[key]);
	return left;
}

乍一看,双重循环嵌套,直接O(n^2),那就错啦。我们用画图来看一看代码逻辑

 完成排序,left 和right指针合起来遍历完整个数组,所以时间复杂度为O(n);

 显然,快排的效率比冒泡排序高得多。这是一个阶别的跨越。冒泡是平方阶,快排是线性阶。

 然后我们来看看经典的斐波那契数列

int Fib(int N)
{
	if (N <= 2)
		return 1;
	return Fib(N - 1) + Fib(N - 2);
}

斐波那契数列类似于细胞分裂,一分二,二分四,四分八,……,指数级增加,但很容易错的是理所当然认为因为这样时间复杂度就是O(n^2),那就又错啦。

虽然斐波那契数列时间复杂度确实是O(n^2);但这里要计算等比数列求和,把每一层执行次数相加;就是2^0+2^1+2^2+2^3+2^4+2^5+……+2^n=2^n-2^0=2^n-1;所以时间复杂度为O(n^2)。

斐波那契数列是一个树的形状,但不是完整的树,右下角会缺一块,但不影响时间复杂度。

 空间复杂度

空间复杂度是计算为运行程序而额外开辟的空间大小。现阶段O(1)和O(n)空间复杂度的算法较多,几乎没有O(n^2)及以上的。接下来我们以斐波那契数列为实例看看其空间复杂度。

空间复杂度实例

int Fib(int n) {
	if (n == 1 || n == 0)
		return 1;
	return Fib(n - 1) + Fib(n - 2);
}

注意:空间可重复利用,时间一去不复返。Fib(n-1)和Fib(n-2)可使用同一块内存区。我们对此做一个验证

int Fib(int n) {
	int a = 1;
	printf("%p\n", &a);
	if (n == 1 || n == 0)
		return 1;
	return Fib(n - 1) + Fib(n - 2);
}
int Factor(int n) {
	int a = 1;
	printf("%p\n", &a);
	if (n == 0 || n == 1)
		return 1;
	return n * Factor(n - 1);
}
int main()
{
	printf("/");
	Fib(3);
	printf("/");
	Factor(3);
	return 0;
}

 

函数第一个变量的地址就是函数地址我们用a来观察函数地址的变化。

由此可观察到斐波那契函数和阶乘函数中,在多层递归中,同层的函数地址相同,不同层的函数地址不同;

 

所以函数递归的最大开辟空间就是递归的最大层数,空间 复杂度就是O(N);

 这里简单的介绍了时间空间复杂度,下篇博客仔细分析几个例题,来解决算法的应用。

另外,在学习数据结构时一定要重视画图,把图画好才能更好地理解,有更清晰的思路;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值