Dijkstra路径搜索算法

原文见:https://dev.to/mxl/dijkstras-algorithm-in-python-algorithms-for-beginners-dkc

本文介绍Dijkstra算法的Python编程实现,对原文程序添加了个人的部分理解,同时完成《无人驾驶汽车概论》一书中第137页中的Dijkstra算法例子的实现。

 

from collections import deque, namedtuple
# deque
# 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,
# 插入和删除效率很低。
#
# deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
#
# >>> from collections import deque
# >>> q = deque(['a', 'b', 'c'])
# >>> q.append('x')
# >>> q.appendleft('y')
# >>> q
# deque(['y', 'a', 'b', 'c', 'x'])
# deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。

# we'll use infinity as a default distance to nodes.
inf = float('inf')
Edge = namedtuple('Edge', 'start, end, cost')
# namedtuple('name', 'attr1,attr2')

def make_edge(start, end, cost=1):
  return Edge(start, end, cost)


class Graph:
    def __init__(self, edges):
        # let's check that the data is right
        wrong_edges = [i for i in edges if len(i) not in [2, 3]]
        if wrong_edges:
            raise ValueError('Wrong edges data: {}'.format(wrong_edges))

        self.edges = [make_edge(*edge) for edge in edges]
        # 如:*parameter是用来接受任意多个参数并将其放在一个元组中。
            #
            # >>> def demo(*p):
            #     print(p)
        # demo(1, 2, 3)
        #     (1, 2, 3)
    @property
    def vertices(self):
        #find all point
        # vertice: top point
        return set(
        # this piece of magic turns ([1,2], [3,4]) into [1, 2, 3, 4]
        # the set above makes it's elements unique.
            sum(
                ([edge.start, edge.end] for edge in self.edges), []
            )
        )

    def get_node_pairs(self, n1, n2, both_ends=True):
        # both_ends judge if n1 and n2 can all approach
        if both_ends:
            node_pairs = [[n1, n2], [n2, n1]]
        else:
            node_pairs = [[n1, n2]]
        return node_pairs

    def remove_edge(self, n1, n2, both_ends=True):
        # remove edges in aim_collection
        node_pairs = self.get_node_pairs(n1, n2, both_ends)
        edges = self.edges[:]
        for edge in edges:
            if [edge.start, edge.end] in node_pairs:
                self.edges.remove(edge)

    def add_edge(self, n1, n2, cost=1, both_ends=True):
        # add edges in aim_collection
        node_pairs = self.get_node_pairs(n1, n2, both_ends)
        for edge in self.edges:
            if [edge.start, edge.end] in node_pairs:
                return ValueError('Edge {} {} already exists'.format(n1, n2))

        self.edges.append(Edge(start=n1, end=n2, cost=cost))
        if both_ends:
            self.edges.append(Edge(start=n2, end=n1, cost=cost))

    @property
    def neighbours(self):
        # return self's neighbour nodes
        neighbours = {vertex: set() for vertex in self.vertices}
        for edge in self.edges:
            neighbours[edge.start].add((edge.end, edge.cost))

        return neighbours

    def dijkstra(self, source, dest):
        # judge if the source is in vertices, if not print 'Such source node doesn\'t exist'
        assert source in self.vertices, 'Such source node doesn\'t exist'
        # 1. Mark all nodes unvisited and store them.
        # 2. Set the distance to zero for our initial node
        # and to infinity for other nodes.

        distances = {vertex: inf for vertex in self.vertices}
        previous_vertices = {
            vertex: None for vertex in self.vertices
        }
        # print(previous_vertices)


        distances[source] = 0

        vertices = self.vertices.copy()

        while vertices:
            # 3. Select the unvisited node with the smallest distance,
            # it's current node now.
            current_vertex = min(
                vertices, key=lambda vertex: distances[vertex])
            # print(current_vertex)
            # 6. Stop, if the smallest distance
            # among the unvisited nodes is infinity.
            if distances[current_vertex] == inf:
                break

            # 4. Find unvisited neighbors for the current node
            # and calculate their distances through the current node.
            # record previous point with the shortest routine
            for neighbour, cost in self.neighbours[current_vertex]:
                alternative_route = distances[current_vertex] + cost
                # print(distances[current_vertex])
                # Compare the newly calculated distance to the assigned
                # and save the smaller one.
                if alternative_route < distances[neighbour]:
                    distances[neighbour] = alternative_route
                    # record previous point
                    previous_vertices[neighbour] = current_vertex
                # print(distances)

            # 5. Mark the current node as visited
            # and remove it from the unvisited set.
            vertices.remove(current_vertex)
        # print(previous_vertices)
        path, current_vertex = deque(), dest
        length = distances[current_vertex]
        while previous_vertices[current_vertex] is not None:
            path.appendleft(current_vertex)
            current_vertex = previous_vertices[current_vertex]

        if path:
            path.appendleft(current_vertex)
        return path, length


graph = Graph([
    ("a", "c", 10),  ("a", "e", 30), ("a", "f", 100),
    ("b", "c", 5), ("c", "d", 50), ("e", "f", 60),  ("e", "d", 20),('d','f',10)])

print(graph.dijkstra("a", "f"))
# print(graph.neighbours)
# print(graph.vertices)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值