QAQ数据结构第一天,开心思密达
目录
算法的概念
算法本质上是告诉计算机按照确切得步骤来执行一个指定的任务;
算法实际上就是解决问题的方法和思想,独立于编程语言。
算法的五大特征
(1)输入:算法的输入可以为0,也可以有多个输入(输入可有可无);
(2)输出:算法至少有1个或多个输出;
(3)有穷性:算法在有限的步骤之内解决问题,并且每一个步骤在可接受的时间内完成;
(4)确定性:算法中的每一步都有确定的含义,不会出现二义性,不能有歧义;
(5)可行性:思路是可以通过计算机语言实现的,每一步都是可行的。
算法效率衡量
对于同一问题,可能有多种解决算法,在算法实现中,我们对程序执行的时间进行测算,实现算法的执行时间可以反映处算法的效率,即算法的优劣。
注意:时间一定能够反映效率吗?有时候在性能比较低的计算机中,也不一定能通过时间表现出效率
#a+b+c = 1000, 且a的平方加b的平方=c的平方
#利用枚举法来做
'''
思路:
a从0 开始
b从0 开始
c从0 开始
一个一个试着来
使用到了嵌套结构
'''
'''
import time
start_time = time.time()
for a in range(0, 1001):
for b in range(0, 1001):
for c in range(0, 1001):
if a + b + c == 1000 and a**2 + b**2 == c ** 2:
print("a,b,c分别为:{0},{1},{2}".format(a, b, c))
end_time = time.time()
print(end_time - start_time)
print("finished")
a,b,c分别为:0,500,500
a,b,c分别为:200,375,425
a,b,c分别为:375,200,425
a,b,c分别为:500,0,500
105.38611555099487
finished
可以看出来,一共的用时时间为105.4秒钟
让计算机以最笨的方式来计算的
'''
import time
start_time = time.time()
for a in range(0, 1001):
for b in range(0, 1001):
c = 1000 - a - b
if a**2 + b**2 == c**2:
print("a,b,c分别为:{0},{1},{2}".format(a, b, c))
end_time = time.time()
print(end_time - start_time)
print("finished")
'''
a,b,c分别为:0,500,500
a,b,c分别为:200,375,425
a,b,c分别为:375,200,425
a,b,c分别为:500,0,500
0.9354948997497559
finished
可以看出来,效率提高了很多
'''
时间复杂度T与“大O记法”
时间复杂度是判断代码运行效率的标准。这里是时间复杂度可以看做计算规模,例如循环的次数。
对于算法的运行步骤进行分析,分析得越详细,越能够体现其复杂程度
但是,并不需要计算得特别精准,我们只需要进行大概的估算
我们基本可以把N^3(N的三次方)* 2 和N^3(N的三次方)*10和N^3(N的三次方)都是一个数量级,基本是一个等级上的。涉及到渐进函数的问题(反正也不重要,重要的是N,不是而不是常数系数)
“大O记法”实际上就是忽略常数系数,只关注N的数量级
在算法设计中我们关注的是:最坏时间复杂度和最优时间复杂度
1、最坏时间复杂度—算法完成工作最多需要多少基本操作;
最坏时间复杂度提供了一种保证,表明算法在这种程度的基本操作中一定可以完成工作
2、最优时间复杂度—算法完成工作最少需要多少基本操作;
3、平均时间复杂度—算法完成工作平均需要多少操作。
时间复杂度的几条基本计算规则
对于基本步骤来说,只有常数项,认为其时间复杂度为O(1),即为1
顺序、条件语句、循环这三个组合到一起基本上可以囊括所有的算法实现
顺序就是基本步骤之间的累加:
循坏结构要用乘法进行计算;
分支结构要考虑最长的步骤,考虑的是最坏时间复杂度
判断一个算法的效率时,往往只需要关注操作数量的最高此项,其他次要项和常数项可以忽略;
如果没有特殊说明,我们分析的算法时间是最坏时间复杂度
常见的时间复杂度
执行次数函数单例 | 阶 | 非正式术语 |
12,只有顺序步骤 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
3n^2+ 2n+1 | O(n^2) | 平方阶 |
5log(2)n+20 | O(logn) | 对数阶 |
3n+3nlog(2)n+19 | O(nlogn) | nlogn阶 |
6n^3+2n^2+3n+4 | O(n^3) | 立方阶 |
2^n | O(2^n) | 指数阶 |
记忆要点:
O(1)< O(logn) < O(n)< O(nlogn)< O(n^2)< O(n^3) < O(2^n) <O(n!) < O(n^n)
测试list里面各种追加方法的效率
timeit模块
timeit模块里面的Timer方法
Timer(“测试代码”, “让你的代码跑起来需要的条件”, timer(不用管))
Timer的timeit方法是可以反复操作多次返回的一个平均值
def t1():
li4 = []
for i in range(10000):
li4.append(i)
def t2():
li5 = []
for i in range(10000):
li5 += [i] #这种形式的累计操作是优化的,执行效率远高于li5 = li5 + [i]
def t3():
li6 = [i for i in range(10000)]
def t4():
li7 = list(range(10000))
def t5():
li8 = []
for i in range(10000):
li8.extend([i])
timer1 = Timer("t1()", "from __main__ import t1")
print("append:", timer1.timeit(1000))
timer2 = Timer("t2()", "from __main__ import t2")
print("+:", timer2.timeit(1000))
timer3 = Timer("t3()", "from __main__ import t3")
print("列表推导式:", timer3.timeit(1000))
timer4 = Timer("t4()", "from __main__ import t4")
print("range:", timer4.timeit(1000))
timer4 = Timer("t5()", "from __main__ import t5")
print("extend:", timer4.timeit(1000))
'''
append: 0.5824885000000001
+: 0.6853064999999999 #已经优化过了的效率较高
列表推导式: 0.28027329999999995
range: 0.1692867
extend: 0.8408731000000003
照理说+是最慢的方法,最不建议采纳的
'''
def t6():
li = []
for i in range(10000):
li.append(i) #从尾部添加元素
def t7():
li = []
for i in range(10000):
li.insert(0, i) #从头开始添加元素
timer6 = Timer("t6()", "from __main__ import t6")
print("append:", timer6.timeit(1000))
timer7 = Timer("t7()", "from __main__ import t7")
print("insert:", timer7.timeit(1000))
'''
append: 0.5251785
insert: 25.314356
'''
List里面的各种方法的时间复杂度总结
索引、队尾添加都是O(1),pop(i)、insert(i,item)是O(n)
dict里面的各种方法的时间复杂度总结
算法重点关注的是问题的解决步骤,并没有关注数据的类型
数据结构导学
数据结构解决的问题是:一组数据如何保存,保存形式是怎样的
数据是一个抽象的概念,将其进行分类后就是python中的基本类型:int,float,char等;数据结构是将一组数据放在一起的形式,python有一些已经封装好的数据结构:列表、元组和字典,而有的数据结构需要我们自己去定义
程序= 数据结构+算法
#储存一组班级信息,不考虑重名的情况下,有以下几种方式:
[
("高宇星", "24", "山西"),
("韩敏", "18", "山西"),
("胖头鱼", "20", "河北"),
("饼饼", "21", "山西"),
]
for stu in student:
if stu[0] == "高宇星"
print(stu[0], stu[1], stu[2]
[
{"name": "高宇星", "age": "24", "hometown":"山西"},
{"name": "韩敏", "age": "18", "hometown": "山西"},
{"name": "胖头鱼", "age": "20", "hometown": "河北"},
{"name":"饼饼", "age": "21", "hometown": "山西"},
]
{
"高宇星": {"age": 17, "hometown": "shanxi"}
}
print(stu["高宇星"])
print(stu.get["高宇星"])
#查找一个学生的信息,如果是列表的形式,需要遍历所有的信息,如果是字典的形式,我们直接可以锁定“键”去找值
抽象数据类型
将数据和操作的方式放在一起就是数据类型
先把数据的保存方式定好,然后把这个数据结构支持的方法定义好,如果执行还不需要考虑,先创建一个接口