二分查找
def binary_search(list, item):
low = 0
high = len(list) - 1
while low <= high:
mid = (low + high) // 2
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
my_list = [1, 3, 5, 7, 9]
print(binary_search(my_list, 3)) # 1
print(binary_search(my_list, -1)) # None
选择排序
# 找出数组中最小元素的函数
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 selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append(arr.pop(smallest))
return newArr
print(selectionSort([5, 3, 6, 2, 10]))
# [2, 3, 5, 6, 10]
递归
# 编写一个倒计时的函数
def countdown(i):
print(i)
if i <= 1: # 基线条件(base case)指函数不再调用自己,从而避免形成无限循环
return
else: # 递归条件(recursive case)指函数调用自己
countdown(i-1)
递归列表求和函数
# 编写求和函数
def sum_list(list):
if list == []:
return 0
else:
return list[0] + sum_list(list[1:])
print(sum_list([1, 2, 3, 4, 5, 6]))
查找列表最大元素函数
# 编写查找列表最大元素的函数
def findmax(list):
if list[0] >= max(list[0:]):
return list[0]
else:
return findmax(list[1:])
print(findmax([2, 5, 6, 1, 0, 9]))
快速排序
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]))
# [2, 3, 5, 10]
广度优先搜索
解决最短路径问题(shortest-path problem)的算法被称为广度优先搜索(breadth-first search, BFS)。
图由节点(node)和边(edge)组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。
图中的关系是单向的时候,即为有向图(directed graph);无向图(undirected graph)没有箭头,其中的关系是双向的,直接相连的节点互为邻居。无向图中,每条边都是一个环。
如果任务A依赖于任务B,在列表中任务A就必须在任务B后面,这被称为拓扑排序,使用它可以根据图创建一个有序列表。
树是一种特殊的图,没有往后指的边。
广度优先搜索可回答两类问题:
1- 从节点A出发,有前往节点B的路径吗?
2- 从节点A出发,前往节点B的哪条路径最短?
from collections import deque
search_queue = deque() # 创建一个队列
search_queue += graph["you"] # 将你的邻居都加入到这个搜索队列中
searched = [] # 用于记录检查过的人
while search_queue:
person = search_queue.popleft() # 取出其中的第一个人
if person not 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'
狄克斯特拉算法
狄克斯特拉算法(Dijkstra’s algorithm)可以找到有向无环图中的最短路径。加权图中每条边都有关联数字,称为权重(weight)。带权重的图称为加权图(weighted graph),不带权重的图称为非加权图(unweighted graph)。它包含4个步骤:
1- 找出“最便宜”的节点,即可在最短时间内前往的节点;
2- 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销;
3- 重复这个过程,直到对图中的每个节点都这样做了;
4- 计算最终路径。
此处最短路径指的并不一定是物理距离,也可能是让某种度量指标最小。
如果出现负权边,即不能使用狄克斯特拉算法,得用贝尔曼-福德算法(Bellman-Ford algorithm)。
# 创建带权重的散列表
graph["start"] = {}
graph["start"]["a"] = 6
grpah["start"]["b"] = 2
graph["a"] = {}
graph["a"]["fin"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
grpah["b"]["fin"] = 5
graph["fin"] = {} # 终点没有任何邻居
# 创建开销表
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 3
costs["fin"] = infinity
# 存储父节点的散列表
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"]= None
# 数组,用于记录处理过的点
processed = []
node = find_lowest_cost_node(costs)
# 在未处理的节点中找出开销最小的节点
while node is not None: # while循环在所有节点都被处理过后结束
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) # 找出接下来要处理的节点,并循环
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
贪婪算法
贪婪算法:每步都采取最优的做法。可理解为,每步都选择局部最优解,最终得到的就是全局最优解。
1- 背包问题
2- 集合覆盖问题
# 使用集合来表示要覆盖的州
states_needed = set(["mt", "wa", "or", "id", "nv", "ut", "ca", "az"])
# 可供选择的广播台清单
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"])
# 创建一个集合来存储最终选择的广播台
final_stations = set()
# 需要遍历所有的电视台,从中选择覆盖了最多的未覆盖州的广播台
while states_needed:
best_station = None
states_covered = set()
for station, states in stations.items():
# 计算交集& 并集| 差集-
covered = states_needed & states
if len(covered) > len(states_covered):
best_station = station
states_covered = covered
states_needed -= states_covered
final_stations.add(best_station)
print(final_stations)
# {'kfive', 'kthree', 'ktwo', 'kone'}
快速排序不是贪婪算法,广度优先搜索和狄克斯特拉算法是贪婪算法。
对于NP完全问题,还没有找到快速解决方案。
面临NP完全问题时,最佳的做法是使用近似算法。
贪婪算法易于实现、运行速度快,是不错的近似算法。
动态规划
动态规划先解决子问题,再逐步解决大问题。但仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用。动态规划可帮助在给定约束条件下找到最优解。
最长公共子串
并没有看懂
动态规划的实际应用
1- DNA链的相似性
2- git diff命令,指出两个文件的差异
3- 编辑距离(levenshtein distance)指出了两个字符串的相似程度
4- ……
K最近邻算法
回归或者分类