• 最短路径问题


    前言

    在上一节图的遍历算法的基础上,解决最短路径问题。

    存在无权有向图如下:

    v1表示起点,v9表示终点,请找出有几条路可以达到,并且最短路径是哪个?

    深度优先搜索算法求解

    from collections import deque
    
    Start = "v1"
    Terminal = "v9"
    Q = deque()
    
    def output():
        lst = []
        while Q:
            item = Q.popleft()
            lst.append(item)
        for item in lst:
            Q.append(item)
        print(">>>{}".format(lst))
    
    def recurse(items, graph):
        for item in items:
            Q.append(item)          # 入栈
            if item == Terminal:    # !!!这个是递归的基线条件,递归终止的地方!!!
                output()            # 输出当前路线
                Q.pop()             # 把终点出栈
                continue
            else:
                recurse(graph[item], graph)
        Q.pop()                     # 兄弟节点都遍历完了,所以把父亲元素出栈
    
    # depth first search
    def dfs(graph):
        Q.append(Start)                     # 把起点入栈
        recurse(graph[Start], graph)        # 开始递归
    
    
    if __name__ == "__main__":
        graph = {}
        graph["v1"] = ["v2", "v3", "v4"]
        graph["v2"] = ["v5", "v6"]
        graph["v3"] = ["v9"]
        graph["v4"] = ["v7", "v8"]
        graph["v5"] = ["v9", "v10"]
        graph["v6"] = ["v9"]
        graph["v7"] = []
        graph["v8"] = ["v7", "v9", "v11"]
        graph["v9"] = ["v7"]
        graph["v10"] = ["v9", "v6"]
        graph["v11"] = ["v9"]
        dfs(graph)

    执行结果 :

    >>>['v1', 'v2', 'v5', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v6', 'v9']
    >>>['v1', 'v2', 'v6', 'v9']
    >>>['v1', 'v3', 'v9']                             # 很明显,这个是最短路径
    >>>['v1', 'v4', 'v8', 'v9']
    >>>['v1', 'v4', 'v8', 'v11', 'v9']

    广度优先搜索算法求解

    方法一:广度优先转换为深度优先

    from collections import deque
    
    Start = "v1"
    Terminal = "v9"
    Q = deque()
    
    
    def output():
        lst = []
        while Q:
            item = Q.popleft()
            lst.append(item)
        for item in lst:
            Q.append(item)
        lst.reverse()
        print(">>>{}".format(lst))
    
    
    def recurse(items, graph):
        for item in items:
            Q.append(item)          # 入栈
            if item == Start:       # !!!这个是递归的基线条件,递归终止的地方!!!
                output()            # 输出当前路线
                Q.pop()             # 把起点出栈
                continue
            else:
                recurse(graph[item], graph)
        Q.pop()                     # 兄弟节点都遍历完了,所以把父亲元素出栈
    
    # depth first search
    def dfs(graph):
        Q.append(Terminal)                     # 把终点入栈
        recurse(graph[Terminal], graph)        # 开始递归
    
    
    # breadth first search
    def bfs(graph):
        q = deque()
        q += graph[Start]
        son2parent = {Start: []}
        son2parent.update(dict.fromkeys(graph[Start], [Start, ]))
        while q:
            item = q.popleft()  # first in first out
            if item != Terminal:
                q += graph[item]
                for ii in graph[item]:
                    if ii not in son2parent:
                        son2parent[ii] = [item, ]
                    else:
                        if item not in son2parent[ii]:
                            son2parent[ii].append(item)
        # !!!转为为使用dfs求解!!!
        dfs(son2parent)
    
    
    if __name__ == "__main__":
        graph = {}
        graph["v1"] = ["v2", "v3", "v4"]
        graph["v2"] = ["v5", "v6"]
        graph["v3"] = ["v9"]
        graph["v4"] = ["v7", "v8"]
        graph["v5"] = ["v9", "v10"]
        graph["v6"] = ["v9"]
        graph["v7"] = []
        graph["v8"] = ["v7", "v9", "v11"]
        graph["v9"] = ["v7"]
        graph["v10"] = ["v9", "v6"]
        graph["v11"] = ["v9"]
        bfs(graph)

    执行结果:

    >>>['v1', 'v3', 'v9']
    >>>['v1', 'v2', 'v5', 'v9']
    >>>['v1', 'v2', 'v6', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v6', 'v9']
    >>>['v1', 'v4', 'v8', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v9']
    >>>['v1', 'v4', 'v8', 'v11', 'v9']

    方法二:为了保存父子关系,所以针对每一步都生成对应的路径,并结合队列先进先出。

    from collections import deque
    
    
    Start = "v1"
    Terminal = "v9"
    
    
    def bfs(graph):
        q = deque()
        path = []
        path.append(Start)
        q.append(path)
        while q:
            path = q.popleft()
            _next = path[len(path)-1]
            if Terminal == _next:
                print(">>>{}".format(path))
            children = graph[_next]
            for child in children:
                new_path = path.copy()  # !!!important!!!
                new_path.append(child)
                q.append(new_path)
    
    
    if __name__ == "__main__":
        graph = {}
        graph["v1"] = ["v2", "v3", "v4"]
        graph["v2"] = ["v5", "v6"]
        graph["v3"] = ["v9"]
        graph["v4"] = ["v7", "v8"]
        graph["v5"] = ["v9", "v10"]
        graph["v6"] = ["v9"]
        graph["v7"] = []
        graph["v8"] = ["v7", "v9", "v11"]
        graph["v9"] = ["v7"]
        graph["v10"] = ["v9", "v6"]
        graph["v11"] = ["v9"]
        bfs(graph)

    执行结果:

    >>>['v1', 'v3', 'v9']
    >>>['v1', 'v2', 'v5', 'v9']
    >>>['v1', 'v2', 'v6', 'v9']
    >>>['v1', 'v4', 'v8', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v9']
    >>>['v1', 'v4', 'v8', 'v11', 'v9']
    >>>['v1', 'v2', 'v5', 'v10', 'v6', 'v9']

    Dijkstra算法求解

    存在加权有向图如下(权重表示耗时):

    求出v1到v9走那条路径耗时最短?

    Start = "v1"
    Terminal = "v9"
    SON2PARENT = {}
    SON2COST = {}
    VISITED = []
    
    
    def find_lowest_cost_node(costs):
    	'''
    	在未处理的节点中找到开销最小的节点
    	:param costs: 节点和对应开销的散列表
    	:return:
    	'''
    	lowest_node = None
    	lowest_cost = float("inf")
    	for node, cost in costs.items():  # 遍历所有节点
    		if node in VISITED:
    			continue
    		if cost < lowest_cost:
    			lowest_cost = cost
    			lowest_node = node
    	return lowest_node
    
    def dijkstra(graph):
    	# 初始化
    	infinity = float("inf")
    	for node in graph.keys():
    		if node in graph[Start].keys():
    			SON2PARENT[node] = Start
    			SON2COST[node] = graph[Start][node]
    		else:
    			SON2PARENT[node] = None
    			SON2COST[node] = infinity
    	# 遍历所有节点,依次处理该节点的所有子节点
    	node = find_lowest_cost_node(SON2COST)
    	while node:
    		for son, weight in graph[node].items():
    			cost = SON2COST[node] + weight
    			if cost < SON2COST[son]:
    				SON2COST[son] = cost
    				SON2PARENT[son] = node
    		VISITED.append(node)
    		node = find_lowest_cost_node(SON2COST)
    
    	# print(VISITED)  ['v3', 'v2', 'v4', 'v5', 'v8', 'v10', 'v6', 'v9', 'v11', 'v7']
    	lst = [Terminal, ]
    	parent = SON2PARENT[Terminal]
    	while parent:
    		lst.append(parent)
    		parent = SON2PARENT[parent]
    	lst.reverse()
    	print("Path={} Cost={}".format(lst, SON2COST[Terminal]))
    
    
    if __name__ == "__main__":
    	graph = {}
    	graph["v1"] = {"v2": 2, "v3": 1, "v4": 3}
    	graph["v2"] = {"v5": 1, "v6": 3}
    	graph["v3"] = {"v9": 10}
    	graph["v4"] = {"v7": 3, "v8": 1}
    	graph["v5"] = {"v9": 7, "v10": 1}
    	graph["v6"] = {"v9": 3}
    	graph["v7"] = {}
    	graph["v8"] = {"v7": 4, "v9": 3, "v11": 1}
    	graph["v9"] = {"v7": 2}
    	graph["v10"] = {"v9": 1, "v6": 2}
    	graph["v11"] = {"v9": 1}
    	dijkstra(graph)

    执行结果:

    Path=['v1', 'v2', 'v5', 'v10', 'v9'] Cost=5
    作者:Standby一生热爱名山大川、草原沙漠,还有妹子
    出处:http://www.cnblogs.com/standby/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    JS循环语句作业讲解(折纸、兔子生兔子、买东西组合)
    JavaScript If...Else 语句
    一个用纯CSS实现的下拉菜单
    form表单练习
    CSS样式表
    form表单的属性标签
    form表单的属性标签和练习
    HTML基础标签入门
    学习目标
    Oracle表格建立
  • 原文地址:https://www.cnblogs.com/standby/p/14420386.html
Copyright © 2020-2023  润新知