【算法——Python实现】有负权图求单源最短路径BellmanFord算法

来源:互联网 发布:java读取blob字段 编辑:程序博客网 时间:2024/05/18 01:29
class Edge(object):    """边"""    def __init__(self, a, b, weight):        self.a = a # 第一个顶点        self.b = b # 第二个顶点        self.weight = weight # 权值    def v(self):        return self.a    def w(self):        return self.b    def wt(self):        return self.weight    def other(self, x):        # 返回x顶点连接的另一个顶点        if x == self.a or x == self.b:            if x == self.a:                return self.b            else:                return self.a    def __lt__(self, other):        # 小于号重载        return self.weight < other.wt()    def __le__(self, other):        # 小于等于号重载        return self.weight <= other.wt()    def __gt__(self, other):        # 大于号重载        return self.weight > other.wt()    def __ge__(self, other):        # 大于等于号重载        return self.weight >= other.wt()    def __eq__(self, other):        # ==号重载        return self.weight == other.wt()class DenseGraph(object):    """有权稠密图 - 邻接矩阵"""    def __init__(self, n, directed):        self.n = n  # 图中的点数        self.m = 0  # 图中的边数        self.directed = directed  # bool值,表示是否为有向图        self.g = [[None for _ in range(n)] for _ in range(n)]  # 矩阵初始化都为None的二维矩阵    def V(self):        # 返回图中点数        return self.n    def E(self):        # 返回图中边数        return self.m    def addEdge(self, v, w, weight):        # v和w中增加一条边,v和w都是[0,n-1]区间        if v >= 0 and v < n and w >= 0 and w < n:            if self.hasEdge(v, w):                self.m -= 1            self.g[v][w] = Edge(v, w, weight)            if not self.directed:                self.g[w][v] = Edge(w, v, weight)            self.m += 1    def hasEdge(self, v, w):        # v和w之间是否有边,v和w都是[0,n-1]区间        if v >= 0 and v < n and w >= 0 and w < n:            return self.g[v][w] != None    class adjIterator(object):        """相邻节点迭代器"""        def __init__(self, graph, v):            self.G = graph  # 需要遍历的图            self.v = v  # 遍历v节点相邻的边            self.index = 0  # 遍历的索引        def __iter__(self):            return self        def next(self):            while self.index < self.G.V():                # 当索引小于节点数量时遍历,否则为遍历完成,停止迭代                if self.G.g[self.v][self.index]:                    r = self.G.g[self.v][self.index]                    self.index += 1                    return r                self.index += 1            raise StopIteration()class SparseGraph(object):    """有权稀疏图- 邻接表"""    def __init__(self, n, directed):        self.n = n  # 图中的点数        self.m = 0  # 图中的边数        self.directed = directed  # bool值,表示是否为有向图        self.g = [[] for _ in range(n)]  # 矩阵初始化都为空的二维矩阵    def V(self):        # 返回图中点数        return self.n    def E(self):        # 返回图中边数        return self.m    def addEdge(self, v, w, weight):        # v和w中增加一条边,v和w都是[0,n-1]区间        if v >= 0 and v < n and w >= 0 and w < n:            # 考虑到平行边会让时间复杂度变为最差为O(n)            # if self.hasEdge(v, w):            #   return None            self.g[v].append(Edge(v, w, weight))            if v != w and not self.directed:                self.g[w].append(Edge(w, v, weight))            self.m += 1    def hasEdge(self, v, w):        # v和w之间是否有边,v和w都是[0,n-1]区间        # 时间复杂度最差为O(n)        if v >= 0 and v < n and w >= 0 and w < n:            for i in self.g[v]:                if i.other(v) == w:                    return True            return False    class adjIterator(object):        """相邻节点迭代器"""        def __init__(self, graph, v):            self.G = graph  # 需要遍历的图            self.v = v  # 遍历v节点相邻的边            self.index = 0  # 遍历的索引        def __iter__(self):            return self        def next(self):            if len(self.G.g[self.v]):                # v有相邻节点才遍历                if self.index < len(self.G.g[self.v]):                    r = self.G.g[self.v][self.index]                    self.index += 1                    return r                else:                    raise StopIteration()            else:                raise StopIteration()class ReadGraph(object):    """读取文件中的图"""    def __init__(self, graph, filename):        with open(filename, 'r') as f:            line = f.readline()            line = line.strip('\n')            line = line.split()            v = int(line[0])            e = int(line[1])            if v == graph.V():                lines = f.readlines()                for i in lines:                    a, b, w = self.stringstream(i)                    if a >= 0 and a < v and b >=0 and b < v:                        graph.addEdge(a, b, w)    def stringstream(self, text):        result = text.strip('\n')        result = result.split()        a, b, w = result        return int(a), int(b), int(w)class BellmanFord(object):    """BellmanFord求单源最短路径,可以有负权值。多次执行松弛操作,一点到一点最短路径最多有V-1条边V个顶点,松弛时超过这个数则证明有负权环,无最短路径    只能处理有向图,无向图如果两个点之间权值都为负会直接形成负权环"""    def __init__(self, graph, s):        self.G = graph  # 图        self.s = s  # 原点s        self.distTo = [0 for _ in range(self.G.V())]  # 从原点到每个节点的权值,初始都为0        self.fromed = [None for _ in range(self.G.V())] # 记录此节点来自哪个节点的连接,存储边。None表示未被访问过        self.hasNegativeCircle = False  # 是否有负权环        # BellmanFord        # 共对所有顶点做V-1次松弛        for p in range(self.G.V()-1):            # 每一轮松弛要对所有节点都做松弛操作            for i in range(self.G.V()):                # 只对原点s或者其他已经到达的顶点做松弛操作                if self.fromed[i] or self.s == i:                    adj = self.G.adjIterator(self.G, i)                    for e in adj:                        # 如果w还未被访问过,或经过v的最短路径再到w节点的权值小于w之前被访问时记录的路径,则用经过v再到w的路径替代                        if not self.fromed[e.w()] or self.distTo[i] + e.wt() <  self.distTo[e.w()]:                            self.distTo[e.w()] = self.distTo[i] + e.wt()                            self.fromed[e.w()] = e        self.hasNegativeCircle = self.detectNegativeCircle()    def detectNegativeCircle(self):        # 再做一轮对所有顶点的松弛操作,如果还能松弛则表示有负权环        for i in range(self.G.V()):            # 只对原点s或者其他已经到达的顶点做松弛操作            if self.fromed[i] or self.s == i:                adj = self.G.adjIterator(self.G, i)                for e in adj:                    # 如果w还未被访问过,或经过v的最短路径再到w节点的权值小于w之前被访问时记录的路径,则表示一定有负权环                    if not self.fromed[e.w()] or self.distTo[i] + e.wt() <  self.distTo[e.w()]:                        return True        return False    def negativeCycle(self):        # 查询是否有负权环        return self.hasNegativeCircle    def shortestPathTo(self, w):        # 从原点到w节点的权值        if not self.hasNegativeCycle and w >= 0 and w < self.G.V():            return self.distTo[w]        else:            return None    def hasPathTo(self, w):        # 从原点到w是否有路径        if w >= 0 and w < self.G.V():            return self.fromed[w] != None    def shortestPath(self, w):        # 从原点到w的最短路径        if not self.hasNegativeCycle and w >= 0 and w < self.G.V():            s = []            e = self.fromed[w]            while e:                s.append(e)                e = self.fromed[e.v()]            s.reverse()            return s        else:            return None    def showPath(self, w):        # 输出路径        if not self.hasNegativeCycle and w >= 0 and w < self.G.V():            s = self.shortestPath(w)            i = 0            while i < len(s):                print s[i].v()                if i == len(s) - 1:                    print s[i].w()                i += 1
阅读全文
0 0
原创粉丝点击