《图解算法》中常见算法总结

目录:

1. 二分查找
2. 选择排序
3. 递归&分治
4. 快速排序
5. 广度优先搜索
6. 狄克斯特拉算法
7. 贪婪算法(近似算法)
8. 动态规划
9. K最近邻算法

1.二分查找

思路:
二分查找是一种算法,其输入是一个有序的元素列表。如果要查找的元素包含在列表中,二分查找返回其位置;否则返回null。
特点:
使用二分查找时,你猜测的是中间的数字,从而每次都将余下的数字排除一半。
《图解算法》中常见算法总结_第1张图片
《图解算法》中常见算法总结_第2张图片
时间复杂度:
一般而言,对于包含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)))

2.选择排序

思路:
遍历这个列表,找出最小值,并将该最小值添加到一个新列表中。
再次这样做,找出第二小的值。
继续这样做,将得到一个有序列表。
时间复杂度:
要找出最小值,必须检查列表中的每个元素,这需要的时间为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]

3.递归算法&分治思想

  • 递归:
    思想:
    每个递归函数都有两部分:基线条件(base case)和递归条件(recursive case)。
    递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。
    特点:
    递归只是让解决方案更清晰,并没有性能上的优势。
  • 分治:
    思想:
    递归式问题解决方法(divide and conquer, D&C)
    使用D&C解决问题的过程包括两个步骤:
    (1) 找出基线条件,这种条件必须尽可能简单。
    (2) 不断将问题分解(或者说缩小规模),直到符合基线条件。
    举例:
    (1)假设你要将一块地均匀地分成方块,且分出的方块要尽可能大:
    首先,找出基线条件。最容易处理的情况是,一条边的长度是另一条边的整数倍。
    《图解算法》中常见算法总结_第3张图片
    你可以从这块地中划出两个640 m×640 m的方块,同时余下一小块地。现在是顿悟时刻:何不对余下的那一小块地使用相同的算法呢?
    最初要划分的土地尺寸为1680 m×640 m,而现在要划分的土地更小,为640 m×400 m。 适用于这小块地的最大方块,也是适用于整块地的最大方块。换言之,你将均匀划分1680 m×640 m土地的问题,简化成了均匀划分640 m×400 m土地的问题!
    《图解算法》中常见算法总结_第4张图片
    (2)数字数组求和:
    第一步:找出基线条件。最简单的数组什么样呢?如果数组不包含任何元素或只包含一个元素,计算总和将非常容易。这就是基线条件。
    第二步:每次递归调用都必须离空数组更近一步。如何缩小问题的规模呢?计算第一个元素与剩余元素总数的和,这缩小了问题规模。
    《图解算法》中常见算法总结_第5张图片

4.快速排序

思想:
快速排序是一种常用的排序算法,比选择排序快得多。例如, 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]))

时间复杂度:
快速排序的性能高度依赖于你选择的基准值。
最糟情况:
假设你总是将第一个元素用作基准值,且要处理的数组是有序的。
《图解算法》中常见算法总结_第6张图片
你将一个元素用作基准值,并将其他的元素划分到两个子数组中。这涉及数组中的全部8个元素,因此该操作的时间为O(n)。在调用栈的第一层,涉及全部8个元素,但实际上,在调用栈的每层都涉及O(n)个元素。因此,完成每层所需的时间都为O(n)。
在最糟情况下,有O(n)层,因此该算法的运行时间为O(n) * O(n) = O(n^2)。
平均情况:
《图解算法》中常见算法总结_第7张图片
在这个示例中,层数为O(log n),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(log n) = O(n log n)。这就是平均情况。
只要你每次都随机地选择一个数组元素作为基准值,快速排序的平均运行时间就将为O(n log n)。

5.广度优先搜索

图算法——广度优先搜索(breadth-first search, BFS),用于处理有向图中最短路径的问题。
思想:
借助队列实现,先放入第一层,判断每个元素是否是想要的,若是,结束;若不是,则将其邻居(第二层),放入队列末端,循环如此。
为解决有些元素重复处理的情况,可加入一个列表,列出所有处理过的,用于判断。
举例:
假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。为此,你可在朋友中查找。《图解算法》中常见算法总结_第8张图片
你应先在一度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的!在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。
《图解算法》中常见算法总结_第9张图片
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"))

其中,图的形成可借助字典结构。

6.狄克斯特拉算法

