本文为学习笔记,感兴趣的读者可在MOOC中搜索《数据结构与算法Python版》或阅读《数据结构(C语言版)》(严蔚敏)
目录链接:https://blog.csdn.net/floating_heart/article/details/123991211
0 前言
0.1 概述:数据时代
0.2 问题求解的计算之道
问题解决的计算之道:
有些问题已经解决
很多问题尚未解决
有些问题似乎无法完全解决
❖尚未解决和无法解决问题的共性
表述含混、标准不一、涉及主观、结果不确定(主观、价值观、意识形态、哲学问题等)
Mathematics is the alphabet in which God has written the universe.
——Galileo, Italian astronomer, mathematician and philosopher(1564-1642)
抽象的“计算”概念提出:
❖基于有穷观点的能行方法
由有限数量的明确有限指令构成;
指令执行在有限步骤后终止;
指令每次执行都总能得到唯一结果;
原则上可以由人单独采用纸笔完成,而不依靠其它辅助;
每条指令可以机械地被精确执行,而不需要智慧和灵感。
0.3 图灵机计算模型
图灵机Turing Machine基本概念:
❖在纸上写上或擦除某个符号;
❖把注意力从纸的一个位置转向另一个位置
❖在每个阶段,要决定下一步动作依赖于:
(a)此人当前所关注的纸上某个位置的符号和
(b)此人当前思维的状态。
0.4算法和计算复杂性
“基于有穷观点的能行方法”
1)算法
世界上最早的算法:欧几里得算法(辗转相除法求最大公约数)
2)计算复杂性
3)不可计算问题
- 停机问题:判定任何一个程序在任何一个输入情况下是否能够停机;
- 不可计算数:几乎所有的无理数,都无法通过算法来确定其任意一位是什么数字(PI、e是可以确定的)
似乎计算之道解决问题存在边界和极限?
0.5突破计算极限
1)超大规模分布式计算
seti@home BOINC
2)新型计算技术:光子计算/DNA计算/量子计算
3)分布式智慧——众包
Foldit:游戏化众包蛋白质结构分析
more
0.6什么是抽象和实现
计算机科学主要研究的是问题、问题解决过程,以及问题的解决方案
编程是通过一种程序设计语言,将抽象的算法实现为计算机可以执行的代码的过程。
算法+数据结构=程序 ——Niklaus Wirth(图灵奖得主)
程序设计语言需要为算法的实现提供实现“过程”和“数据”的机制。
具体表现为:
“控制结构”和“数据类型”
0.7什么是数据结构
数据结构 = 结构定义 + 结构操作
0.8为什么研究数据结构与算法
- 清晰高效地表达算法
- 数据抽象:ADT抽象数据类型
- 数据结构是对ADT的具体实现
- ADT实现:数据结构Data Structure
- 接口的两端:抽象与实现——抽象数据类型可以有多种实现方案;独立于实现的数据模型;通过层层抽象,降低问题解决过程的复杂度
WHY
首先,学习各种不同问题的解决方案。
有助于我们在面对未知问题的时候,能够根据类似问题的解决方案来更好解决。
其次,各种算法通常有较大差异。我们可以通过算法分析技术来评判算法本身特性而不仅仅根据实现算法的程序在特定机器和特定数据上运行的表现来评判它;即使同一个程序,在不同的运行环境和输入数据的情况下,其表现的差异可能也会很大。
在某些情况下,当我们碰到棘手的难题。得能区分这种问题是根本不存在算法还是能找到算法,但需要耗费大量的资源
某些问题解决需要一些折衷的处理方式。我们需要学会在不同算法之间进行选择,以适合当前条件的要求。
算法及复杂度分析
0.9 什么是算法分析
运行时间:
import time
start = time.time()
end = time.time()
0.10 大O表示法
算法分析的目标是要找出问题规模会怎么影响一个算法的执行时间
问题规模:影响算法执行时间的主要因素
算法时间度量指标:赋值语句的执行次数
描述方法:数量级函数Order of Magnitude
数量级函数描述了T(n)中随着n增加而增加速度最快的主导部分
称作“大O”表示法,记作O(f(n)),其中f(n)表示T(n)中的主导部分
eg.
- T(n) = 1 + n -> O(n)
- T(n) = 5n2 + 27n + 1005 -> O(n2)
**影响算法运行时间的其他因素:**具体的数据
某些具体数据也会影响算法运行时间
分为最好、最差和平均情况,平均状况体现了算法的主流性能
对算法的分析要看主流,而不被某几种特定的运行状况所迷惑
常见的大O数量级函数:
f(n) | 名称 |
---|---|
1 | 常数 |
log(n) | 对数 |
n | 线性 |
n*log(n) | 对数线性 |
n2 | 平方 |
n3 | 立方 |
2n | 指数 |
其他算法复杂度表示法:
大O表示法:
表示了所有上限中最小的那个上限。
大𝛀表示法:
表示了所有下限中最大的那个下限
大𝚹表示法:
如果上下限相同,那么就可以用大𝚹表示
0.11 “变位词”判断问题(上)
问题描述:
所谓“变位词”是指两个词之间存在组成字母的重新排列关系
如:heart和earth,python和typhon
为了简单起见,假设参与判断的两个词仅由小写字母构成,而且长度相等
解题目标:写一个bool函数,以两个词作为参数,返回这两个词是否变位词
(可以很好展示同一问题的不同数量级算法)
解法1:逐字检查
解法思路:
将词1中的字符逐个到词2中检查是否存在存在就“打勾”标记(防止重复检查)
如果每个字符都能找到,则两个词是变位词
只要有1个字符找不到,就不是变位词
程序技巧:
实现“打勾”标记:将词2对应字符设为None
由于字符串是不可变类型,需要先复制到列表中
代码:
def anagranSolution1(s1,s2):
alist = list(s2)
pos1 = 0
stillOK = True
while pos1 < len(s1) and stillOK:
pos2 = 0
found = False
while pos2 < len(s1) and not found:
if s1[pos1] == alist[pos2]:
found = True
else:
pos2 = pos2 + 1
if found:
alist[pos1] = None
else:
stillOK = False
return stillOK
算法分析:
总执行次数:1+2+3+…+n
数量级:O(n2)
∑
i
=
1
n
i
=
n
(
n
+
1
)
2
=
1
2
n
2
+
1
2
n
—
>
O
(
n
2
)
\sum_{i=1}^{n}{i=\frac{n(n+1)}{2}=\frac{1}{2}n^2+\frac{1}{2}n—>O(n^2)}
i=1∑ni=2n(n+1)=21n2+21n—>O(n2)
解法2:排序比较:
解题思路:
将两个字符串都按照字母顺序排好序
再逐个字符对比是否相同,如果相同则是变位词
有任何不同就不是变位词
代码:
def anagramSolution2(s1,s2):
alist1 = list(s1)
alist2 = list(s2)
alist1.sort()
alist2.sort()
pos = 0
matches = True
while pos < len(s1) and matches:
if alist1[pos] == alist2[pos]:
pos += 1
else:
matches = False
return matches
算法分析:
总执行次数:两个sort() + 一个循环
数量级:O(n log(n))
0.12 “变位词”判断问题(下)
解法3:暴力法
解题思路:
穷尽所有可能组合。
将s1中出现的字符进行全排列,再查看s2是否出现在全排列列表中。
算法分析:
总执行次数:n!
结论:恐怕不是一个好算法。
解法4:计数比较
解题思路:
对比两个词中每个字母出现的次数,如果26个字母出现的次数都相同的话,这两个字符串就一定是变位词。
具体做法:
为每个词设置一个26位的计数器,先检查每个词,在计数器中设定好每个字母出现的次数;
计数完成后,进入比较阶段,看两个字符串的计数器是否相同,如果相同则输出是变位词的结论;
代码:
def anagramSolution4(s1,s2):
c1 = [0] * 26
c2 = [0] * 26
for i in range(len(s1)):
pos = ord(s1[i]) - ord('a')
c1[pos] += 1
for i in range(len(s2)):
pos = ord(s2[i]) - ord('a')
c2[pos] += 1
j = 0
stillOK = True
while j < 26 and stillOK:
if c1[j] == c2[j]:
j += 1
else:
stillOK = False
return stillOK
算法分析:
总循环次数:3个循环迭代 T(n) = 2n + 26
数量级:O(n)
注意:本算法依赖于两个长度为26的计数器列表,相比前几个算法需要更多的存储空间(时间和空间之间的取舍)
0.13 Python数据类型的性能
对比list和dict的操作:
类型 | list | dict |
---|---|---|
索引 | 自然数i | 不可变类型值key |
添加 | append、extend、insert | b[k]=v |
删除 | pop、remove* | pop |
更新 | a[i]=v | b[k]=v |
正查 | a[i]、a[i:j] | b[k]、copy |
反查 | index(v)、count(v) | 无 |
其它 | reverse、sort | has_key、update |
List列表数据类型:
总的方案:让最常用的操作性能最好,牺牲不太常用的操作。
(80/20准则:80%的功能其使用率只有20%)
dict数据类型:
Python官方的算法复杂度网站:
https://wiki.python.org/moin/TimeComplexity