前言
数据结构和数据库的区别?
数据结构是在内存中管理数据,而数据库是在磁盘空间中管理数据(管理数据的方式主要是以增删查改为主)
磁盘可以不带电存储数据,也就是在突然断电的时刻任然可以存储数据,但是内存就不一样必须带电存储
1 算法效率
1.1 如何衡量一个算法的好坏
如何衡量一个算法的好坏呢?比如对于以下斐波那契数列:
long long Fib(int N) {
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}
斐波那契数列的递归实现方式非常简洁,但简洁一定好吗?那该如何衡量其好与坏呢?
1.2 算法的复杂度
根据摩尔定律,得知当前硬件水平的快速发展,空间复杂度已经不在是大家所过分追求的方向,主要研究时间复杂度
2 时间复杂度
2.1 时间复杂度的概念
函数是指的数学中的带未知数的表达式
计算时间复杂度并不是判断代码在计算机上运行时间长短
如果用学校机房的老机器和自己配置的电脑相比较来衡量算法的好坏所运行的时间差异会很大
所以时间复杂度是通过语句所执行的次数来衡量算法的好坏
时间复杂度的计算并不是要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
2.2 大O的渐进表示
2.3 常见的时间复杂度的计算举例
2.3.1 M与N
通常时间复杂度的表示都是用N表示
2.3.2 冒泡排序
冒泡排序的算法思想:第一次比较N-1,第二次N-2…最后1次1 等差数列的求和(N*N-1)/2 数量级是N2==复杂度O(N2)
2.3.3 常数次O(1)
100w,10亿这些常数看上去很大,但实际上相对于CPU的运行速率来讲都是微乎其微的,当前cpu单秒运算能高达亿次
根据上述的时间复杂度的计算发现可以通过数循环的方式
真是如此吗?
2.3.4 希尔排序(了解)
2.3.5 二分排序
2.3.6 阶乘
2.3.7 菲波那切数列
运用错位相减法,计算斐波那契数列的时间复杂度
斐波那切数列计算到40已经是1万亿次了,再大很难计算,所以该算法没啥价值
2.3.8 轮转
额外开辟一个tmp数组 将后k个放前,剩下的N-k个放后,再拷贝回原来的空间 O(2N) 两次
时间复杂度为O(N)
思路三:
效率高的同时还节省空间,并未开辟新的内存空间
3 空间复杂度
不会去计算多少个字节,例如:结构体这种类型的变量开辟的空间是不确定的,结构体存在内存对齐而各编译器默认对齐数不一致,无法计算。
所以空间复杂度计算的是临时、额外开辟的变量个数
这里额外开辟的是exchange、end 和 i 变量,数组作为参数传递进来并不是额外的
for循环的局部域当中为exchange、end和i 开辟空间,1次循环结束就销毁(归还给操作系统)了,再进来还是在这个位置开辟函数栈帧
空间是重复利用的,不累计,而时间是一去不复返需要累积
这里只是额外开辟了3个空间
轮转的第二种方法:额外开辟了一个数组,临时存放改变后的数组
这里tmp是额外开辟的占n个空间,所以空间复杂度是N
Fac阶乘递归的空间复杂度:
额外开辟了N个函数栈帧空间,而每个函数栈帧空间中只有常数个变量,所以最终是O(N)
计算菲波那切数列的空间复杂度
F(N) F(N-1)…F(1) 不断进行递归 递归是先执行左边的完成后再执行右边的
例如:递归到最后1层 F(1)递归完成后要执行F(0) 而F(1)递归完成,函数栈帧空间就归还给操作系统了,而F(0)又要开辟函数栈帧空间,也就是F(0)使用的是F(1)之前所开辟的空间
所以Fib总共所开辟的函数栈帧空间是N个 ,即空间复杂度为:O(N)
4 常见的复杂度对比
N!(N的阶乘)是最大的,一般不存在N!的算法:递归套N层循环