狄克斯特拉算法(Dijkstra’s algorithm)也是一种图算法,用来处理有权图的最短路径。
思想:
(1) 找出“最便宜”的节点,即权值最小的节点。
(2) 更新该节点的邻居的开销(前提是更小),即经过该节点到邻居点的总权重。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。
举例:
(1)找两点最短路径
《图解算法》中常见算法总结_第10张图片
第一步: 找出最便宜的节点。
《图解算法》中常见算法总结_第11张图片
第二步:计算经节点B前往其各个邻居所需的时间。
《图解算法》中常见算法总结_第12张图片
第三步:重复
找出可在最短时间内前往的节点
《图解算法》中常见算法总结_第13张图片
更新节点A的所有邻居的开销
《图解算法》中常见算法总结_第14张图片
(2)换钢琴
Rama想拿一本乐谱换架钢琴,如何花最少的钱实现这个目标。
《图解算法》中常见算法总结_第15张图片
准备工作:创建一个表格,在其中列出每个节点的开销;还需在这个表中添加表示父节点的列。在执行狄克斯特拉算法的过程中,你将不断更新表。
《图解算法》中常见算法总结_第16张图片《图解算法》中常见算法总结_第17张图片
算法分析过程:
《图解算法》中常见算法总结_第18张图片
《图解算法》中常见算法总结_第19张图片
《图解算法》中常见算法总结_第20张图片
《图解算法》中常见算法总结_第21张图片
python实现:
问题:
《图解算法》中常见算法总结_第22张图片
三个表存储:随着算法的进行,你将不断更新散列表costs和parents
《图解算法》中常见算法总结_第23张图片

'''狄克斯特拉算法'''
'''有权图形成'''
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)

7.贪婪算法(近似算法)

  • 贪婪算法:
    贪婪算法很简单:每步都采取最优的做法。即每步都选择局部最优解,最终得到的就是全局最优解,但得到的往往是接近于最优解的近似解。
  • 近似算法:
    判断近似算法优劣的标准如下:
    (1)速度有多快;
    (2)得到的近似解与最优解的接近程度。
  • NP完全问题:
    需要计算所有的解,并从中选出最小/最短的那个。
    如果能够判断出要解决的问题属于NP完全问题就好了,这样就不用去寻找完美的解决方案,而是使用近似算法即可。

近似算法python实现:
集合覆盖问题:
假设你办了个广播节目,要让各个州的听众都收听得到。为此,你需要决定在哪些广播台播出。在每个广播台播出都需要支付费用,因此你力图在尽可能少的广播台播出。
《图解算法》中常见算法总结_第24张图片
每次需要遍历所有的广播台,从中选择覆盖了最多的未覆盖州的广播台。

'''近似算法解决电台覆盖问题'''
'''数据输入'''
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)

8.动态规划

动态规划先解决子问题,再逐步解决大问题,得到最优解。
举例:
(1)背包问题:
假设你是个小偷,背着一个可装4磅东西的背包。你可盗窃的商品有如下3件,为了让盗窃的商品价值最高,你该选择哪些商品?
《图解算法》中常见算法总结_第25张图片
每个动态规划算法都从一个网格开始,背包问题的网格如下。
《图解算法》中常见算法总结_第26张图片
按行输入值,只可选当前行及上面行的物品。
《图解算法》中常见算法总结_第27张图片
《图解算法》中常见算法总结_第28张图片
《图解算法》中常见算法总结_第29张图片
计算每个单元格的价值时,使用的公式都相同:
《图解算法》中常见算法总结_第30张图片
(2)旅游形成最优化:
假设你要去伦敦度假,假期两天,但你想去游览的地方很多。你没法前往每个地方游览,因此你列个单子。根据这个清单,你能确定该去游览哪些名胜吗?
《图解算法》中常见算法总结_第31张图片
网格如下:
《图解算法》中常见算法总结_第32张图片
《图解算法》中常见算法总结_第33张图片

9.K最近邻算法

K最近邻(k-nearest neighbours, KNN)算法做两项基本工作——分类和回归:
(1)分类就是编组;
(2)回归就是预测结果(如一个数字)。
举例:
猜水果,是橙子还是柚子呢?我知道,柚子通常比橙子更大、更红。
《图解算法》中常见算法总结_第34张图片
《图解算法》中常见算法总结_第35张图片
如果判断这个水果是橙子还是柚子呢?一种办法是看它的邻居。来看看离它最近的三个邻居。在这三个邻居中,橙子比柚子多,因此这个水果很可能是橙子。
(2)创建推荐系统:
假设你是Netflix,要为用户创建一个电影推荐系统。
方法:
你可以将所有用户都放入一个图表中。这些用户在图表中的位置取决于其喜好,因此喜好相似的用户距离较近。假设你要向Priyanka推荐电影,可以找出五位与他最接近的用户。
特征提取:
需要将每位用户都转换为一组坐标
用户注册时,要求他们指出对各种电影的喜欢程度。这样,对于每位用户,都将获得一组数字!
《图解算法》中常见算法总结_第36张图片
这里计算的是五维(而不是二维)空间中的距离。
(3)烤面包
假设你在伯克利开个小小的面包店,每天都做新鲜面包,需要根据如下一组特
征预测当天该烤多少条面包
《图解算法》中常见算法总结_第37张图片
你还有一些历史数据,记录了在各种不同的日子里售出的面包数量。
《图解算法》中常见算法总结_第38张图片
今天是周末,天气不错。根据这些数据,预测你今天能售出多少条面包呢?我们来使用KNN算法,其中的K为4。首先,找出与今天最接近的4个邻居。
《图解算法》中常见算法总结_第39张图片
最近的邻居为A、 B、 D和E。
《图解算法》中常见算法总结_第40张图片
将这些天售出的面包数平均,结果为218.75。这就是你今天要烤的面包数!
使用到回归

你可能感兴趣的:(常见算法)