1. 二分查找
2. 选择排序
3. 递归&分治
4. 快速排序
5. 广度优先搜索
6. 狄克斯特拉算法
7. 贪婪算法(近似算法)
8. 动态规划
9. K最近邻算法
思路:
二分查找是一种算法,其输入是一个有序的元素列表。如果要查找的元素包含在列表中,二分查找返回其位置;否则返回null。
特点:
使用二分查找时,你猜测的是中间的数字,从而每次都将余下的数字排除一半。
时间复杂度:
一般而言,对于包含n个元素的列表,用二分查找最多需要log2n步,而简单查找最多需要n步。
python实现:
def binary_search(list,item):
'''二分查找'''
low=0 #起点
high=len(list)-1 #终点
while high>=low:
mid=low+int((high-low)/2) #中间值
if item==list[mid]:
return mid
elif item>list[mid]:
low=mid+1;
else:
high=mid-1;
return None
list=[1,2,3,6,9,10]
print(str(binary_search(list,6)))
print(str(binary_search(list,4)))
思路:
遍历这个列表,找出最小值,并将该最小值添加到一个新列表中。
再次这样做,找出第二小的值。
继续这样做,将得到一个有序列表。
时间复杂度:
要找出最小值,必须检查列表中的每个元素,这需要的时间为O(n)。因此对于这种时间为O(n)的操作,你需要执行n次。
需要的总时间为 O(n × n),即O(n^2)。
python实现:
def findsmallest(arr):
'''找列表中最小值'''
smallest=arr[0]
smallest_index=0
for i in range(1,len(arr)):
if arr[i]
思想:
快速排序是一种常用的排序算法,比选择排序快得多。例如, C语言标准库中的函数qsort
实现的就是快速排序。快速排序也使用了D&C。
基线条件:为数组为空或只包含一个元素。在这种情况下,只需原样返回数组——根本就不用排序。
递归条件:
从数组中选择一个元素,这个元素被称为基准值(pivot)。
找出比基准值小的元素以及比基准值大的元素,这被称为分区(partitioning)。
对于包含左边的子数组以及右边的子数组,快速排序知道如何将它们排序,因此只要对这两个子数组进行快速排序,再合并结果,就能得到一个有序数组。
python实现:
def quicksort(arr):
'''快速排序法'''
if len(arr)<2:
return arr
else:
pivot=arr[0]
less=[i for i in arr[1:] if ipivot]
return quicksort(less)+[pivot]+quicksort(larger)
print(quicksort([1,5,3,2]))
时间复杂度:
快速排序的性能高度依赖于你选择的基准值。
最糟情况:
假设你总是将第一个元素用作基准值,且要处理的数组是有序的。
你将一个元素用作基准值,并将其他的元素划分到两个子数组中。这涉及数组中的全部8个元素,因此该操作的时间为O(n)。在调用栈的第一层,涉及全部8个元素,但实际上,在调用栈的每层都涉及O(n)个元素。因此,完成每层所需的时间都为O(n)。
在最糟情况下,有O(n)层,因此该算法的运行时间为O(n) * O(n) = O(n^2)。
平均情况:
在这个示例中,层数为O(log n),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(log n) = O(n log n)。这就是平均情况。
只要你每次都随机地选择一个数组元素作为基准值,快速排序的平均运行时间就将为O(n log n)。
图算法——广度优先搜索(breadth-first search, BFS),用于处理有向图中最短路径的问题。
思想:
借助队列实现,先放入第一层,判断每个元素是否是想要的,若是,结束;若不是,则将其邻居(第二层),放入队列末端,循环如此。
为解决有些元素重复处理的情况,可加入一个列表,列出所有处理过的,用于判断。
举例:
假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。为此,你可在朋友中查找。
你应先在一度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的!在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。
python实现:
from collections import deque
'''图的形成'''
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"]=[]
print(graph)
'''广度优先搜索'''
def person_is_seller(name):
'''定义一个供应商确定函数'''
return name[-1]=="m"
def BFsearch(name):
'''借助队列'''
search_queue = deque()
search_queue += graph[name]
searched = []
while search_queue:
person = search_queue.popleft()
if person not in searched:
'''判断是否重复'''
if person_is_seller(person):
print(person+" is a seller!")
return True
else:
'''否的话要将其朋友拉入队列'''
search_queue += graph[person]
searched.append(person)
return False
print(BFsearch("you"))
其中,图的形成可借助字典结构。
狄克斯特拉算法(Dijkstra’s algorithm)也是一种图算法,用来处理有权图的最短路径。
思想:
(1) 找出“最便宜”的节点,即权值最小的节点。
(2) 更新该节点的邻居的开销(前提是更小),即经过该节点到邻居点的总权重。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。
举例:
(1)找两点最短路径
第一步: 找出最便宜的节点。
第二步:计算经节点B前往其各个邻居所需的时间。
第三步:重复
找出可在最短时间内前往的节点
更新节点A的所有邻居的开销
(2)换钢琴
Rama想拿一本乐谱换架钢琴,如何花最少的钱实现这个目标。
准备工作:创建一个表格,在其中列出每个节点的开销;还需在这个表中添加表示父节点的列。在执行狄克斯特拉算法的过程中,你将不断更新表。
算法分析过程:
python实现:
问题:
三个表存储:随着算法的进行,你将不断更新散列表costs和parents
'''狄克斯特拉算法'''
'''有权图形成'''
graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}
graph["a"]["fin"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5
graph["fin"] = {}
print(graph)
'''开销表'''
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity
print(costs)
'''父节点表'''
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None
print(parents)
processed=[]
'''找开销最低的节点'''
def find_lowestcost_node(costs):
lowestcost = infinity
lowestcost_node = None
for node in costs.keys():
cost=costs[node]
if cost < lowestcost and node not in processed:
lowestcost = cost
lowestcost_node = node
return lowestcost_node
'''狄克斯特拉算法'''
node = find_lowestcost_node(costs) #找最便宜的点
while node is not None:
cost=costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if new_cost < costs[n]:
costs[n] = new_cost #更新邻居的值
parents[n] = node
processed.append(node)
node = find_lowestcost_node(costs)
print(costs)
print(parents)
近似算法python实现:
集合覆盖问题:
假设你办了个广播节目,要让各个州的听众都收听得到。为此,你需要决定在哪些广播台播出。在每个广播台播出都需要支付费用,因此你力图在尽可能少的广播台播出。
每次需要遍历所有的广播台,从中选择覆盖了最多的未覆盖州的广播台。
'''近似算法解决电台覆盖问题'''
'''数据输入'''
states_needed = set(["mt","wa","or","id","nv","ut","ca","az"])
print(states_needed)
stations = {}
stations["kone"] = set(["id","nv","ut"])
stations["ktwo"] = set(["wa","id","mt"])
stations["Kthree"] = set(["or","nv","ca"])
stations["kfour"] = set(["nv","ut"])
stations["kfive"] = set(["ca","az"])
print(stations)
final_stations = set()
'''近似算法'''
while states_needed:
'''每次找最优解'''
best_station = None
state_covered = set()
for station,states in stations.items():
covered = states & states_needed
if covered > state_covered:
best_station = station
state_covered = covered
states_needed -= state_covered
final_stations.add(best_station)
print(final_stations)
动态规划先解决子问题,再逐步解决大问题,得到最优解。
举例:
(1)背包问题:
假设你是个小偷,背着一个可装4磅东西的背包。你可盗窃的商品有如下3件,为了让盗窃的商品价值最高,你该选择哪些商品?
每个动态规划算法都从一个网格开始,背包问题的网格如下。
按行输入值,只可选当前行及上面行的物品。
计算每个单元格的价值时,使用的公式都相同:
(2)旅游形成最优化:
假设你要去伦敦度假,假期两天,但你想去游览的地方很多。你没法前往每个地方游览,因此你列个单子。根据这个清单,你能确定该去游览哪些名胜吗?
网格如下:
K最近邻(k-nearest neighbours, KNN)算法做两项基本工作——分类和回归:
(1)分类就是编组;
(2)回归就是预测结果(如一个数字)。
举例:
猜水果,是橙子还是柚子呢?我知道,柚子通常比橙子更大、更红。
如果判断这个水果是橙子还是柚子呢?一种办法是看它的邻居。来看看离它最近的三个邻居。在这三个邻居中,橙子比柚子多,因此这个水果很可能是橙子。
(2)创建推荐系统:
假设你是Netflix,要为用户创建一个电影推荐系统。
方法:
你可以将所有用户都放入一个图表中。这些用户在图表中的位置取决于其喜好,因此喜好相似的用户距离较近。假设你要向Priyanka推荐电影,可以找出五位与他最接近的用户。
特征提取:
需要将每位用户都转换为一组坐标
用户注册时,要求他们指出对各种电影的喜欢程度。这样,对于每位用户,都将获得一组数字!
这里计算的是五维(而不是二维)空间中的距离。
(3)烤面包
假设你在伯克利开个小小的面包店,每天都做新鲜面包,需要根据如下一组特
征预测当天该烤多少条面包
你还有一些历史数据,记录了在各种不同的日子里售出的面包数量。
今天是周末,天气不错。根据这些数据,预测你今天能售出多少条面包呢?我们来使用KNN算法,其中的K为4。首先,找出与今天最接近的4个邻居。
最近的邻居为A、 B、 D和E。
将这些天售出的面包数平均,结果为218.75。这就是你今天要烤的面包数!
使用到回归。