数据结构和算法基础
一、目录简介
1 1、数据结构和算法的作用 2 2、算法引入 3 3、算法的概念 4 4、算法的五大特征 5 5、执行时间反应算法效率 6 6、时间复杂度与“大O记法” 7 7、时间复杂度 8 8、时间复杂度的引入 9 9、引入表示时间复杂度的特征 10 10、大O表示法 11 11、最坏时间复杂度 12 12、时间复杂度的集中基本计算规则 13 13、常见时间复杂度
二、数据结构和算法
数据结构和算法在开发中是有序优化性能的时候的必备利器。
数据结构和算法的作用:
如果没有数据结构和算法,有时候面对问题可能会没有任何思路不知道如何下手,或许大部分时间都能解决问题,可是程序的运行效率和开销没有意识,性能低下。
所以数据结构和算法是一个开发人员的必备基本功不是一朝一夕就能练成的,需要平时不断的去学习和积累。
算法概念:
算法是计算机处理信息的本质,因为计算机程序本质上是一个算法告诉计算机确切的步骤来执行一个指定的任务,一般来说,当一个算法在处理信息的时候,会从输入设备或者数据的存储地址读取数据,把结果写到输出设备或者某个存储地址以后再调用
三、算法引入
如果a+b+c=100,并且a^2+b^2=c^2(a,b,c都是自然数),求出a、b、c可能的组合
解析:
首先想到的可能是枚举法
用枚举法:
a=0
b=0
c=1
或者
a=0
b=1
c=1
等等这样来试验
代码如下:
import time start_time=time.time() for a in range(0,101): for b in range(0,101): for c in range(0,101): if a+b+c==100 and a**2+b**2==c**2: print("a,b,c:%d,%d,%d"%(a,b,c)) end_time=time.time() print("time:%s" %(end_time-start_time)) #time:0.17448711395263672 print("finish")
其实还可以进行优化:
import time start_time=time.time() for a in range(0,101): for b in range(0,101-a): c =100- a- b if a**2+b**2==c**2: print("a,b,c:%d,%d,%d"%(a,b,c)) end_time=time.time() print("time:%s" %(end_time-start_time)) #time:0.006990909576416016 print("finish")
这个优化解析:这里由于a和b不能相同,所以可以少一个循环遍历
然后两种程序时间结果对比,可以看出两个程序的效率
四、算法五大特征
1、 输入:算法具有0个或者多个输入
2、 输出:算法至少有一个或者多个输出
3、 有穷形:算法在有限的步骤之后就会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成
4、 确定性:算法中的每一步都是有确定含义的,不会出现二义性
5、 可行性:算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成
五、执行时间反应算法效率,这样可靠吗?
对于上面的同一个问题,给出了两种解决算法,在两种算法的实现中,我们对程序执行的时间进行了测算,发现两端程序的时间相差悬殊,得出结论:实现算法的程序的执行时间可以反映出算法的效率,即算法的优劣
单靠时间之绝对可靠吗?
如果一段程序跑在不同的机器上面,时间是不一样的,因此单靠执行的时间来比较算法的优秀与否,并不一定是客观准确的!程序的运行离不开计算机环境(包括硬件和操作系统)这些客观原因会影响程序运行的速度并且反应在程序的执行时间上。
六、时间复杂度与“大O记法”
每台机器执行的总时间不同,但是执行基本运算数量和答题是相同的
七、时间复杂度
什么是时间复杂度?
每台机器运行的总时间就是基本运行数量乘于每一个基本步骤完成的时间
每台机器运行的总时间==基本运行数量×每一个基本步骤完成的时间
基本运行数量的总和 就是时间复杂度
我们把算法执行时间效率的快慢 称之为 时间复杂度
时间复杂度怎么去描述?
我们用一个程序执行到底经过了多少个基本运算数量来描述时间复杂度
八、时间复杂度的引入
引入上面的程序:
import time
start_time=time.time()
for a in range(0,101):
for b in range(0,101):
for c in range(0,101):
if a+b+c==100 and a**2+b**2==c**2:
print("a,b,c:%d,%d,%d"%(a,b,c))
end_time=time.time()
print("time:%s" %(end_time-start_time)) #time:0.17448711395263672
print("finish")
这里我们让时间复杂度叫做T
上面这个程序的时间复杂度为多少?
第一次循环要做100次
第二次循环又要做100次
第三层循环也要做100次
第四层 细化要做10次,如果不细化,就是两句话
T=100*100*100*2
如果让上面的程序更改为a+b+c=200
T=200*200*200*2
如果是该为n
T=n*n*n*2
这类问题和规模n有关系T(n)
上面的时间复杂度就是
T(n)=n^3*2
我们就把T(n)就叫做这个程序的时间复杂度
九、引入表示时间复杂度的特征
我们可以把
T(n)=n^3*2
T(n)=n^3*10
上面的函数同一个数量级的也就是和T(n)=n^3 是同一个数量级的
比如服务器用的是T这个数量级,而不去关注是多少T,以前的mp3是M的数量级,而不去关心是多少M
也就是上面T(n)=n^3*10 的10这个系数只影响走势
T(n)=n^3*10+10
g(n)=n^3
T(n)=k* g(n)
T(n)和g(n)相差的就是常量 k,这个时候我们就说g函数是T函数的渐进函数
详细解释:
“大O记法”:对于单调的整数函数f,如果存在一个整数函数g和实常数c>0,使得对于充分大的n总有f(n)<=c*g(n),就说函数g是f的一个渐近函数(忽略常数),记为f(n)=O(g(n))。也就是说,在趋向无穷的极限意义下,函数f的增长速度受到函数g的约束,亦即函数f与函数g的特征相似。 时间复杂度:假设存在函数g,使得算法A处理规模为n的问题示例所用时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐近时间复杂度,简称时间复杂度,记为T(n)
十、大O表示法
我们用提出来的g(n)(能够表示出时间复杂度的特征)就叫做一个时间复杂度的大O表示法
其实就是 能够表示时间复杂度特征 就表示时间复杂度的大O表示法
T(n)=O(g(n))
详细解释:
对于算法进行特别具体的细致分析虽然很好,但在实践中的实际价值有限。对于算法的时间性质和空间性质,最重要的是其数量级和趋势,这些是分析算法效率的主要部分。而计量算法基本操作数量的规模函数中那些常量因子可以忽略不计。例如,可以认为3n2和100n2属于同一个量级,如果两个算法处理同样规模实例的代价分别为这两个函数,就认为它们的效率“差不多”,都为n2级。
十一、最坏时间复杂度才是我们分析的时间复杂度
1、 基本操作,只有常数项,认为其时间复杂度为O(1)
2、 顺序结构,时间复杂度按加法进行计算
3、 循环结构,时间复杂度按惩罚进行计算
4、 分支结构,时间复杂度取最大值
5、 判断一个算法的效率的时候,往往只需要关心操作数量的最高次项,其他次要项和常数项可以忽略
6、 在没有特殊说明的时候我们分析的算法的时间复杂度都是指的最坏时间复杂度
因此我们把我们优化的程序的时间复杂度为:
import time start_time=time.time() for a in range(0,101): for b in range(0,101-a): c =100- a- b if a**2+b**2==c**2: print("a,b,c:%d,%d,%d"%(a,b,c)) end_time=time.time() print("time:%s" %(end_time-start_time)) #time:0.006990909576416016 print("finish")
T(n)=n*n*(1+max(1,0))
=2n^2
因此这个程序的时间复杂度为O(n^2)
十二、常见时间复杂度
|
执行次数函数举例 |
阶 |
非正式术语 |
|
12 |
O(1) |
常数阶 |
|
2n+3 |
O(n) |
线性阶 |
|
3n2+2n+1 |
O(n2) |
平方阶 |
|
5log2n+20 |
O(logn) |
对数阶 |
|
2n+3nlog2n+19 |
O(nlogn) |
nlogn阶 |
|
6n3+2n2+3n+4 |
O(n3) |
立方阶 |
|
2n |
O(2n) |
指数阶 |
注意,经常将log2n(以2为底的对数)简写成logn
十三、常见时间复杂度之间的关系

所消耗的时间从小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

浙公网安备 33010602011771号