《算法图解》部分算法


广度优先搜索–寻找供货商

广度优先搜索索(breadth-first search,BFS)是一种用于图的查找算法,可帮助回答两类问题
在这里插入图片描述

  • 问题描述

假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。为此,你可在朋友中查找。假设你没有朋友是芒果销售商,那么你就必须在朋友的朋友中查找。你的目标是在你的人际关系网中找到一位芒果销售商。
在这里插入图片描述

  • 代码
# 构建图
graph = {}
graph['you'] = ['Alice', 'Bob', 'Claire']
graph['Bob'] = ['Anuj', 'Peggy']
graph['Alice'] = ['Peggy']
graph['Claire'] = ['Thom', 'Jonny']
graph['Peggy'] = []
graph['Thom'] = []
graph['Jonny'] = []
graph['Anuj'] = []


# 广度优先搜索
def bfs(name):
    # 售货商是Jonny
    def person_is_seller(name):
        if name == 'Jonny':
            return True
        else:
            return False

    # 使用队列存储待查找的人
    from collections import deque
    search_queue = deque()
    search_queue.append(name)

    processed = []  # 存储已经查找过的人,避免多次查找或无限循环
    while search_queue:
        name = search_queue.popleft()  # 出队
        if name not in processed and person_is_seller(name):
            print('找到售货商嘞,就是 {} !'.format(name))
            return
        else:
            processed.append(name)
            search_queue += graph[name]  # 入队
    print('你的朋友网里妹有供货商啊...')

bfs('you')
# 找到售货商嘞,就是 Jonny !
  • 运行时间

在这里插入图片描述


贪心算法–集合覆盖问题

  • 问题描述

假设你办了个广播节目,要让全美8个州的听众都收听得到。为此,你需要决定在哪些广播台播出。在每个广播台播出都需要支付费用,因此你力图在尽可能少的广播台播出。现有广播台名单如下。
在这里插入图片描述
每个广播台都覆盖特定的区域,不同广播台的覆盖区域可能重叠。
在这里插入图片描述

  • 目标
    • 找出覆盖8个州的最小广播台集合
  • 解决方案
    在这里插入图片描述
  • 分析
    1. 只要还有未覆盖地区,就进行一轮筛选:
      在未选取的广播站中找到覆盖未覆盖地区最多的广播站,将其选中后,从未覆盖地区中剔除该广播站覆盖的区域
    2. 不断进行上述过程,直到覆盖全部地区
  • 代码
states_needed = {"mt", "wa", "or", "id", "nv", "ut", "ca", "az"}

# 键为广播站名称,值为广播站覆盖的地区名称‘集合’
stations = {}
stations["kone"] = {"id", "nv", "ut"}
stations["ktwo"] = {"wa", "id", "mt"}
stations["kthree"] = {"or", "nv", "ca"}
stations["kfour"] = {"nv", "ut"}
stations["kfive"] = {"ca", "az"}


def search(stations, states_needed):
    ans = []
    while states_needed:
        max_cover_nums = 0
        max_station = None
        for station, state in stations.items():
            union = states_needed & state
            if len(union) > max_cover_nums:
                max_cover_nums = len(union)
                max_station = station
        ans.append(max_station)
        states_needed -= stations[max_station]
        del stations[max_station]
    return ans


print(search(stations, states_needed))

迪杰斯特拉算法–加权有向无环图的最短路径

  • 求解加权图的最短路径
  • 只适用于有向无环图(directed acyclic graph,DAG)
  • 广度优先搜索与迪杰斯特拉算法的区别
    + 要计算非加权图中的最短路径,可使用广度优先搜索
    + 要计算加权图中的最短路径,可使用狄克斯特拉算法
  • 问题描述
  • 有以下加权有向无环图,其中每个数字表示的都是时间,单位分钟。需要找出从起点到终点耗时最短的路径
    在这里插入图片描述
  • 算法步骤
    1. 找出最便宜的节点,即可在最短时间内前往的节点。
    2. 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。
    3. 重复这个过程,直到对图中的每个节点都这样做了。
    4. 计算最终路径。
  • 实现
# 构建加权有向无环图
graph = {}
graph['start'] = {'A': 6, 'B': 2}
graph['A'] = {'end': 1}
graph['B'] = {'A': 3, 'end': 5}
graph['end'] = None

# 记录每一个接电的前驱结点
parents = {}
parents['start'] = None
parents['A'] = 'start'
parents['B'] = 'start'
parents['end'] = None

# 记录每一个结点的最短距离
costs = {}
costs['A'] = 6
costs['B'] = 2
costs['end'] = float('inf')


def findLowestPath(graph, parents, costs):
    processed = ['end']

    # 寻找当前未处理过的距离最近的点
    def findLowestCostNode():
        lowest_cost = float('inf')
        lowest_cost_node = None
        for node in costs:
            c = costs[node]
            if node not in processed and c < lowest_cost:
                lowest_cost = c
                lowest_cost_node = node
        return lowest_cost_node

    node = findLowestCostNode()
    while node:
        c = costs[node]
        for k, v in graph[node].items():
            if c + v < costs[k]:
                costs[k] = c + v
                parents[k] = node
        processed.append(node)
        node = findLowestCostNode()


# 调用
findLowestPath(graph, parents, costs)

# 输出
for node in costs:
    print('到达{:3}的最短距离为{:2}'.format(node, costs[node]), end=',')
    path = node
    while parents[node]:
        path = parents[node]+'-->'+path
        node = parents[node]
    print(path)

# 到达A  的最短距离为 5,start-->B-->A
# 到达B  的最短距离为 2,start-->B
# 到达end的最短距离为 6,start-->B-->A-->end

参考资料:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值