【数据结构】想要学好怎么计算算法的时间和空间复杂度请点进本文

算法效率

如何衡量一个算法的好坏?一般都是从时间复杂度和空间复杂度两个维度来衡量的。
时间复杂度主要衡量的是一个算法的运行快慢,而空间复杂度主要衡量的一个算法运行所需要的额外空间。

时间复杂度

时间复杂度的概念

时间复杂度的定义:算法中的基本操作的执行次数,为算法的时间复杂度。
就是找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。
比如计算下面中++count语句总共被执行了多少次:

void Func1(int N)
{
	int count = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			++count;
		}
	}
	for (int k = 0; k < 2 * N; k++)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	printf("%d\n", count);
}

如果在数学中使用函数表达式来求这个语句的执行次数的话得到的结果如下:
image.png
在实际中我们计算时间复杂度的时间时,我们比不一定要计算精确的执行次数,而只需要大概执行次数,在表示上我们可以使用大O的渐进表示法。
所以可以这么理解大O的渐进表示法,就是抓大头,也就是取决定量级的那一项。
即上述问题的时间复杂度就是O(N^2)

大O的渐进表示法

  1. 用O(1)表示所有的常数时间复杂度。
  2. 只保留决定的量级的项。
  3. 取出确定量级项中的系数。

同时在时间复杂度中存在最好、平均和最坏的情况:
**最坏的情况:**任意输入规模的最大运行次数(上限)
**平均的情况:**任意输入规模的期望次数
**最好的情况:**任意输入规模的最小运行次数(下限)
但是在实际中一般关注的是算法中的最坏运行情况。

常见的时间复杂度举例

例1:
计算Func2的时间复杂度

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);
}

上述代码精确的时间复杂度是Func(N)=2N+10,使用大O的渐进表示法的是O(N);
_因为决定量级的项就是2
N,同时还有除去系数那么结果就是O(N)_
例2:
计算Func3的时间复杂度

void Func3(int N,int M)
{
	int count = 0;
	for (int k = 0; k < N; k++)
	{
		count++;
	}
	for (int k = 0; k < M; k++)
	{
		count++;
	}
	printf("%d\n", count);
}

如果在没有前提条件下的话那么上述代码的时间复杂度就是O(M+N);如果说M远大于N那么时间复杂度就是O(M),反之就是O(N)。
例3:
计算Func4的时间复杂度

void Func4(int N)
{
	int count = 0;
	for (int i = 0; i < 100; i++)
	{
		++count;
	}
	printf("%d\n", count + N);
}

上述代码的时间复杂度是O(1),因为O(1)不是表示1次,而是表示常数次。
为什么常数次可以用1表示?

  1. 因为常数是整数类型所以常数的大小是受到类型的限制
  2. 因为cpu对于运算万亿级一下的次数都是非常快的

例4:
计算strchr的时间复杂度

const char * strchr ( const char * str, int character );

strchr是c语言中的一个库函数,它的作用就是在str字符数组中查找一个字符,它所使用的方法如下:

while(*str)
{
    
}

通过while循环来查找字符数组中的指定字符。
它有3种情况

  1. 最好的情况就是一次就找到了
  2. 找了差不多一半的次数
  3. 找到了最后一次

因为在实际中我们关注的是算法的最坏情况所以数组中搜索数据的时间复杂度是O(N)。
所以说时间复杂度的计算是一个保守稳健的预期。
例5:
计算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(N2),这是冒泡排序其精确的时间复杂度是(N-1)*N/2,此数学表达式化解成大O的渐进表达式之后就是O(N2)。
例6:
计算BinarySearch的时间复杂度

int BinarySearch(int* a, int n, int x)
{
	assert(a);
	int begin = 0;
	int end = n - 1;
	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;
}

上述代码中的时间复杂度是O(logN)
image.png
image.png
在大O的渐进表达式中只有以2为底的才能用logN表示
例7:
计算阶乘递归Fac的时间复杂度

long long Fac(size_t N)
{
	if (0 == N)
		return 1;

	return Fac(N - 1) * N;
}

上述代码中的时间复杂度是O(N)
image.png
总结:递归算法时间复杂度是多次调用的次数累加之和
例8:
计算斐波那契递归Fib的时间复杂度

long long Fib(size_t N)
{
 if(N < 3)
 return 1;

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

上述代码中的时间复杂度是O(2^N)
image.png
image.png
思路1:排序+遍历(下一个数不等于下一个数据+1,这个下一个数就是消失的数字)
时间复杂度:O(logN*N)

int missingNumber(int* nums, int numsSize){
    int n=numsSize;
    int ret=n*(n+1)/2;
    for(int i=0;i<n;i++)
    {
        ret-=nums[i];
    }
    return ret;
}

思路2:使用等差数列计算0到N相加的结果减去数组中的值,结果就是消失的数字
时间复杂度:O(N)
思路3:使用异或
image.png
image.png

空间复杂度

空间复杂度是对一个算法在运行过程中额外临时占用存储空间大小的量度。
空间复杂度不是程序占用了多少bytes的空间,因为这个也没有太大的意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也使用大O的渐进表示法。
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。
例1:
计算Bubblesort的空间复杂度

void Bubblesort(int *a,int n)
{
	assert(a);
	for (size_t i = n; i > 0; i--)
	{
		int exchange = 0;
		for (size_t j = 1; j < i; j++)
		{
			if (a[j-1]>a[j])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange==0)
		{
			break;
		}
	}
	
}

上述代码的空间复杂度是O(1),因为只是额外的创立了三个变量,根据 大O的渐进表示法可以得到空间复杂度O(1)。
例2:
计算Fibonacci的空间复杂度

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;
}

上述代码的空间复杂度是O(N),因为在代码中使用malloc函数创建了N+1个空间,所以它的空间复杂度是O(N)。
例3:
计算阶乘递归Fac的空间复杂度

long long Fac(size_t N)
{
	if (N == 0)
		return 1;

	return Fac(N - 1) * N;
}

上述代码的空间复杂度是O(N),解释如下:
image.png
从图可以看出一共创建了N+1个空间,所以得到的空间复杂度是O(N)。
例4:
计算斐波那契递归Fib的空间复杂度


long long Fib(size_t N)
{
	if (N < 3)
		return 1;

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

上述代码的空间复杂度是O(N),解释如下:
image.png
注意:时间是可以累积叠加的,空间是可以重复利用的。
递归的层次太深,容易栈溢出。

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

君落华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值