时间复杂度及空间复杂度(简略)

时间复杂度

计算时间复杂度时,我们只需计算大致执行次数,再用大O的渐进表示法就可以了

常见的复杂度为O(N),O(1),O(N^2)的几个情况这里就不提了,下面是几个相对来说需要分析的算法

算法1:

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );

这是一个在字符串中找到指定字符的函数,它的时间复杂度分为最好情况、最坏情况和平均情况
最好的情况:首字符就是指定的字符,此时时间复杂度为:O(1)
最坏的情况:最后一个字符才是指定的字符,所以需要从头遍历一遍字符串,时间复杂度为O(N)

一般情况下,关注最坏的情况
所以此函数的时间复杂度为O(N)


算法2:
// 计算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;
	}
}

最好情况:当数组为有序的时候,只需比较n-1次,所以此时的时间复杂度为O(N)
最坏情况:当数组为乱序的时候,比较的次数如下图,所以时间复杂度为O(N^2)
在这里插入图片描述

如果在上面的函数中,去掉exchange ,那么最好情况和最坏情况下的时间复杂度都为O(N^2)
因为在最好情况下,比较完第一轮之后,虽然已经有序,但是还是会进行第二轮比较


算法3:
// 计算BinarySearch的时间复杂度?
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;
}

最好情况:当数组下标为(begin +end)/2的值为指定要查找的值时,一次循环都不需要进行,所以时间复杂度为O(1)
最坏情况:不断地二分查找,直到一侧只剩一个值,不能再二分的情况下
N*(1/2)^n = 1
所以n = log2N
最坏情况下,时间复杂度为O(logN)


算法4:
// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
 if(0 == N)
     return 1;

 return Fac(N-1)*N;
}

时间复杂度为O(N),如下图:
在这里插入图片描述


算法5:

// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{
 if(N < 3)
     return 1;

 return Fib(N-1) + Fib(N-2);
}

和上一个算法类似,也是一层一层地调用,如下图,时间复杂度为O(2^N)
在这里插入图片描述



空间复杂度

空间复杂度,是一个算法在运行过程中临时占用的额外存储空间大小的度量
空间复杂度算的是变量的个数

注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。

算法1:

// 计算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;
	}
}

只定义了几个变量,所以空间复杂度为O(1)


算法2:

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
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;
}

动态开辟出了n-1个long long大小的空间,所以空间复杂度为O(N)


算法3:

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{
 if(N == 0)
     return 1;

	return Fac(N-1)*N;
}

空间复杂度为O(N),递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间

算法4:

// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{
 if(N < 3)
     return 1;

 return Fib(N-1) + Fib(N-2);
}

这个算法的空间复杂度为O(N)而不是O(2^N)

在讲解这个之前,先讲解一个类似原理的情况:

void testa()
{
	int a;
	printf("%p\n",&a);
}
void testb()
{
	int b;
	printf("%p\n",&b);
}
int main()
{
	testa();
	testb();
	return 0;
}

这段代码的运行结果为:输出的2个地址是相同的
因为先调用testa函数,建立了一个testa的栈帧,运行完testa后,它对应的栈帧销毁
然后调用testb函数,所以testb的栈帧就建立了在原先testa的位置,所以输出的2个地址是相同的

分析以下Fib这个递归函数,它的执行过程类似于一个二叉树,只有把当前左子树执行完,才会执行右子树,而右子树建立的栈帧就会建立在左子树的位置上,所以空间复杂度为O(N)

例如,执行Fib(3)后,先执行Fib(2),建立了对应的栈帧,执行完Fib(2)后,栈帧销毁,接着执行Fib(1),Fib(1)的栈帧建立在之前Fib(2)的位置,所以本质上并未额外开辟空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯癫了的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值