1.2.2 算法的时间复杂度
算法的效率应该怎么度量?
时间复杂度和空间复杂度
这一节主要讲解时间复杂度
怎么评估算法的时间开销?
传统思维:让算法先运行,事后统计运行时间
传统思维存在问题:
运行速度受机器性能影响,如超级计算机和单片机
和编程语言有关,越高级的语言效率越低,C就比Java快
和编译程序产生的机器指令质量有关(?)
有些算法不能事后统计,如导弹控制算法
(总体来说是有很多外部因素,我们需要排除外部因素;最后一条说明需要能事先估计)
算法时间复杂度:
事先估计算法时间开销T(n)与问题规模n的关系(T表示“time”)
语句频度:
① 1次 ② 3001次 ③④ 3000次 ⑤ 1次
T(3000) = 1 + 3001 + 2*3000 + 1 (简化为每一个语句的耗时相同)
时间开销与问题规模n的关系:T(n)= 3n + 3
问题1:当问题规模n足够大的时候可以忽略n的低阶部分,只保留最高阶部分,甚至可以忽略系数
如T(n)= 3n^3 + 3n^2 + 3 可以视为 T(n)= O(n^3)
算法复杂度分析:
证明方法是高数的基本方法,这里不多展开
结论1:顺序执行的代码只会影响常数项,可以忽略
结论2:只需挑选循环中一个基本操作他的执行次数与n的关系
当出现嵌套循环时:
结论3:如果有多层嵌套循环时只需要关注最深层的循环次数与n的关系
结论:很多算法的执行时间和输入时间有关
一般只考虑最坏、平均时间复杂度
1.2.3 算法的空间复杂度
空间复杂度:
空间开销(内存开销)与问题n之间的关系
程序运行时的内存需求
例子:
函数递归调用带来的内存开销:
函数调用栈:
我们在编程中写的函数,会被编译器编译为机器指令,写入可执行文件,程序执行的时候,会把这个可执行文件加载到内存,在虚拟地址空间中的代码段存放。
如果在一个函数中调用另一个函数,编译器就会对应生成一条call指令,当程序执行到这条call指令时,就会跳到对应的函数入口处开始执行,而每一个函数的最后,都有一条ret指令,负责在函数结束后跳回到调用处继续执行。
(原文链接:https://blog.youkuaiyun.com/qq_29582443/article/details/123937089)
2.1.0 线性表的定义和基本操作
定义就是逻辑结构,基本操作是运算,
后续会探讨不同的存储结构来实现线性表,相对性的运算也是不同的
线性表的定义(逻辑结构):
(每一个数据元素的类型应当相同,各个数据元素所占的空间是一样大的,可帮助计算机快速找到某一数据元素;有限序列,首先要有限,整数列就不行,因为它无限,其实次要有序的数列)
线性表的基本操作
tips:
1、对数据的操作(记忆思路)——创销、增删改查
2、C语言函数的定义——<返回值类型>函数名(<参数1类型>参数1,<参数2类型>参数2,...)
3、在实际开发中,可以根据实际需求定义其他的基本操作
4、函数名和参数的形式和命名可以改变(我觉得最好统一)
5、当参数的修改结果需要带回来时,要引用“&”
为什么要实现对数据结构的基本操作:
关于斐波那契数列用递归算法实现时的时间复杂度和空间复杂度:
实际上需要统计运行次数的程序是:
if(N<3) return 1;
先码住,之后再写