数据结构与算法Python版 最短路径问题与最小生成树


一、图的应用-最短路径问题

最短路径问题-介绍

  • 互联网实际上是一个由路由器连接成的网络。这些路由器各自独立而又协同工作,负责将信息从Internet的一端传送到另一端。建模如下:
    • 将互联网路由器体系表示为一个带权边的图
    • 路由器作为顶点,路由器之间网络连接作为边
    • 权重可以包括网络连接的速度、网络负载程度、分时段优先级等影响因素
    • 作为一个抽象,我们把所有影响因素合成为单一的权重
  • 解决信息在路由器网络中选择传播速度最快路径的问题,就转变为在带权图上最短路径的问题。与广度优先搜索BFS算法解决的词梯问题相似,只是在边上增加了权重

在这里插入图片描述

最短路径问题-Dijkstra算法

  • 这是一个迭代算法,得出从一个顶点到其余所有顶点的最短路径。
  • 在顶点Vertex类的属性distance,用于记录从开始顶点到本顶点的最短带权路径长度(权重之和),对图中的每个顶点迭代一次
    • 顶点的访问次序由一个优先队列来控制,队列中作为优先级的是顶点的distance属性。
    • 最初,只有开始顶点distance设为0,而其他所有顶点distance设为sys.maxsize(最大整数),全部加入优先队列。
    • 随着队列中每个最低distance顶点率先出队,并计算它与邻接顶点的权重,会引起其它顶点distance的减小和修改,引起堆重排
    • 并据更新后的distance优先级再依次出队
  • Dijkstra算法时间复杂度约为O((|V|+|E|)log|V|)

Dijkstra算法实现

  • 优先队列BinaryHeap类:增加堆重排rebuild_heap方法
  • 顶点类Vertex:属性distance的默认值设置为sys.maxsize
  • dijkstra函数:实现Dijkstra算法
import sys


class BinaryHeap:
	def rebuild_heap(self):
        """对堆中所有元素进行堆重排"""
        tmp = self.heap_list[1:]
        self.build_heap(tmp)
        
    # 其它代码详见《数据结构与算法Python版 二叉堆与优先队列》


class Vertex:
    """顶点类"""

    def __init__(
        self,
        key,
        distance=sys.maxsize,
        pred=None,
        color="white",
        discovery=None,
        finish=None,
    ):
        self.key = key
        self.connected_to = {}
        self.distance = distance
        self.pred = pred
        self.color = color
        self.discovery = discovery
        self.finish = finish
      
	# 其它代码详见《数据结构与算法Python版 通用的深度优先搜索》
    
    

def dijkstra(g: Graph, start: Vertex):
    """dijkstra算法实现"""
    bh = BinaryHeap()
    # 开始顶点distance设为0
    start.set_distance(0)
    # 全部加入优先队列
    bh.build_heap([v for v in g])
    while not bh.is_empty():
        # 最低distance顶点出队
        cur_vert = bh.del_min()
        # 计算它与邻接顶点的权重
        for next_vert in cur_vert.get_connections():
            new_dist = cur_vert.get_distance() + cur_vert.get_weight(next_vert)
            if new_dist < next_vert.get_distance():
                next_vert.set_distance(new_dist)
                next_vert.set_pred(cur_vert)
        # 堆重排
        bh.rebuild_heap()

最短路径问题-示例

# 构建图
edges = [
    ["u", "v", 2],
    ["u", "x", 1],
    ["u", "w", 5],
    ["v", "w", 3],
    ["v", "x", 2],
    ["x", "w", 3],
    ["x", "y", 1],
    ["w", "y", 1],
    ["w", "z", 5],
    ["y", "z", 1],
]
g = Graph()
for i in edges:
    g.add_edge(i[0], i[1], i[2])
    g.add_edge(i[1], i[0], i[2])
dijkstra(g, g.vertexes["u"])


def get_path(cur: Vertex, start: Vertex):
    """获取从当前顶点到开始顶点的路径"""
    tmp = []
    tmp.append(cur.key)
    while cur != start:
        cur = cur.get_pred()
        tmp.append(cur.key)
    return tmp

for i in g.vertexes.values():
    print(
        f'顶点:{i.key},最短距离:{i.distance},最短路径:{get_path(i, g.vertexes["u"])}'
    )

