Python 图算法

    xiaoxiao2022-07-07  219

    inf = float("inf") # inf的值大于任何float类型的值 class GraphError(ValueError): pass

    邻接矩阵实现

    class Graph: def __init__(self, mat, unconn=0): vnum = len(mat) for x in mat: if len(x) != vnum: # 检查是否为方阵 raise ValueError("Argument for 'Graph' . ") self._mat = [mat[i][:] for i in range(vnum)] # 做拷贝 self._unconn = unconn self._vnum = vnum def vertex_num(self): return self._vnum def _invalid(self, v): return 0 > v or v >= self._vnum def add_vertex(self): raise GraphError( \ "Adj-Matrix dose not support 'add_vertex' . ") def add_edge(self, vi, vj, val=1): if self._invalid(vi) or self._invalid(vj): raise GraphError(str(vi) + 'or' + str(vj) + / " is not a valid verdex. ") self._mat[vi][vj] = val def get_edge(self, vi, vj): if self._invalid(vi) or self._invalid(vj): raise GraphError(str(vi) + 'or' + str(vj) + / " is not a valid verdex. ") return self._mat[vi][vj] def out_edges(self, vi): # 获得从v出发的所有边 raise GraphError( \ "Adj-Matrix dose not support 'add_vertex' . ") return self._out_edges(self._mat[vi], self._unconn) @staticmethod def _out_edges(row, unconn): edges = [] for i in range(len(row)): if row[i] != unconn: edges.append((i, row[i])) return edges def __str__(self): return "[\n" + ",\n".join(map(str, self._mat)) + "\n"\ + "\nUnconnected: " + str(self._unconn)

     

    邻接表实现

    class GraphAL(Graph): def __init__(self, mat=[], unconn=0): vnum = len(mat) for x in mat: if len(x) != vnum: raise ValueError("Argument for 'GraphAL' .") self._mat = [Graph._out_edges(mat[i], unconn) \ for i in range(vnum)] self._vnum = vnum self._unconn = unconn def add_vertex(self): self._mat.append([]) self._vnum += 1 return self._vnum - 1 def add_edge(self, vi, vj, val = 1): if self._vnum == 0: raise GraphError("Cannot add edge to empty graph.") if self._invalid(vi) or self._invalid(vj): raise GraphError(str(vi) + 'or' + str(vj) + / " is not a valid verdex. ") row = self._mat[vi] i = 0 while i < len(row): if row[i][0] == vj: self._mat[vi][i] = (vj, val) return if row[i][0] > vj: break i += 1 self._mat[vi].insert(i, (vj, val)) def get_edge(self, vi, vj): if self._invalid(vi) or self._invalid(vj): raise GraphError(str(vi) + 'or' + str(vj) + / " is not a valid verdex. ") for i, val in self._mat[vi]: if i == vj: return val return self._unconn def out_edges(self, vi): if self._invalid(vi): raise GraphError(str(vi) + \ " is not a valid vertex. ") return self._mat[vi]

     

    深度优先遍历的非递归算法

    def DFS_graph(graph, v0): vnum = graph.vertex_num() visited = [0]*vnum visited[v0] = 1 DFS_seq = [v0] st = SStack() st.push((0, graph.out_edges(v0))) while not st.is_empty(): i, edges = st.pop() if i < len(edges): v, e = edges[i] st.push((i+1, edges)) if not visited[v]: DFS_seq.append(v) visited[v] = 1 st.push((0, graph.out_edges(v))) return DFS_seq

     

    最小生成树

    ''' Kruskal T = (v, {}) while T中所含边数小于n-1 从E中选取当前最小边(u, v), 将它从E中删除 if (u, v)两端点属于T的不同连通分量: 将边(u, v)加入T ''' def Kruskal(graph): vnum = graph.vertex_num() reps = [i for i in range(vnum)] mst, edges = [], [] for vi in range(vnum): # 所有边加入edges for v, w in graph.out_edges(vi): edges.append((w, vi, v)) edges.sort() # 边按权值排序, O(nlogn) for w, vi, vj in edges: if reps[vi] != reps[vj] # 两端点属于不同连通分量 mst.append(((vi, vj), w)) # 记录边 if len(mst) == vnum - 1: break rep, orep = reps[vi], reps[vj] for i in range(vnum): # 合并连通分量, 统一代表元 if reps[i] == orep: reps[i] = rep return mst ''' Prim *初始吧(0,0,0)放入优先队列,表示从顶点0到其自身的长为0的边 *循环在第一次迭代中先把顶点0记入最小生成树顶点集U,方法是设置元素 mst[0] = ((0,0),0). 然后把顶点0到其余顶点的边按权值存入优先 队列cands,表示待考察的候选边集合 *随后的循环执行中反复选择cands里记录的最短边(u, v)。 如果确定了它 是连接U中顶点与V-U顶点的边,就把这条边及其权重记入mst[v], 并 把v的出边存入cands;否则直接丢掉。 ''' def Prim(graph): vnum = graph.vertex_num() mst = [None]*vnum cands = PrioQueue([(0, 0, 0)]) count = 0 while count < vnum and not cands.is_empty(): w, u, v = cands.dequeue() # 取当时的最短边 if mst[v]: continue # 邻接点v已经在mst上,继续 mst[v] = ((u, v), w) # 记录新的MST边和顶点 count += 1 for vi, w in graph.out_edges(): # 考虑v的邻接顶点vi if not mst[vi]: cands.enqueue((w, v, vi)) return mst

     

    最短路径

    ''' Dijkstra算法 找图G中从顶点v0到其余顶点最短路径 初始: 1.在集合U中放入顶点v0,v0到v0的距离为0 2.对V-U里的每个顶点,如果(v0, v)属于E(即存在直接的边),则到v的已知最短路径 长度设为w(v0,v),否则令v的已知最短路径长度为无穷大。这里的w(v0,v)是从v0到v 的边的权值 反复做: 1.从V-U中选出当时已知最短路径长度最小的顶点vmin加入U,因为这时间到vmin的已知 最短路径长度cdis(v0,vmin)就是v0到vmin的距离 2.由于vmin的加入,V-U中某些顶点的已知最短路径可能改变。如果从v0经过vmin到v` 的路径比原来已知的最短路径更短,就说明发现了到v`的新的已知最短路径(及其长度) 该路径经过vmin到v`。在这种情况下更新到v`的已知最短路径以及距离的记录,保证下 面能正确地继续从V-U中选择顶点。 反复选择顶点并更新到所有非U顶点的最短路径信息,直到从v0可达的所有顶点都在集合U中 为止。如果这时还存在未加入U的顶点,就说明被处理的图不连通(对于有向图,是存在从v0 出发不可达的顶点) ''' def dijkstra_shortest_paths(graph, v0): vnum = graph.vertex_num() assert 0 <= v0 < vnum paths = [None]*vnum count = 0 cands = PrioQueue([(0, v0, v0)]) # 初始优先队列 while count < vnum and not cands.is_empty(): plen, u, vmin = cands.dequeue() # 取路径最短顶点 if paths[vmin]: # 如果其最短路径已知则继续 continue paths[vmin] = (u, plen) # 记录新确定的最短路径 for v, w in graph.out_edges(vmin): # 考察经由新U顶点的路径 if not paths[v]: # 是到尚未知最短路径的顶点的路径,记录它 cands.enqueue((plen + w, vmin, v)) count += 1 return paths

     

    拓扑排序算法

    ''' 1.从N中选出一个入度为0的顶点作为序列的下一个顶点 2.从N网中删除所选顶点及其所有的出边 3.反复执行上面的操作,直到已经选出了图中的所有顶点,或者再也找不到入度非0的顶点时算法结束 ''' def toposort(graph): vnum = graph.vertex_num() indegree. toposeq = [0]*vnum, [] zerov = -1 for vi in range(vnum): # 建立初始的入度表 for v, w in graph.out_edges(vi): indegree[v] += 1 for vi in range(vnum): # 建立初始的0度表 if indegree[vi] == 0: indegree[vi] = zerov zerov = vi for n in range(vnum): if zerov == -1: # 不存在拓扑序列 return False vi = zerov # 从0度表弹出顶点vi zerov = indegree[zerov] toposeq.append(vi) # 把一个vi加入拓扑序列 for v, w in graph.out_edges(vi): indegree[v] -= 1 if indegree[v] == 0: indegree[v] == zerov zerov = v return toposeq

     

    最新回复(0)