Dijkstra算法python的实现(有向图/无向图)

我用Dijkstra算法,写了一个无环有向图/无向图(多加一条相反的路径仅此而已) 的最短路径问题的解决方案。如果是无向图也很简单,把每个无向的edge拆开成两个有向的就可以解决了。

为了每次弹出正确的端点,我也实现了一个最小优先队列。

代码由4个类构成:

Edge定义两个端点之间路径,有三个属性:源,目的地和权重。

Graph定义了整个图形,包括路径和端点。在这个code里面,端点不能单独添加。当添加路径的时候,端点会自动添加。

MinPQ定义一个优先队列,它用来储存端点到起始点的最小距离。它有一个方法del_min,用来弹出队列中距离最小的那个端点。

ShortestPath是主要类,它使用Dijkstra算法,有两个方法:

path_to(vertex)方法返回包含从起点到顶点的最短路径的list 。
main(list_Grph,from_point,to_point) //该方法主要构造 并且输出最短路径经过的节点
list_Grph 使所有边的一个list 下面有例子可以看看
from_point //从哪一个节点出发
to_point //到哪个节点

dist_to(vertex)返回path_to(vertex)的总长度。
class Edge:
    def __init__(self, source, destine, weight):
        self.weight = weight
        self.source = source
        self.destine = destine


class Graph:
    def __init__(self):
        self.vertices = set([])
        self.edges = set([])
        self.adjacents = {}

    def add_edge(self, edge):
        self.vertices.add(edge.source)
        self.vertices.add(edge.destine)
        if edge.source not in self.adjacents.keys():
            self.adjacents[edge.source] = set([])
        self.adjacents[edge.source].add(edge)
        self.edges.add(edge)
        # print("add edge from {} to {}, weight {}".format(edge.source, edge.destine, edge.weight))

    def get_adjacents(self, vertex):
        # print("get the adjacent vertices of vertex {}".format(vertex))
        if vertex not in self.adjacents.keys():
            return set([])
        return self.adjacents[vertex]

    def vertex_number(self):
        return len(self.vertices)

    def edge_number(self):
        return len(self.edges)


class MinPQ:
    def __init__(self):
        self.queue = [(0, 0)]
        # print("create a min Priority Queue to record the distance")

    def is_empty(self):
        return len(self.queue) == 1

    def size(self):
        return len(self.queue) - 1

    def min(self):
        return self.queue[1]

    def insert(self, vertex, new_value):
        self.queue.append((vertex, new_value))
        self.swim(self.size())

    def del_min(self):
        self.queue[1], self.queue[-1] = self.queue[-1], self.queue[1]
        temp = self.queue.pop(-1)
        self.sink(1)
        return temp

    def swim(self, index):
        while index > 1 and self.queue[index // 2][1] > self.queue[index][1]:
            self.queue[index //
                       2], self.queue[index] = self.queue[index], self.queue[
                index // 2]
            index = index // 2

    def sink(self, index):
        while 2 * index <= self.size():
            next_level = 2 * index
            if next_level < self.size() and self.queue[next_level][
                1] > self.queue[next_level + 1][1]:
                next_level += 1
            if self.queue[index][1] <= self.queue[next_level][1]:
                return
            self.queue[index], self.queue[next_level] = self.queue[
                                                            next_level], self.queue[index]
            index = next_level

    def contains(self, vertex):
        for index in range(1, len(self.queue)):
            if self.queue[index][0] == vertex:
                return True
        return False

    def change_dist(self, vertex, new_dist):
        for index in range(1, len(self.queue)):
            if self.queue[index][0] == vertex:
                self.queue[index] = (vertex, new_dist)


class ShortestPath:
    def __init__(self, graph, start_point):
        self.dist_to = {}
        self.edge_to = {}
        for vertex in graph.vertices:
            self.dist_to[vertex] = float("inf")
        self.dist_to[start_point] = start_point
        self.start_point = start_point
        self.dist_queue = MinPQ()
        self.dist_queue.insert(start_point, self.dist_to[start_point])
        # print("insert the start point into the priority queue and initialize the distance")
        while not self.dist_queue.is_empty():
            vertex, _ = self.dist_queue.del_min()
            # print("grow the mini-distance tree by poping vertex {} from the queue".format(vertex))
            for edge in graph.get_adjacents(vertex):
                self.relax(edge)

    def relax(self, edge):
        # print("relax edge from {} to {}".format(edge.source, edge.destine))
        source = edge.source
        destine = edge.destine
        if self.dist_to[destine] > self.dist_to[source] + edge.weight:
            self.dist_to[destine] = self.dist_to[source] + edge.weight
            self.edge_to[destine] = edge
            if self.dist_queue.contains(destine):
                self.dist_queue.change_dist(destine, self.dist_to[destine])
            else:
                self.dist_queue.insert(destine, self.dist_to[destine])

    def dist_to(self, vertex):
        return self.dist_to[vertex]

    def path_to(self, vertex):
        lPath = [vertex]
        temp_vertex = vertex
        while temp_vertex != self.start_point:
            temp_vertex = self.edge_to[temp_vertex].source
            lPath.append(temp_vertex)
        lPath.reverse()
        return lPath


def main(list_Grph,from_point,to_point):
    test = Graph()
    #Create an  graph 
    for item in list_Grph:
          test.add_edge(Edge(item[0], item[1],item[2]))
          # you will need to the next line of code if the graph is a disorderly 
          #test.add_edge(Edge(item[1], item[0],item[2]))
    path = ShortestPath(test, from_point)
    distPath = path.path_to(to_point);
    print "the distPth is :"
    print distPath

def test():
    edge_1=[1,2,1]
    edge_2=[2,5,1]
    edge_3=[2,4,1]
    edge_4=[2,3,2]
    edge_5=[3,4,1]
    edge_6=[3,6,2]
    edge_7=[4,6,1]
    list_edge =[edge_1,edge_2,edge_3,edge_4,edge_5,edge_6,edge_7]
    main(list_edge,5,6)
test()

你可能感兴趣的:(python)