2022_07_24__106期__复杂度讲解

数据结构和算法

 数据结构就是内存中管理数据:增删查改

而数据库实在磁盘中管理数据:增删查改

算法最简单的例子就是排序

数据结构和算法是相辅相成的。

复杂度分为时间复杂度和空间复杂度。

1:如何衡量一个算法的好坏。

可以从时间维度和空间维度进行分析:

现在的算法更加关注时间效率。

算法的时间复杂度是一个函数,他定量描述了该算法的运行时间:通过计算算法中的基本操作的执行次数。

我们举一个例子,假如我们需要100w个算法的基本操作

冒泡排序的时间复杂度为O(N^2)

快排的时间复杂度为O(logN)

我们使用哪个排序更好呢?

假如我们使用冒泡排序的话

100w*100w的结果为1万亿我们需要计算1万亿次

而假如我们使用快排的话

log100w的结果大概是20,我们只需要计算20次。

所以我们选择快排更好。

我们再举一个例子

 count语句一共计算了多少次,N^2+2N+10

当N=10时,对应的结果为130

当N=100时,对应的结果为10210

当N=1000时,对应的结果为1002010

我们计算时间复杂度时,我们只需要取一个大概执行次数。我们把表达式影响不大的项都去掉,则对应的结果为

=N^2

总结:我们在计算时间复杂度时,我们只需要计算大概执行次数,这里我们使用大O的渐进表示法。

所以该算法的时间复杂度为O(N^2)

实例

这串代码的时间复杂度是多少。

 初学者会说结果是N*2+10

但其实N和2N的量级是相同的。而常数10对结果的影响很小,所以忽略不计。

实例

 这个时间复杂度是多少?

这个时间复杂度就是O(M+N)

但是当有前提M>>的时候或者N>>M的时候,时间复杂度是O(M)或者O(N).

冒泡排序的时间复杂度如何求呢?

 假设end=N,我们计算他的基础运算的次数:第一次for循环执行N-1次,第二次执行N-2次,第三次执行N-3次,最后一次for循环执行1次,这是等差数列,等差数列的前N项的和的结果是

((N-1+1)*(N-1))/2,计算的结果为(N^2+N)/2

我们可以忽略二分之一和N,所以时间复杂度结果为O(N^2).

 1:运行时间中的常数全部被取代为1.

2:只保留最高阶项

3:最高阶的系数为1.

实例

 这个函数的时间复杂度是多少呢?

答:时间复杂度为O(1),因为只要是运行时间中的常数,都被当成是1. 这里的O(1)并不代表是1次,而是代表是常数次。

实例

我们知道,strstr是在一个字符串中找另外一个字符串。

那么strchr就是在一个字符串中找一个字符

 那么算法的基本操作的执行次数也非情况

最好情况,1次就找到

平均情况,N^2次找到

最坏情况,N次找到。

在实际中一般关注的是算法的最坏情况,所以该函数的时间复杂度是O(N).

实例

接下来,我们对冒泡排序进行优化操作:

时间复杂度分情况

最好情况下,O(N)

最坏情况下,O(N^2) 

假如不进行优化的话,无论是最好情况还是最坏情况,计算的结果都是N^2

注意:时间复杂度不能数循环。要看算法逻辑,运行计算时间复杂度。

实例

 这个二分查找的时间复杂度是多少?

二分查找是这样的:我们是通过不断的折半,判断来求取我们的目标值的

最坏情况是不断地折半,直到范围中只有一个元素,或者找不到这个元素

假设最坏情况查找了x次。

N是初始的范围,N/2 ^x=1

所以x=logN

所以二分查找的时间复杂度O(logN).

首先,第一个函数是一个递归,递归的次数是N次,每一次递归中也有其他的计算,例如,判断,*,这些次数就相当与常数次,所以时间复杂度为O(N).

 

假如我们这样写呢?这样写的时间复杂度是多少呢?

答:时间复杂度为O(N^2),原因是我们有for循环,第一次循环,我们递归N次,第二次循环,我们递归N-1次,等等,形成了等差数列,等差数列的前N项和形成的时间复杂度为O(N^2)

 

那么斐波那契数列的复杂度呢?

 我们首先要绘制图像

 第一层有2^0个元素,第二层有2^1个元素,最后一层有2^(n-1)个元素

形成了等比数列,等比数列的前N项和就表示执行次数,计算结果为2^n-1,对应的时间复杂度为

O(2^N)

斐波那契数列在实际的算法中是没有用的一种算法。

因为斐波那契数的计算范围太小,当N=40,就需要计算大概1万亿次。

我们对斐波那契数列进行改动,使他的时间复杂度下降。

long long Fib(size_t N)
{
	int i = 0;
	long long f1 = 1, f2 = 1, f3;
	for (size_t i = 3; i <= N; i++)
	{
		f3 = f1 + f2;
		f1 = f2;
		f2 = f3;
	}
	return f3;
}
int main()
{
	long long ret = Fib(1000);
	printf("%lld", ret);
	return 0;
}

long long类型的原因:

答:Fib当输入的参数很小时,就能够计算出很大的值

size_t i = 3从3开始的原因

答:我们的f1和f2都是1,我们这里表示从3开始计算

这时候,我们输入参数1000,进行编译

 一样能够打印出对应的结果。

实例

 第一种思路,假如有N项,我们让最后一项移位拿出,把其他项往后移一位,再把第一项移位到首项,再通过for循环,循环k次,实现向右轮转k步

所以第一种思路的随时间复杂度使O(K*N)

最坏的情况下,时间复杂度为O(N^2)

当K%N==N-1的时候,时间复杂度就是(N^2).

第二种思路

我们创建一个数组元素为N的数组tmp,我们要向右轮转k步,所以我们直接取出后面k位,放在tmp的前K位,再取出前面的N-K位,放在tmp的后N-K位

这种思路需要额外开辟空间,但这种思路的时间复杂度仅为O(N).

第三种思路,我们可以前使前N-K个元素逆置,再取后k个元素逆置,再整体逆置,我们可以检验

前两项逆置

 

后两项逆置

 

再整体逆置 

得到我们的结果,我们进行模拟实现

void reverse(int *p, int begin, int end)
{
	int tmp = 0;
	while (begin < end)
	{
		tmp = end;
		end = begin;
		begin = tmp;
		begin++;
		end--;
	}

}
void rotate(int*p, int numsSize, int k)
{
	if (k >= numsSize)
	{
		k %= numsSize;
	}
	reverse(p, 0, numsSize - k-1);
	reverse(p, numsSize - k, numsSize - 1);
	reverse(p, 0, numsSize - 1);
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值