输出结果

顶点:u,最短距离:0,最短路径:['u']
顶点:v,最短距离:2,最短路径:['v', 'u']
顶点:x,最短距离:1,最短路径:['x', 'u']
顶点:w,最短距离:3,最短路径:['w', 'y', 'x', 'u']
顶点:y,最短距离:2,最短路径:['y', 'x', 'u']     
顶点:z,最短距离:3,最短路径:['z', 'y', 'x', 'u']

在这里插入图片描述

二、图的应用-最小生成树

信息广播问题

  • 单播解法:广播源维护一个收听者的列表,将每条消息采用最短路径算法向每个收听者发送一次。如果有4个收听者,则每条消息会被发送4次,会产生许多额外流量。
  • 洪水解法:所有的路由器都将收到的消息转发到自己相邻的路由器和收听者,如果没有任何限制,这个方法将造成网络洪水灾难,永不停止!给每条消息附加一个生命值(TTL:Time To Live),每个路由器收到一条消,如果其TTL值大于0,则将TTL减少1,再转发出去。
  • 最小生成树:信息广播问题的最优解法
    • 路由器关系图上选取具有最小权重的生成树(minimum weight spanning tree)
    • 生成树:拥有图中所有的顶点和最少数量的边,以保持连通的子图。
    • 图G(V,E)的最小生成树T,定义为包含所有顶点V,以及E的无圈子集,并且边权重之和最小

最小生成树-Prim算法

  • Prim算法,属于“贪心算法”,即每步都沿着最小权重的边向前搜索。如果T还不是生成树,则反复做:
    • 找到一条最小权重的可以安全添加的边,将边添加到树T
    • “可以安全添加”的边,定义为一端顶点在树中,另一端不在树中的边,以便保持树的无圈特性

Prim算法实现

  • 优先队列BinaryHeap类:增加特殊方法__contains__方法,实现in语法判断
  • Prim函数:实现Prim算法
class BinaryHeap:
    def __contains__(self, key):
        """实现in语法判断"""
        return key in self.heap_list
    
    # 其它代码详见《数据结构与算法Python版 二叉堆与优先队列》

    
def prim(g: Graph, start: Vertex):
    """prim算法实现"""
    bh = BinaryHeap()
    # 开始顶点distance设为0
    start.set_distance(0)
    # 全部加入优先队列
    bh.build_heap([v for v in g])
    while not bh.is_empty():
        # 每次最低distance顶点出队(贪婪算法)
        cur_vert = bh.del_min()
        for next_vert in cur_vert.get_connections():
            new_dist = cur_vert.get_weight(next_vert)
            if next_vert in bh and new_dist < next_vert.get_distance():
                next_vert.set_pred(cur_vert)
                next_vert.set_distance(new_dist)

        bh.rebuild_heap()

最小生成树-示例

# 构建图
edges = [
    ["A", "B", 2],
    ["A", "C", 3],
    ["B", "C", 1],
    ["B", "D", 1],
    ["B", "E", 4],
    ["C", "F", 5],
    ["D", "E", 1],
    ["E", "F", 1],
    ["F", "G", 1],
]
g = Graph()
for i in edges:
    g.add_edge(i[0], i[1], i[2])
    g.add_edge(i[1], i[0], i[2])

# 执行prim算法,并打印结果
prim(g, g.vertexes["A"])


def get_path(cur: Vertex, start: Vertex):
    """获取从当前顶点到开始顶点的路径"""
    tmp = []
    tmp.append(cur.key)
    while cur != start:
        cur = cur.get_pred()
        tmp.append(cur.key)
    return tmp


for i in g.vertexes.values():
    print(f'顶点:{i.key},最小生成树路径:{get_path(i, g.vertexes["A"])}')

输出结果

顶点:A,最小生成树路径:['A']
顶点:B,最小生成树路径:['B', 'A']
顶点:C,最小生成树路径:['C', 'B', 'A']
顶点:D,最小生成树路径:['D', 'B', 'A']
顶点:E,最小生成树路径:['E', 'D', 'B', 'A']
顶点:F,最小生成树路径:['F', 'E', 'D', 'B', 'A']     
顶点:G,最小生成树路径:['G', 'F', 'E', 'D', 'B', 'A']

在这里插入图片描述


您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值