快速排序是一种常用的排序,比选择排序。
基线条件为:数组为空或只包含一个元素,这种情况下根本不用排序。
pythondef quicksort(array):
if len(array) <2:
return array
基准值:将任意一个元素作为基准值
检查第一个元素是否比第二个元素小,如果不比第二个小,就变换他们的位置
要使用D&C(分而治之),需要将数组分解,直到满足基线条件。下面介绍快速排序的工作原理:
分为三个区(两个子数组是无序的):
归纳证明
归纳证明是一种证明算法行之有效的方式,它分为两步:基线条件和归纳条件
是不是有点似曾相识的感觉?例如,假设我要证明我能爬到梯子的最上面。
递归条件是这样的:如果我站在一个横档上,就能将脚放到下一个横档上。换言之,如果站在第二个横档上,就能爬到第三个横档。 这就是归纳条件。
而基线条件是这样的,即我已经站在第一个横档上。因此,通过每次爬一个横档,我就能爬到梯子最顶端
对于快速排序,可使用类似的推理。
在基线条件中,我证明这种算法对空数组或包含一个元素的数组管用。在归纳条件中,我证明如果快速排序对包含一个元素的数组管用,对包含两个元素的数组也将管用;如果它对包含两个元素的数组管用,对包含三个元素的数组也将管用,以此类推。
因此,我可以说,快速排序对任何长度的数组都管用。
快速排序的代码
python# coding:utf-8
def quicksort(li):
if len(li)<2:
return li
else:
pivot = li[0]
less = [i for i in li[1:] if i <= pivot]
greater = [i for i in li[1:] if i > pivot]
return quicksort(less)+[pivot]+quicksort(greater)
print(quicksort([10,5,3,6,9]))
快速排序的独特之处在于,其速度取决于选择的基准值。
大O的表示法,分平均情况和最糟的情况,像快速排序平均的情况是nlogn,最糟的是n方
题目练习: 使用大O表示法时,下面各种操作都需要多长时间? 4.5 打印数组中每个元素的值。 o(n) 4.6 将数组中每个元素的值都乘以2。 o(n) 4.7 只将数组中第一个元素的值乘以2。 o(1) 4.8 根据数组包含的元素创建一个乘法表,即如果数组为[2, 3, 7, 8, 10],首先将每个元素 都乘以2,再将每个元素都乘以3,然后将每个元素都乘以7,以此类推。 o(n方)
##总结
目标:
散列函数是这样的函数,即无论给它什么数据,它都还你一个数字。如果用专业术语来表达,散列函数是“将输入映射到数字”
要满足的要求:
结合散列函数和数组创建了一种被称为散列表的数据结构。数组和链表都被直接映射到内存,但散列表更复杂,它使用散列函数来确定元素的位置。
python提供的散列表实现为——字典,你可以使用函数dict来创建散列表
63页 练习 对于同样的输入,散列表必须返回同样的输出,这一点很重要。如果不是这样的,就无法找到你在散列表中添加的元素! 请问下面哪些散列函数是一致的? 5.1 f(x) = 1 一致 不是理想的情况,但是属于散列函数 5.2 f(x) = rand() 不一致 输入一样的时候,输出必须一样 5.3 f(x) = next_empty_slot()-返回散列表中下一个空位置的索引 不一致 还不了数字 5.4 f(x) = len(x) 一致
应用于查找
防止重复
用作缓存
缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中!
仅当URL不在缓存中时,你才让服务器做些处理,并将处理生成的数据存储到缓存中,再返 回它。这样,当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进 行处理了。
总结
冲突:给两个键分配的位置相同。
冲突很糟糕,必须要避免,不然就会出现输入与输出的不匹配
处理冲突的最简单的方法:如果两个键映射到了同一个位置,就再这个位置存储一个链表,但是这种情况会影响到散列表的查询速度
这里有两个经验教训:
在平均情况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除速 度与链表一样快,因此它兼具两者的优点!
但在最糟情况下,散列表的各种操作的速度都很慢。 因此,在使用散列表时,避开最糟情况至关重要。为此,需要避免冲突。而要避免冲突,需要有:
较低的填装因子(数组中被占用的位置数。例如:散列表中有5个位置,只有2个被占用,因子为0.4) 一旦填装因子大于1,就超出了数组的位置,这个时候就需要调整长度,调整长度是需要很长的工作时间的,一旦超过0.7就该调整列表的长度
良好的散列函数 良好的散列函数让数组中的值呈均匀分布,糟糕的散列函数让值扎堆,导致大量的冲突
目标
广度优先搜索让你能够找出两样东西之间的最短距离,使用广度优先搜索可以:
图是什么?
图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为令居。图用于模拟不同的东西是如何相连的。
广度优先搜索是一种用于图的查找算法,可帮助回答两类问题:
假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。在Facebook,你与 芒果销售商有联系吗?为此,你可在朋友中查找。
这种查找很简单。首先,创建一个朋友名单。
然后,依次检查名单中的每个人,看看他是否是芒果销售商。
假设你没有朋友是芒果销售商,那么你就必须在朋友的朋友中查找。
检查名单中的每个人时,你都将其朋友加入名单。
这样一来,你不仅在朋友中查找,还在朋友的朋友中查找。别忘了,你的目标是在你的人际 关系网中找到一位芒果销售商。因此,如果Alice不是芒果销售商,就将其朋友也加入到名单中。 这意味着你将在她的朋友、朋友的朋友等中查找。使用这种算法将搜遍你的整个人际关系网,直 到找到芒果销售商。这就是广度优先搜索算法。
谁是关系最近的芒果销售商。例如,朋友是一度关系,朋友的朋友是二度关系
你按顺序依次检查名单中的每个人,看看他是否是芒果销售商。这将先在一度关系中查找, 再在二度关系中查找,因此找到的是关系最近的芒果销售商。广度优先搜索不仅查找从A到B的 路径,而且找到的是最短的路径。
注意,只有按添加顺序查找时,才能实现这样的目的。
队列只支持两种操作:入队和出队。
如果你将两个素加入队列,先加入的元素将在后加入的元素之前出队。因此,你可使用队 列来表示查找名单!这样,先加入的人将先出队并先被检查。——保证了添加顺序
表示这种映射关系的python代码如下:
pythongraph = {}
graph["you"] = ["alice", "bob", "claire"]
注意:“你”被映射到了一个数组,因此graph[“you”]是一个数组,其中包含了“你”的所有邻居
表示图的代码:
pythongraph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []
首选,创建一个队列。在python中,可以使用函数deque来创建一个双端队列
pythonfrom collection import deque
search_queue = deque()
search_queue+=graph['you']
其次,将你的这些邻居加入到搜索队列中
pythonwhile search_queue: # 只要队列不为空
person = search_queue(person) # 就取出其中的第一个人
if person_is_seller(person): # 检查这个人是否是芒果商
print(person+"is a mango seller") # 是芒果销售商
return True
else:
search_queue+=graph[person] # 不是芒果商,将找个人的朋友都加入搜索队列
def person_is_seller(name):
return name[-1] == 'm'
算法的结束条件:
优化:检查一个人之前,要确认之前没检查过他
python# coding:utf-8
from collections import deque
def search(name):
search_queue = deque()
search_queue += graph[name]
print(search_queue)
searched = []
while search_queue:
person = search_queue.popleft()
if not person in searched:
if person_is_seller(person):
print(person + " is a mango seller!" )
return True
else:
search_queue += graph[person]
searched.append(person)
return False
def person_is_seller(name):
return name[-1] == 'm'
graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []
search("you")
如果你在你的整个人际关系网中搜索芒果销售商,就意味着你将沿每条边前行(记住,边是 从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。
你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的, 即为O(1),因此对每个人都这样做需要的总时间为O(人数)。所以,广度优先搜索的运行时间为 O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。
从某种程度上说,这种列表是有序的。如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序,使用它可根据图创建一个有序列表。
假设你正在规划一场婚礼,并有一个很大的图,其中充斥着需要做的事情,但却不知道要从哪里开始。这时就可使用拓扑排序来创建一个有序的任务列表。
准备第七章
本文作者:Eric
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!