数据结构与算法
引子
如果a+b+c=1000,且a^2+b^2=c^2(a,b,c为自然数),如何求出a,b,c可能的组合。
import time
start_time = time.time()
for a in range(0,1001):
for b in range(0,1001):
for c in range(1,1001):
if a+b+c == 1000 and a**2 +b**2 == c**2:
print('a,b,c:%d,%d,%d' %(a,b,c))
end_time = time.time()
print('time:{}'.format(end_time-start_time))
print('finished')
算法的概念
算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后调用。
算法是独立存在的一种解决问题的方法和思想。
对于算法而言,实现的语言并不重要,重要的是思想。
算法可以有不同的语言描述现实现版本(如C描述,C++描述,Python描述等)。
算法的五大特性
- 输入:算法具有0个或多个输入
- 输出:算法至少有1个或多个输出
- 有穷性:算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间完成。
- 确定性:每一步有确定的含义,不会出现二义性
- 可行性:每一步都是可行的,也就是说每一个都能够执行有限的次数完成。
时间复杂度与大O记法
前面的问题如果改成如下程序,运行的时间会减少很多
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:%d,%d,%d' %(a,b,c))
end_time = time.time()
print('time:{}'.format(end_time-start_time))
print('finished')
“大O记法”:对于单调的整数函数f,如果存在一个整数函数g和实常数c>0,使得对于充分大的n总有f(n)<=c*g(n),就说函数g是f的一个渐近函数(忽略常数),记为f(n)=O(g(n))。也就是说,在趋于无穷的极限意义下,函数的增长速度收到g(n)限制,亦即函数f与函数g的特征相似。
时间复杂度:假设存在函数g,使得算法A处理规模为n的问题示例所用的时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐近时间复杂度,简称时间复杂度,记为T(n)
如何理解大O
对于算法进行特别具体的细致分析虽然很好,但在实践中的实际价值有限。对于算法的时间性质和空间性质,最重要的是其数量级和趋势,这些是分析算法效率的主要部分。而计量算法基本操作数量的规模函数中那些常量因子可以忽略不计。例如,可以认为3n^2和100n^2属于同一量级,如果两个算法处理同样规模的实例的代价分别为这两个函数,就认为它们的效率“差不多”,都为n^2级。
最坏时间复杂度
分析算法时,存在几种可能的考虑:
- 算法完成工作最少需要多少基本操作,即最优时间复杂度。
- 算法完成工作最多需要多少基本操作,即最坏时间复杂度。
- 算法完成工作平均需要多少基本操作,即平均时间复杂度。
对于最优的时间复杂度,价值不大,因为它没有提供什么有用的信息。
对于最坏时间复杂度,提供了一种保证。
一般只关注最坏时间复杂度。
时间复杂度的几条基本计算规则
- 基本操作,即只有常数项,认为其时间复杂度为O(1)
- 顺序结构,时间复杂度按加法进行计算
- 循环结构,时间复杂度按乘法进行计算
- 分支结构,时间复杂度取最大值
- 判断一个算法的效率时,往往只需要关注操作数量的最高次项,其他次要项和常数项可以忽略。
- 在没有特殊说明的情况下,我们分析的算法复杂度都是指最坏时间复杂度。
常见的时间复杂度
执行次数函数举例 | 阶 | 非正式叫法 |
---|---|---|
12 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
3n^2+2n+1 | O(n^2) | 平方阶 |
5log2n+20 | O(logn) | 对数阶 |
2n+3nlog2n+19 | O(nlogn) | nlogn阶 |
6n^3+2n+1 | O(n^3) | 立方阶 |
2^n | O(2^n ) | 指数阶 |
所消耗的时间从小到大
O
(
1
)
<
O
(
l
o
n
g
n
)
<
O
(
n
)
<
O
(
n
l
o
n
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O\left(1\right)<O\left(longn\right)<O\left(n\right)<O\left(nlongn\right)<O\left(n^2\right)<O\left(n^3\right)<O\left(2^n\right)<O\left(n!\right)<O\left(n^n\right)
O(1)<O(longn)<O(n)<O(nlongn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
注意经常将以2为底的
log
2
n
\log_2n
log2n写成
l
o
g
n
logn
logn
timeit模块
timeit模块用来测试一小段代码运行
class timeit.Timer(stmt = ‘pass’,setup=‘pass’,timer=<timer function>)
Timer是测量小段代码执行速度的类。
stmt参数是要测试的代码语句
setup参数是运行代码时需要的设置
timer参数一个定时器函数,与平台有关
timer.Timer.timeit(number=1000000)
Timer类中测试语句执行速度的对象方法。number参数是测试代码时的测试次数,默认为1000000次。方法返回执行代码的平均耗时,一个float类型的秒数。
def test1():
li = []
for i in range(10000):
li.append(i)
def test2():
li = []
for i in range(10000):
li+=[i]
def test3():
li = [i for i in range(100000)]
def test4():
li = list(range(100000))
timer1 = Timer("test1()","from __main__ import test1")
print('+:',timer1.timeit(1000))
timer2 = Timer("test2()","from __main__ import test1")
print('+:',timer1.timeit(1000))
timer3 = Timer("test3()","from __main__ import test1")
print('+:',timer1.timeit(1000))
timer4 = Timer("test4()","from __main__ import test1")
print('+:',timer1.timeit(1000))
list内置操作的时间复杂度
list的操作 | 时间复杂度 |
---|---|
index | O(1) |
index assignment | O(1) |
append | O(1) |
pop() | O(1) |
pop(i) | O(n) |
insert() | O(n) |
del operator | O(n) |
iteration | O(n) |
contains(in) | O(n) |
get slice | O(n) |
set slice | O(n+k) |
reversed | O(n) |
concatenate | O(n) |
sort | O(nlogn) |
multiply | O(nk) |
字典的时间复杂度
dict操作 | 时间复杂度 |
---|---|
copy | O(n) |
get item | O(1) |
set item | O(1) |
contains(in) | O(1) |
iteration | O(n) |
数据结构引入
数据时一个抽象的概念,将其进行分类后得到的程序设计语言中的基本类型。如:int,float,char等。数据元素之间不是独立的,存在特定的关系,这些关系便是结构。数据结构指数据对象中数据元素之间的关系。
Python给我们提供了很多线程的数据结构,这些系统自己定义好的,不需要我们自己定义的数据结构叫做Python的内置数据结构,比如列表、元组、字典。而有些数据组织方式Python系统里面没有直接定义,需要我们自己取定义这些数据的组织方式,这些数据组织方式称之为Python的扩展数据据结构,比如栈,队列等。
程序 数据结构+算法
总结:算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题载体。
抽象数据类型(ADT)的含义是指一个数据模型以及定义在此数学模型上的一组操作。即把数据类型和数据类型上的运算捆在一起,进行封装。引入抽象数据类型的目的是把数据类型的表示和数据类型上运算的实现与这些数据类型和运算再程序中的引用隔开,使他们相互独立。
最常用的数据运算有五种:
- 插入
- 删除
- 修改
- 查找
- 排序