学习软件开发这么多年,常常听到程序=数据结构+算法,但是很多人对这句话提出质疑,因为实际项目开发的时候大部分人是做螺丝钉的角色,而且大部分甘于做螺丝钉的角色,就会认为实际项目,只是完成业务开发而已,去哪都是增删改查,数据结构根本用不到。我认为,算法和基本的数据结构是非常重要的,对于一个合格的程序猿来说,有时候我们没有涉及到,只是别人把需要的事情都给我们做了,比如的java版本的hashmap,采用红黑树的结构,提高了更多效率,软件开发高速发展的同时,编程的门槛也会越来越低,只有了解了最本质的才会不被技术淘汰。
算法的五大特性:
1.有穷性:不是数学,算法比较合理,每一步在规定时间内进行
2.确定性:每一条指令都有一个明确的含义
3.可行性:算法可以执行
4.输入0或者多个
5.输出 只有一个
算法设计的四大要求:
1.正确性
2.可读性
3.健壮性:容错能力,输入数据非法的时候,不会产生的输出结果
边界问题 (数组的长度的判断,非法字段,树Root是否为空)
4.效率和存储
注:1.研究算法的复杂度,侧重的是研究算法随着输入规模扩大增长量的一个抽象,而不是精确定位执行多少次
2. 不关心编译语言,不关心机器
所以我们应该用什么方式进行算法的度量方式呢?接下来我们聊聊时间复杂度
二、时间复杂度 1.概述我们知道程序的效率可以称之为程序的时间复杂度,通俗点说就是算法执行的时间,所以将算法中基本操作的执行次数作为算法时间复杂度的度量。
比如:如何求1+2+..... n的结果
第一种:O(n)
int sum=0; for(int i=0;i<=n;i++){ sum=sum+i; }第二种:O(1)
int i=0; int sum=0; sum =(1+n)*n/2;上述的例子可以说明如果不同的策略对待同一个需求而已,时间复杂度是不一样的,算法的优化,时间复杂度越低也是算法优化的目的之一。
时间复杂度:算法中基本语句重复执行的次数是问题规模n的某个函数f(n),算法的时间量度记作:\(T(n)=O(f(n))\)表示随着n的增大,算法执行的时间的增长率和f(n)的增长率相同,称渐近时间复杂度。
函数的渐进增长:给定两个函数,f(n).g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么我们说f(n)的增长渐进快于g(n)
上面讨论的时间复杂度是官方解释,仔细可以看时间复杂度可以表示渐进函数的抽象形式即可。
2.时间复杂度的记法: 1.大O记号 (常用)假设\(f(n)和g(n)\)的定义域是非负整数,存在两个正整数c和n0,使得n>n0的时候,\(f(n)≤c*g(n)\),则\(f(n)=O(g(n))\)。可见\(O(g(n))\)可以表示算法运行时间的上界。\(O(g(n))\)表示的函数集合的函数是阶数不超过\(g(n)\)的函数。
例如:\(f(n)=2*n+2=O(n)\)
证明:
\(当n>3的时候,2*n +2<3n,所以可选n0=3,c=3,则n>n0的时候,f(n)<c*(n),所以f(n)=O(n)。\)
现在再证明\(f(n)=2*n+2=O(n^2)\)
证明:\(当n>2的时候,2*n+2<2*n^2,所以可选n0=2,c=2,则n>n0的时候,f(n)<c*(n^2),所以f(n)=O(n^2)。\)
同理可证\(f(n)=O(n^a)\),a>1
2.Ω记号\(f(n) > c*g(n)\)
Ω记号与大O记号相反,他可以表示算法运行时间的下界。\(Ω(g(n))\)表示的函数集合的函数是所有阶数超过g(n)的函数。
例如:\(f(n)=2*n^2+3*n+2=Ω(n^2)\)
证明:\(当n>4的时候,2*n^2+3*n+2>n^2,所以可选n0=4,c=1,则n>n0的时候,f(n)>c*(n^2),所以f(n)=Ω(n^2)。\)
同理可证\(f(n)=Ω(n),f(n)=Ω(1)\)
3.Θ记号Θ记号介于大O记号和Ω记号之间。他表示,存在正常数c1,c2,n0,当n>n0的时候,\(c1*g(n)≤f(n)≤c2*g(n)\),则f\((n)=Θ(g(n))\)。他表示所有阶数与g(n)相同的函数集合。
4.小o记号\(f(n)=o(g(n))当且仅当f(n)=O(g(n))且f(n)≠Ω(g(n))\)。也就是说小o记号可以表示时间复杂度的上界,但是一定不等于下界。
5.例子假设f(n)=2n^2+3n+5,
则f(n)=O(n^2)或者f(n) = O(n^3)或者f(n)=O(n^4)或者……
f(n)=Ω(n^2)或者f(n)=Ω(n)或者f(n)=Ω(1)
f(n)=Θ(n^2)
f(n) = o(n^3)或者f(n)=o(n^4)或者f(n)=o(n^5)或者……
3.时间复杂度类型 1.常数阶如上面的例子可以知道,执行次数是常数,可以定为O(1)
int i=0; int sum=0; sum =(1+n)*n/2; 2.线性阶如上述的例子可以知道,单次循环n,定为O(n)
int sum=0; for(int i=0;i<=n;i++){ sum=sum+i; } 3.对数阶