第一章
二分查找
- 输入:一个有序的元素列表
- 过程:要查找的元素包含在列表中,二分查找返回其位置,否则返回NULL
- 对于包含n个元素的列表,用二分查找最多需要步,简单查找最多需要步
- 算法实现:
def binary_search(list,item):
low=0
high=len(list)-1
while low<=high:
mid=(low+high)/2 #向下取整
guess=list[mid]
if guess=item:
reutrn mid
if guess>item:
high=mid-1
else:
low=mid+1
return None #None 表示空,意味着没有找到指定元素
- 运行时间为对数时间(O())
- 最多需要猜测的次数与列表长度相同,这被称为线性时间(linear time)
- O(1)代表常量时间
大O表示法
- 大O表示法指出了算法运行时间的增速,指出了最糟情况下的运行时间
- 常见的大O运行时间
- :对数时间,比线性时间快,例如二分查找
- :线性时间,例如简单查找
- :例如快速排序
- :例如选择排序
- :例如旅行商问题
- 算法的速度指的并非时间,而是操作数的增速。
- 谈论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
- 算法的运行时间用大O表示法表示。
第二章
需要存储多项数据时,有两种基本方式-数组和链表
- 数组
- 添加元素慢且麻烦,有时可能会浪费内存
- 读取简单,地址连续
- 访问方式
- 顺序访问:从第一个元素开始逐个读取元素
- 随机访问:
- 同一个数组中,所有元素的类型必须相同(int或double)
- 链表
- 元素可存储在内存的任何地方
- 链表的每个元素都存储下一个元素的地址,从而使一系列随机的内存地址串在一起。
- 不需要移动元素,只要有足够内存空间,就能为链表分配内存
- 只能顺序访问
- 数组和链表比较
- 数组:读取O(1) 插入O(n)删除O(n)
- 链表:读取O(n) 插入O(1)删除O(1)(当能够立即访问要删除的元素时)
快速排序
- 运行时间
- 算法示例:
def findSmallest(arr):
smallest=arr[0]
smallest_index=0
for i in range(1,len(arr)):
if arr[i]<smallest:
smallest=arr[i]
smallest_index=i
return smallest_index
def selectinSort(arr):
newArr=[]
for i in range(len(arr)):
smallest=findSmallest(arr)#找出数组中最小的元素,降入新数组
newArr.append(arr.pop(smallest))
return newArr
第三章
递归
- 调用自己的函数
- 作用:让解决方案更清晰,并没有性能上的优势
- 循环使程序的性能更高,递归使程序更容易理解
- 为了避免死循环,编写递归函数,必须告诉它何时停止递归
- 递归函数包含两个部分:
- 基线条件:函数不再调用自己,避免死循环
- 递归条件:函数调用自己
栈
- 后进先出
- 两种操作:
- 压入(插入)
- 弹出(删除并读取)
- 所有函数调用都进入调用栈
- 调用栈可能很长,将占用大量的内存
第四章
分而治之(D&C)
- 解决问题:
- (1)找出基线条件,这种条件必须尽可能简单。
- (2)不断将问题分解(或者说缩小规模),直到符合基线条件。
快速排序
- 流程:
- (1)选择基准值。
- (2)将数组分成两个子数组:小于基准值的元素和大于基准值的元素。
- (3)对这两个子数组进行快速排序。
- 算法实现
def quicksort(array):
if len(array)<2: #基线条件,为空或只含一个元素的数组
return array
else:
pivot=array[0] #递归条件
less=[i for i in array[1:] if i <= pivot]#由所有小于基准值的元素组成的子数组
greater=[i for i in array[1:] if i >pivot]#由所有大于基准值的元素组成的子数组
return quicksort(less)+[pivot]+quicksort(greater)
print quicksort([10,5,2,3])
- 最慢运行时间 ,平均运行时间
第五章
散列表和散列函数
-
散列表是无序的,将输入映射到数字
-
散列函数总是将同样的输入映射到相同的索引。
-
散列函数将不同的输入映射到不同的索引。但常常会有冲突
-
散列函数知道数组有多大,只返回有效的索引。
-
Python提供的散列表实现为字典,你可使用函数dict来创建散列表。
book=dict() #或book={}
book["apple"]=3
- 散列表由键和值组成,散列表将键映射到值
- 应用:用于查找 例如DNS解析,将网址映射为IP地址
用于防止重复;用于缓存数据 - 处理冲突的简单办法:如果两个键映射到了同一个位置,就在这个位置存储一个链表。
- 为了避免冲突,需要较低的填装因子,良好的散列函数
- 填装因子:散列表包含元素数/位置总数。填装因子越低,发生冲突可能性越小,性能越高。散列表适合用于模拟映射关系
- 良好的散列函数让数组中的值呈均匀分布
- 为了避免冲突,需要较低的填装因子,良好的散列函数
- 性能:查找,插入,删除的平均情况是O(1),最糟情况是O(n)
第六章 广度优先搜索
广度优先搜索
解决最短路径问题的算法被称为广度优先搜索
- 广度优先搜索是用于图的查找算法,帮助解决两类问题:
- 有无路径
- 路径最短
- 图由节点和边组成
- 有向图
- 无向图:没有箭头,直接相连的节点互为邻居
- 运行时间:O(V+E),V为顶点数,E为边数
队列
- 先进先出(FIFO)
- 操作:入队和出队
第七章 狄克斯特拉算法
- 找出加权图中前往x的权重最小路径
- 该算法只适用于有向无环图,对负权边不适用(可用贝尔曼-福德算法)
- 于每条边都有关联数字的图,这些数字称为权重
- 带权重的图称为加权图(weighted graph),不带权重的图称为非加权图(unweighted graph)
- 无向图意味着两个节点彼此指向对方,其实就是环
注:python中表示无穷大infinity=float(“inf”)
def find_lowest_cost_node(costs):
lowest_cost=float("inf")
lowest_cost_node=None
for node in costs:#遍历所有节点
cost=costs[node]
if cost<lowest_cost and node not in processed:
lowest_cost=cost#视为开销最低节点
lowest_cost_node=node
return lowest_cost_node
node=find_lowest_cost_node(costs)#在未处理的节点中找出开销最小的节点
while node is not None:
cost=costs[node]
neighbors=graph[node]
for n in neighbors.keys():
new_cost=cost+neighbors[n]
if costs[n] > new_cost:#如果经当前节点前往该邻居更近,
costs[n]=new_cost#就更新该邻居的开销
parents[n]=node#同时将该邻居的父节点设置为当前节点
processed.append(node)#将当前节点标记为处理过
node=find_lowest_cost_node(costs)
第八章 贪婪算法
- 优点:简单易行,每步都采取最优的做法,得到的就是全局最优解
- 常见问题:背包问题,集合覆盖问题
- 贪婪算法寻找局部最优解,企图以这种方式获得全局最优解。
NP完全问题
-
旅行商问题和集合覆盖问题有一些共同之处:你需要计算所有的解,并从中选出最小/最短的那个。这两个问题都属于NP完全问题。
-
要判断问题是不是NP完全问题很难,易于解决的问题和NP完全问题的差别通常很小
- 元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢。涉及“所有组合”的问题通常是NP完全问题。
- 不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题。
- 如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题。
- 如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题。
- 如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题。
-
NP完全问题,还没有找到快速解决方案。
-
面临NP完全问题时,最佳的做法是使用近似算法。贪婪算法易于实现、运行速度快,是不错的近似算法。
近似算法
- 优劣标准:
- 速度有多快
- 得到的近似解与最优解的接近程度
注:集合类似于列表,但不能包含重复的元素。可以做交并差集运算
第九章 动态规划
问题:背包问题
-
动态规划先解决子问题,再逐步解决大问题
-
动态规划可帮助你在给定约束条件下找到最优解。在背包问题中,你必须在背包容量给定的情况下,偷到价值最高的商品。
-
在问题可分解为彼此独立且离散的子问题时,就可使用动态规划来解决。
-
小建议:
- 每种动态规划解决方案都涉及网格。
- 元格中的值通常就是你要优化的值。在前面的背包问题中,单元格的值为商品的价值。
- 每个单元格都是一个子问题,因此你应考虑如何将问题分成子问题,这有助于你找出网格的坐标轴。
-
最长公共子串
-
最长公共序列
-
需要在给定约束条件下优化某种指标时,动态规划很有用。
-
问题可分解为离散子问题时,可使用动态规划来解决。
-
每种动态规划解决方案都涉及网格。单元格中的值通常就是你要优化的值。
-
每个单元格都是一个子问题,因此你需要考虑如何将问题分解为子问题。
-
没有放之四海皆准的计算动态规划解决方案的公式。
第十章 K最近邻法(KNN)
案例:创建推荐系统
- 计算两点距离,可用毕达哥拉斯公式,距离指出了两组数字之间的相似程度
- 还有计算方式是余弦相似度,比较的是角度
- 利用KNN来做分类和回归,分类就是编组,回归就是预测结果
- 特征抽取意味着将物品(如水果或用户)转换为一系列可比较的数字。能否挑选合适的特征事关KNN算法的成败。
案例:机器学习简介
- OCR
- OCR的第一步是查看大量的数字图像并提取特征,这被称为训练(training)。大多数机器学习算法都包含训练的步骤:要让计算机完成任务,必须先训练它
- 创建垃圾邮件过滤器
- 算法:朴素贝叶斯分类器
第十一章 接下来如何做
-
树
- 二叉查找树
- 特点:对于每个节点,左子节点值都比它小,右子节点的值都比它大
- 在二叉查找树查找节点时,平均运行时间为 ,最糟为
- 执行插入和删除操作速度快很多
- 对数据库或高级数据结构感兴趣,请研究如下数据结构:B树,红黑树,堆,伸展树。
- 二叉查找树
-
反向索引
- 常用于创建搜索引擎
-
傅里叶变换
- 适合处理信号,也可以用来压缩音乐,地震预测,DNA分析
-
并行运算
-
MapReduce
- 分布式运算提升速度和性能,适合短时间完成海量工作
- 基于映射map函数和归并reduce函数
- map函数:将一个数组转换为另一个数组
- reduce函数:将很多项归并为一项
#map
arr1=[1,2,3,4,5]
arr2=map(lambda x:2*x,arr1) #[2,3,6,8,10]
#reduce
arr1=[1,2,3,4,5]
reduce(lambda x,y:x+y,arr1)#15
- 布隆过滤器和HyperLogLog
- SHA算法
- 一种散列函数,安全散列算法(SHA),给定一个字符串,SHA返回其散列值(较短的字符串)
- 可用SHA判断两个文件是否相同
- 检查密码:存储的都是密码的SHA散列值,由于无法根据散列值推断出原始字符串
- 希望散列函数是局部敏感的,可使用Simhash
- Diffie-Hellman密钥交换
- 使用两个密钥:公钥和私钥,加密的信息只有使用私钥才能解密
- 线性规划
- 线性规划用于在给定约束条件下最大限度地改善指定的指标
来源:CSDN
作者:史迪仔and三眼仔
链接:https://blog.csdn.net/qq_33808544/article/details/104640122