|-图的表示
邻接矩阵:适合表示稠密图(完全图)
邻接表:适合表示稀疏图
|-图的遍历
深度优先遍历
可以用于计算图的连通分量个数
寻路: 定义一个from数组指向该点的前一个节点
复杂度:
邻接表-O(n+e)
邻接矩阵-O(n^2)
广度优先遍历
最短路径
复杂度:
邻接表-O(n+e)
邻接矩阵:适合表示稠密图(完全图)
邻接表:适合表示稀疏图
|-图的遍历
深度优先遍历
可以用于计算图的连通分量个数
寻路: 定义一个from数组指向该点的前一个节点
复杂度:
邻接表-O(n+e)
邻接矩阵-O(n^2)
广度优先遍历
最短路径
复杂度:
邻接表-O(n+e)
邻接矩阵-O(n^2)
源代码:
https://github.com/lzneu/Algrithm_python
# 邻接矩阵实现稠密图
class DenseGraph:
def __init__(self, n, directed):
self.__m = 0 # 边数
self.__n = n # 顶点个数
self.__directed = directed # 是否有向图
self.g = []
for i in range(n):
self.g.append(list(False for vec in range(n)))
def V(self):
return self.__n
def E(self):
return self.__m
def addEdge(self, v, w):
assert 0 <= v < self.__n
assert 0 <= w < self.__n
# 若已经存在边
if self.hasEdge(v, w):
return
self.g[v][w] = True
if (not self.__directed):
# 非有向图
self.g[w][v] = True
self.__m += 1
def hasEdge(self, v, w):
assert (v >= 0 and v < self.__n)
assert (w >= 0 and w < self.__n)
return self.g[v][w]
# 打印邻接矩阵
def show(self):
for v in range(self.__n):
print(('vertex%d: %s') % (v, str(self.g[v])))
# 返回v节点的邻接节点
def getAdj(self, v):
retList = []
adj_list = self.g[v]
for i in range(len(adj_list)):
if adj_list[i] == True:
retList.append(i)
return retList
# 稀疏图
class SparseGraph:
def __init__(self, n, directed):
self.__n = n
self.__m = 0
self.__directed = directed
self.g = []
for i in range(n):
self.g.append([])
def V(self):
return self.__n
def E(self):
return self.__m
def addEdge(self, v ,w):
assert (v >= 0 and v < self.__n)
assert (w >= 0 and w < self.__n)
self.g[v].append(w)
if (v != w and not self.__directed):
self.g[w].append(v)
self.__m += 1
def hasEdge(self, v, w):
assert (v >= 0 and v < self.__n)
assert (w >= 0 and w < self.__n)
# # 不包平行边处理 复杂度 O(N) 一般单独处理 此处注释
# if self.hasEdge(v, w):
# return
for i in range(self.__n):
if w == self.g[v][i]:
return True
else:
return False
# 打印邻接表
def show(self):
for v in range(self.__n):
print(('vertex%d: %s')% (v, str(self.g[v])))
# 返回v节点的邻接节点
def getAdj(self, v):
return self.g[v]
# 读取文件生成图数据结构的类
class ReadgRraph:
# 从文件filename中读取图的信息, 存储进图graph中
def __init__(self, graph, filename):
with open(filename, 'r') as f:
lines = f.readlines()
f.close()
assert len(lines) >= 1, '文件格式错误'
self.V = int(lines[0].split(' ')[0])
self.E = int(lines[0].split(' ')[1])
edges = []
for line in lines[1:]:
edges.append(line.split(' '))
assert self.V == graph.V(), '文件和图的顶点个数不一致'
# 读取每一条边的信息
for edge in edges:
a = int(edge[0])
b = int(edge[1])
assert 0 <= a < self.V
assert 0 <= a < self.V
graph.addEdge(a, b)
# 图的深度优先遍历类
class Component:
# 构造函数 求出无权图的联通分量
def __init__(self, graph):
self.__graph = graph
self.__ccount = 0 # 记录连通分量的个数
self.__visited = [] # 记录dfs过程中节点是否被访问
self.__id = [] # 每个节点对应的连通分量的标记
# 初始化两个指示数组
for i in range(self.__graph.V()):
self.__visited.append(False)
self.__id.append(-1)
# 求图的连通分量
for i in range(self.__graph.V()):
if not self.__visited[i]:
self.__dfs(i)
self.__ccount += 1
# 图的深度优先遍历
def __dfs(self, v):
self.__visited[v] = True
self.__id[v] = self.__ccount
for i in self.__graph.getAdj(v):
# i为v节点的临接点
if not self.__visited[i]:
self.__dfs(i)
# 返回图的连通分量个数
def count(self):
return self.__ccount
# 查询点v和是否联通
def isConnect(self, v, w):
assert v >= 0 and v < self.__graph.V()
assert w >= 0 and w < self.__graph.V()
return self.__id[v] == self.__id[w]
# 利用深度优先遍历进行图的寻路
class Path:
# 构造函数 求出从s到任意一个点的路径
def __init__(self, graph, s):
assert 0 <= s < graph.V(), '源结点不在图中'
self.__graph = graph
self.__s = s # 记录连通图的源节点
self.__visited = [] # 记录dfs过程中节点是否被访问
self.__from = [] # 每个结点从哪个结点过来
# 初始化两个指示数组
for i in range(self.__graph.V()):
self.__visited.append(False)
self.__from.append(-1)
# 寻路算法
self.__dfs(s)
# 图的深度优先遍历
def __dfs(self, v):
self.__visited[v] = True
for i in self.__graph.getAdj(v):
# i为v节点的临接点
if not self.__visited[i]:
self.__from[i] = v
self.__dfs(i)
# 查询从s结点到w结点是否有路径
def hasPath(self, w):
assert 0 <= w < self.__graph.V(), 'w结点不在图中'
return self.__visited[w] # 如果该为True 证明从s访问过w
# 查询从s到w的路径,返回一个存放路径的list
def path(self, w):
assert self.hasPath(w), '从源结点到该结点不存在路径'
# 通过from数组你想查找从s到w的路径 存放到栈中 此处用list作为一个栈
p = w
stack = []
while p != -1:
stack.append(p)
p = self.__from[p]
stack.reverse()
return stack
def showPath(self, w):
retVec = self.path(w)
for i in range(len(retVec)-1):
print(retVec[i], end='-')
print(retVec[-1])
from queue import Queue
# 寻找无权图的最短路径
class ShortestPath:
def __init__(self, graph, s):
self.__graph = graph
assert 0 <= s < self.__graph.V()
self.__s = s
self.__visited = []
self.__from = []
self.__ord = [] # 该点到源结点的最短路径长度
for i in range(self.__graph.V()):
self.__visited.append(False)
self.__from.append(-1)
self.__ord.append(-1)
# 无向图最短路径算法,从s开始广度优先遍历整张图
q = Queue()
q.put(self.__s)
self.__visited[self.__s] = True
self.__ord[self.__s] = 0
while not q.empty():
v = q.get() # 出队一个结点
for i in self.__graph.getAdj(v):
# i为结点v的邻接节点
if not self.__visited[i]:
# 没有遍历该节点
q.put(i)
self.__visited[i] = True
self.__from[i] = v
self.__ord[i] = self.__ord[v] + 1
def hasPath(self, w):
assert 0 <= w < self.__graph.V()
return self.__visited[w]
def path(self, w):
assert 0 <= w < self.__graph.V()
stack = []
p = w
while p != -1:
stack.append(p)
p = self.__from[p]
# 依此取出stack中的元素
stack.reverse()
return stack
def showPath(self, w):
retVec = self.path(w)
for i in range(len(retVec)-1):
print(retVec[i], end='-')
print(retVec[-1])
def length(self, w):
assert 0 <= w < self.__graph.V()
return self.__ord[w]
if __name__ == '__main__':
filename = './data/testG1.txt'
g1 = SparseGraph(13, False)
readGraph1 = ReadgRraph(g1, filename)
g1.show()
component1 = Component(g1)
print(component1.count())
filename2 = './data/testG.txt'
g2 = SparseGraph(7, False)
readGraph2 = ReadgRraph(g2, filename2)
g2.show()
component2 = Component(g2)
print(component2.count())
path2 = Path(g2, 0)
path2.showPath(6)
path3 = ShortestPath(g2, 0)
path3.showPath(6)