Python 数据结构
如果你是大学新生,想学数据结构,最好的语言一定是 Python3
笔记来源 :
Python数据结构与算法第二版(runestone有免费英文版);
老师上课ppt;
学习步骤:
1、Python
2、Algorithm
3、Basic Data Structure
语言基础:Python
you can learn it from
by Python 教程
数据结构
第一讲:DS导论
一、抽象数据类型ADT
1、 通俗比喻:
用户相当于开车的人;
我们码农相当于组装车的人;
(那些零件就是ADT,我们只需要知道怎么用那些零件就好)
最底层的复杂的基础工作:怎么做车零件我们装的人不用知道;
二、 Python需要注意的问题
第二讲:算法分析
Algorithm: ways to solve things.
一个程序的Algorithm好坏决定因素
- 解决问题时要占用的空间或内存
- 执行所需的时间进行分析和比较
一、基准技术计算实际执行时间
由于我们不知道使用哪类函数对算法的运行时间表现进行建模,所以这个做法并没得到有用的度量;所以我们需要找到独立于所使用的程序或者计算机的算法特征。
1、举例函数
1)迭代求和
迭代解决函数:
def sumOfN(n):
theSum=0
for i in range(1,n+1):
theSum=theSum+i
return theSum
2)非迭代求和
def sumOfN3(n)利用了一个封闭的方程
#在不迭代的情况下计算n的总和 return (n*(n+1))/2
ns=np.linspace(10,10_000,100,dtype=int)
ts3=[timeit.timrit('sumOfN3(r)',setup=f'r={
n}',number=1000,globals=globals()) for n in ns]
plt.plot(ns,ts3,'ob')
2、测量技术(时间、内存)
1)测量执行时间:Time 类
ps: 测试占用内存:MemoryMonitor类
例1:对累加问题sumOfN® 计算时间成本
sum运行时间变化的绘图函数:
import matplotlib.pyplot as plt
import numpy as np
import timeit
ns=np.linspace(10,10_000,100,dtype=int)#问题规模
ts=[timeit.timeit('sumOfN(r)',setup=f'r={
n}',number=1000,globals=globals())for n in ns]
plt.plot(ns,ts,'or')#绘制图像:问题规模为横轴,时间成本为纵轴
#ts=[timeit.timeit('sum(r)',setup=f'r=range{n}',number=1000,globals=globals())for n in ns]
例2:sumrange(n)平均斜率得出近似拟合曲线(邻两点之间的平均斜率)
total=0
for i in range(len(ns)-1):
total+=(ts[i+1]-ts[i])/(ns[i+1]-ns[i])# 所有相邻两点之间的斜率之和
avg_slope=total/(len(ns)-1)
plt.plot(ns,ts,'or')
plt.plot(ns,[avg_slope*n for n in ns],'--b']
#运行时间估计
for n in np.linspace(1,100_000_000,11,dtype=int)
print(f'Rumtime of sum(range({
n:>11,})~{
avg_slope*n/100:>5.2f}s')
>>>>>Runtime of sum(range( 1)~0.00s
>>>>>Runtime of sum(range(10,000,000)~0.08s
例3:polyfit为sum(range(n))数据计算任意次数的最佳拟合多项式函数
degree=100
coefffs=np.polyfit(ns,ts,degree)
p=np.ploy1d(coeffs)
pt.plot(ns,ts,'or')
plt.plot(ns,[p(n) for n in ns],'-b')
for n in np.linspace(1,100_000_000,11,dtype=int)
print(f'Rumtime of sum(range({
n:>11,})~{
p(n)/100:2f}s')
>>>>>Runtime of sum(range( 1)~0.00s
>>>>>Runtime of sum(range(10,000,000)~929393014.....s
2) 测量sum(range(n))&sumOfN3(n)执行时间
ns=np.linspace(10,10_000,100,dtype=int)
ts=[timeit.timrit('sum(r)',setup=f'r=range({
n})',number=1000,for n in ns]
ts3=[timeit.timeit('sumOfN3(r)',setup=f'r={
n}',number=1000,globals=globals()) for n in ns]
plt.plot(ns,ts,'or')
plt.plot(ns,ts3,'ob')
二、独立于使用计算机的算法特征
1、n,T(n),S(n)
算法执行时间:可用 解决问题所需步骤数进行表示(T(n)=S(n))
问题规模:n
T(n):解决n的问题所花的时间
S(n):解决n的问题所需的步骤
2、如何计算S(n)
#程序1 S(n)=5
i=1 #1
j=2 #1
temp=i #1
i=j #1
j=temp #1
#程序2
x=0 #1
n=2 #1
for i in range(1,2*n):#⚠️range(a,b)是从a到b-1
x=x+1#2n-1
3、解释S(n)如何随着n的大小而变化
S(n)=O(f(n)) :算法运行时间增长量级的上界
f(n):S(n)中起决定性增长的部分
4、f(n)=S(n)中起决定性增长的部分
1)大O的数量级
ps:常量阶,对数阶,线性阶,线性对数阶,平方阶,多项式阶,指数阶,阶乘阶
Ps: 指数阶与阶乘阶数量级复杂度为难解问题。
2)大O的性质
- 取高阶:O(n^a + n^b) = O(n^a)
- 忽略常系数:O(c*f(n))=O(f(n))
- O(3n^2) =O(n^2) ; O(5)=O(1)
- 更复杂的运算原则:
O(f(n))+O(g(n))=O(max{f(n),g(n)})
O(f(n))*O(g(n))=O(f(n)*g(n))
例(函数):
例(程序):
f(n):S(n)增长最快的部分
T(n)=O(f(n))=O(n^2)**
#程序1 s(n)=3+(n-1)+(n-1)*(zn+1) f(n)=S(n)增长最快的部分
y=0 #1 T(n)=O(f(n))=O(n^2)
x=0 #1
n=3 #1
for i in range (1,n):
x=y+1#(n-1)
for j in range(2*n+1)
x=x+1#(2n+1)
#程序2 s(n)=n^2 T(n)=O(f(n))=O(n^2)
for i in range(n):#n
for j in range(n):#n
M1[i][j]=M1[i][j]+M2[i][j]
3)矩阵相乘的时间复杂度分析
3、增长阶数描述伸缩性
1)最好、最坏、普通情况(经典的异序词检测问题)
算法的性能有时不仅依赖于问题规模,
还依赖于数据值。
经典的异序词检测问题
1、用None替换相同的元素遍历
异序词:一个字符串与另一个字符串所含元素相同,但是排序不同。
S1[0]在S2遍历,与S2中的元素依次比较直到有相同的出现,将S2中的元素等于None.
每个元素每次情况:
- 最好:访问一次就找到了,1
- 最坏:访问到最后才找到,n
- 平均:(1+n)/2
- n个元素:n(1+n)/2
- 时间复杂度:O(n^2)
2、按照字母表顺序给字符排序,异序词得到的结果将是同一个字符串。
Python 中,可以先将字符串转换为列表,然后使用内建的 sort 方法对列表排序。乍看可能只需要遍历一次比较基本字符,但是还需要考虑sort()的代价。
3、因为字符可能有26 种,所以使用 26 个计数器,对应每个字符。每遇到一个字符,就将对应的计数器加 1。
ord()函数:
print(ord(‘5’)) # 53
print(ord(‘A’)) # 65
print(ord(’$’)) # 36
def ang(s1,s2):
c1=[0]*26
c2=[0]*26
for i in range(len(s1)):
pos=ord(s1[i])-ord('a')
print(pos)
c1[pos]=c1[pos]+1
print(c1)
for i in range(len(s2)):
pos=ord(s2[i])-ord('a')
c2[pos]=c2[pos]+1
j=0
Stillok=True
while j<26 and Stillok:
if c1[i]==c2[j]:
j=j+1
print(j)
else:
Stillok=False
return Stillok
ang("za","bdca")
我举了一个很明显的例子:
"za"print(pos)print(c1)
观察结果可以明显看出pos和c1计数器的用途。
不同数量级的算法
展示不同数量级的算法,
2)比较sumofN(n)&sumofN3(n)算法的优劣
3)各Python数据结构效率(大O性能分析)
创建列表的4种方式:
4种方式
#被测试函数
def test1(n):#连接
l=[]
for i in range(n):
l=l+[i]
def test2(n):#追加
l=[]
for i in range(n):
l.append(i)
def test3(n):#列表解析式
l=[i for i in range(n)]
def test4(n):#range函数+列表构造器
l=list(range(n))
比较四种构造列表方式的效率:绘图time类函数
#横轴:问题规模n
ns=np.linspace(10,10_00,100,dtype=int)
#纵轴:解决问题的时间成本
lts1=[timeit.timeit('test1(r)',setup=f'r={
n}',number=100,globals=globals()) for n in ns]
lts2=[timeit.timeit('test2(r)',setup=f'r={
n}',number=100,globals=globals()) for n in ns]
lts3=[timeit.timeit('test3(r)',setup=f'r={
n}',number=100,globals=globals()) for n in ns]
lts4=[timeit.timeit('test4(r)',setup=f'r={
n}',number=100,globals=globals()) for n in ns]
#绘制函数1-4 问题规模n与对应时间成本的函数
plt.plot(ns,lts1,'sb')
plt.plot(ns,lts2,'g')
plt.plot(ns,lts3,'r')
plt.plot(ns,lts4,'K')
列表各种操作的性能分析
POP
#函数
popzero=timeit.Timer("x.pop(0)","from_main_import x")#从列表头pop出,要遍历完列表
popend=timeit.Timer("x.pop()","from_main_import x")#从列表尾pop出,与长度无关
#纵轴:时间成本
popendtime=[]
popzerotime=[]
for i in range(10000,1000001,100):
x=list(range(i))
pop_end_t = pop_end.timeit(number=1000)
popendtime.append(pop_end_t)#插入利用timeit函数计算的时间成本数组
x = list(range(i))
pop_zero_t = pop_zero.timeit(number=1000)
popzerotime.append(pop_zero_t)#插入利用timeit函数计算的时间成本数组
#横轴:
ns=np.linspace(10000,1000001,100,dtype=int)
plt.plot(ns,popendtime,'sb')
plt.plot(ns,popzerotime,'dr')
index(x)查找
#函数
index=timeit.Timer("lst.index(x)","from_main_import lst,x")
#纵轴:时间成本
indextime=[]
for n in range(1000,100001,1000):
lst=list(range(n))
x=n//2
index_t=index.timeit(number=1000)
indextime.append(index_t)#插入利用timeit函数计算的时间成本数组
#横轴:
ns=np.linspace(1000,100000,100,dtype=int)
plt.plot(ns,indextime,'.g',label='index')
plt.lengend()
insert(i,x)
#函数
insert=timeit.Timer("lst.index(i,x)","from_main_import lst1,x")
#纵轴:时间成本
inserttime=[]
for n in range(1000,100001,1000):
lst1=list(range(n))
i=0
x=0
insert_t=insert.timeit(number=1000)
inserttime.append(insert_t)#插入利用timeit函数计算的时间成本数组
#横轴:
ns=np.linspace(1000,100000,100,dtype=int)
plt.plot(ns,insertime,'.b',label='insert')
plt.lengend()
remove(x)
remove=timeit.Timer("lst2.remove(x)","from_main_import lst2,x")
del lst[i]
delete=timeit.Timer("del lst3a[i][0];i=i+1","from_main_import lst3a,i")
sort()
sort=timeit.Timer("lst4.reverse()","from_main_import lst4")
reverse()
reverse=timeit.Timer("lst5.reverse()","from_main_import lst5")
count(x)
count=timeit.Timer("lst6.count(x)","from_main_import lst6,x")
append(x)
append=timeit.Timer("lst7a[i].append(0);i+=","from_main_import lst7a,i")
列表索引赋值操作[ ]
#函数
idexassign=timeit.Timer("lst8[i]=0)","from_main_import lst8,i")
#纵轴:时间成本
idxassigntime=[]
for n in range(10000,1000001,10000):
lst1=list(range(n))
i=n//2
idxa_t=idxassign.timeit(number=1000)
inserttime.append(idxa_t)#插入利用timeit函数计算的时间成本数组
#横轴:
ns=np.linspace(10000,1000001,100,dtype=int)
plt.plot(ns,idxassigntime,'.','color='darkorange',![请添加图片描述](https://img-blog.csdnimg.cn/5e66689e05664f189d11b8b87301facb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA54mn5rO954-j,size_20,color_FFFFFF,t_70,g_se,x_16)
label='insert')
plt.lengend()
字典与列表性能比较
判断随机数在列表和字典是否存在的时间对比
import timeit
import random
#定义 存储时间的数组
lst_timelst=[]
d_timelst=[]
for i in range((10000, 1000001, 20000):
t=timeit.Timer("random.randrange(%d)in x"%i,"from_main_import random,x")
x=list(range(i))#列表构造器
list_time=t.timeit(number=1000)
#列表数组存入所得对应时间成本
lst_timelst.append(list_time)
x={
j:None for j in range(i))
d_time=t.timeit(number=1000)#字典构造
#字典数组存入所得对应时间成本
d_timelst.append(d_time)
#drawing the curve
plt.plot(ns,lst_timelst,'.b',label='list')#列表
plt.plot(ns,d_timelst,'.r',label='dict')#字典
#我没有写输出操作