数据结构
定义
- 官方定义:数据元素不是孤立存在的,它们之间存在着某种关系,数据元素相互之间的关系称为结构(Structure)。
- 简单来说:元素按照什么样的方式组织和保存起来,就叫做数据结构。例如:身份信息可以通过列表,元组,字典的方式存储。列表,元组,字典就是一些Python封装的高级数据结构。
分类
- 逻辑结构(研究对象的特性及其相互之间的关系):
- 划分方法一:
{ ( 1 ) 线 性 结 构 : 只 有 一 个 直 接 前 趋 和 一 个 直 接 后 继 。 例 如 : 线 性 表 、 栈 、 队 列 、 串 ( 2 ) 非 线 性 结 构 : 一 个 结 点 可 能 有 多 个 直 接 前 趋 和 直 接 后 继 。 例 如 : 树 、 图 \begin{cases} (1) 线性结构:\\ 只有一个直接前趋和一个直接后继。 例如: 线性表、栈、队列、串\\ (2) 非线性结构:\\ 一个结点可能有多个直接前趋和直接后继。 例如: 树、图 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧(1)线性结构:只有一个直接前趋和一个直接后继。例如:线性表、栈、队列、串(2)非线性结构:一个结点可能有多个直接前趋和直接后继。例如:树、图
- 划分方式二——四类基本逻辑结构:
{ ( 1 ) 集 合 结 构 : 除 了 同 属 于 一 个 集 合 的 关 系 外 , 无 任 何 其 它 关 系 。 ( 2 ) 线 性 结 构 : 存 在 着 一 对 一 的 线 性 关 系 。 ( 3 ) 树 形 结 构 : 存 在 着 一 对 多 的 层 次 关 系 。 ( 4 ) 图 状 结 构 或 网 状 结 构 : 数 据 元 素 之 间 存 在 着 多 对 多 的 任 意 关 系 。 \begin{cases} (1)集合结构:除了同属于一个集合的关系外,无任何其它关系。\\ (2)线性结构:存在着一对一的线性关系。\\ (3)树形结构:存在着一对多的层次关系。\\ (4)图状结构或网状结构:数据元素之间存在着多对多的任意关系。 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧(1)集合结构:除了同属于一个集合的关系外,无任何其它关系。(2)线性结构:存在着一对一的线性关系。(3)树形结构:存在着一对多的层次关系。(4)图状结构或网状结构:数据元素之间存在着多对多的任意关系。
- 存储结构(有效地组织计算机存储)
{ 顺 序 存 储 : 数 组 一 个 接 着 一 个 链 式 存 储 : 数 据 同 时 存 储 指 针 地 址 索 引 存 储 : 附 加 索 引 表 ( 目 录 ) 散 列 存 储 : 关 键 字 直 接 计 算 \begin{cases} 顺序存储:数组一个接着一个\\ 链式存储:数据同时存储指针地址\\ 索引存储:附加索引表(目录)\\ 散列存储:关键字直接计算 \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧顺序存储:数组一个接着一个链式存储:数据同时存储指针地址索引存储:附加索引表(目录)散列存储:关键字直接计算
算法
定义
求解方法和步骤: 算法就是解决问题的方法和步骤。
为了实现目的,将思想用计算机实现的过程。
算法时间复杂度
- 为了便于比较不同算法的时间效率,我们仅比较它们的数量级,只关注操作数量的最高次项。
(1)渐进函数:
T
(
n
)
=
k
∗
g
(
n
)
+
c
T(n)=k*g(n)+c
T(n)=k∗g(n)+c
含义:k只反映图像的陡峭程度,忽略k,近似的反应时间复杂度
(2)大O表示法:g(n); 忽略其他因素,只关注数量级
(3)最坏时间复杂度(最常用):指在最坏情况下,算法的时间复杂度。不考虑最优时间复杂度和平均时间复杂度
时间复杂度计算基本准则
- 基本准则
- 顺序结构,时间复杂度按加法进行计算
- 循环结构,时间复杂度按乘法进行计算
- 分支结构:时间复杂度取最大值(if,else 哪一个多选哪个)
- 例子
for a in range(0, 1001): #重复1000遍
for b in range(0, 1001): #重复1000遍
for c in range(0, 1001):# 重复1000遍
if a**2 + b**2 == c**2 and a+b+c == 1000: #重复1遍
print("a, b, c: %d, %d, %d" % (a, b, c)) #重复1遍
时间复杂度:
T
(
n
)
=
O
(
n
∗
n
∗
n
)
=
O
(
n
3
)
T(n) = O(n*n*n) = O(n^3)
T(n)=O(n∗n∗n)=O(n3)
常见时间复杂度
执行次数函数举例 | 阶 |
---|---|
12 | O(1) |
2n+3 | O(n) |
3 n 2 n^2 n2+2n+1 | O(n^2) |
5 l o g 2 n log_2n log2n+20 | O(logn) |
2n+ 3 n l o g 2 n 3nlog_2n 3nlog2n+19 | O(nlogn) |
6 n 3 + 2 n 2 n^3+2n^2 n3+2n2+3n+4 | O(n3) |
2 n 2^n 2n | O( 2 n 2^n 2n) |
所消耗的时间从小到大
O
(
1
)
<
O
(
l
o
g
n
)
<
O
(
n
)
<
O
(
n
l
o
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1) <O(logn) <O(n) <O(nlogn) <O(n^2) <O(n^3) <O(2^n) <O(n!) <O(n^n)
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
- ** 列表**
Operation | Big-O Efficiency | comprehension |
---|---|---|
index[] | O(1) | 索引的取值不是从头遍历,而是一步找到 |
index assignment | O(1) | 索引方式赋值,一步 |
append | O(1) | 队尾加,一步 |
pop() | O(1) | 队尾删,一步 |
pop(i) | O(n) | 从指定位置,最坏的可能是第一个,需要遍历整个列表 |
insert(i,item) | O(n) | 同上 |
del operator | O(n) | 清空每一个元素 |
iteration | O(n) | 迭代 |
contains (in) | O(n) | 判断是否存在,要遍历一遍 |
get slice [x:y] | O(k) | 直接定位到x,从x到y中有k个元素 |
del slice | O(n) | 删除中间位置后,将整个后面的往前移 |
set slice | O(n+k) | 切片赋值,先删掉,遍历一遍,再赋值 |
- 字典
Operation | Big-O Efficiency | comprehension |
---|---|---|
copy | O(n) | 将每个元素都复制一遍 |
get item | O(1) | 通过key直接找到 |
set item | O(1) | 通过key直接找到 |
delete item | O(1) | 通过key直接找到 |
contains (in) | O(1) | 通过key直接找到 |
iteration | O(n) | 迭代 |
算法时间测量模块 timeit module
- Timer:是测量小段代码执行速度的类。
class timeit.Timer(stmt=‘pass’, setup=‘pass’,timer=<timer function>)
stmt: 参数是要测试的代码语句(statment);需要传一个字符串
setup:参数是运行代码时需要的设置; 从当前窗口载入
timer: 参数是一个定时器函数,与平台有关,有默认值。不用管。
timeit.Timer.timeit(number=1000000)
Timer类中测试语句执行速度的对象方法。number参数是测试代码时的测试次数,默认为1000000次。方法返回执行代码的平均耗时,一个float类型的秒数。
- 例1: 列表类型不同操作的时间效率
from timeit import Timer #从timeit模块中载入Timer
def t1():
li=[]
for i in range(10000):
li.append(i)
def t2():
li = []
for i in range(10000):
li+=[i]
def t3():
li = [i for i in range(10000)] #列表生成器
def t4():
li=list(range(10000))
def t5():
li=[]
for i in range(10000):
li.extend(10000)
#从当前窗口载入,窗口名称默认为是__mian__
timer1= Timer("t1","from __main__ import t1") # __main__前后都有空格
print("append:", timer1.timeit(1000))
timer2= Timer("t2","from __main__ import t2") ,
print("+:",timer2.timeit(1000))
timer3= Timer("t3","from __main__ import t3")
print("i for i in range:",timer3.timeit(1000))
timer4= Timer("t4","from __main__ import t4")
print("list(range()):",timer4.timeit(1000))
timer5= Timer("t5","from __main__ import t5")
print("list(range()):",timer5.timeit(1000))
--------------------------------------
append: 4.935400000000145e-05
+: 1.8091999999997332e-05
i for i in range: 1.8264000000003944e-05
list(range()): 1.7505000000000992e-05
list(range()): 2.4399000000008275e-05
疑难解答
Q1: 为什么不用时间表示时间效率?
算然对于不同的机器环境而言,确切的单位时间是不同的,但是对于算法进行多少个基本操作(即花费多少时间单位)在规模数量级上却是相同的,由此可以忽略机器环境的影响而客观的反应算法的时间效率。
Q2:N是什么?
代表问题的基本规模
例如:
# 迭代1000次和迭代2000次在本质上只是循环重复的次数不同,在计算时间复杂度时,引入N代表1000,2000或更多,使之有一种统一的表示
for a in range(0, 1001): # 重复1000次
for a in range(0, 2001): # 重复2000次
for a in range(0, n): # 重复n次