矩阵规则
对角线:元素 = 0
没有边: inf
有边: 权值
结构应该存在的东西
- 是否为有向图和无向图
- 顶点数
- 边数
- 存储矩阵(python:np.narray)
class Graph:
def __init___(self):
self.mat # 存储矩阵
self.n # 存储边数
self.type # 有向图和无向图
self.s # 边数
遍历图 深度搜索 广度搜索
- 二者都需要存在访问标志,这样才可以知道这个点 是否 已经被访问过了!
- 二者都需要用某一个办法获取邻接的节点,然后用上面的访问标志才判断。
1. 深度搜索 DFS
visited = [False] * graph.n
def DFS(graph, v):
visited[v] = True
for i in v.neighbors_node():
if not visited[i]:
DFS(graph, i)
2. 广度搜索 BFS
visited = [False] * graph.n
def BFS(graph, v):
q = Queue() # 初始化队列
visited[v] = True # 访问过了
q.put(v) # 入队
print(v) # 输出顶点
while not q.empty():
i = q.put() # 出列
for j in i.neighbors_node(): # 获取的邻近节点是按照 从最近的顺序开始的
if not visited[j]:
visited[j] =True
q.put(j)
print(j)
路径的保存: 用栈;
3 应用:递归的使用。用来找一条简单路径。
visited = [False] * graph.n
def Simple(graph, v, w, stack):
visited[v] = True
stack.push(v)
for i in v.neighbors_node():
if i == w:
return True
if not visited[i]:
if Simple(graph, i, w, stack):
return True
stack.pop()
最核心的思想,就是一个搜索,把这个点的附近的全部顶点进行递归搜索;没用的路其实也就没戏了,而有用的路径则保留了下来。
函数最终的停止就是i == w
找到了最后的点。
4 应用:两个顶点的最短路径
广度搜索;
因为获得的邻点 是 从最近的开始排序的;
最小生成树 普里姆Prim 克鲁斯卡尔 Kruskal
1. 普里姆Prim
思想: 把图分开成为两部分,一部分是加入顶点集合的,另一部分是外面的。而且加入的除了顶点还有边。直到最后,顶点全部加载进去,一部分n-1个边被加载了进去。最后顶点和n-1个边 组合成为了 最小生成树。适合稠密图
2. 克鲁斯卡尔 Kruskal
思想:按照边的权值进行排序,然后选择一个最短的边。选择之后,要判断是否构成了圆环,如果构成者舍弃,如果没有则使用。边的排序,使用堆排序。环的判断使用了等价类和并查集的思想。适合稀疏图。
最短路径问题
一个源点到其他点的距离,一直更新;
1. 迪科斯特拉 Dijkstra 单源(一点到全部点)
思想: 类比Prim,也是分成了两个集合。一个是加入顶点集合的,剩下的是未加入的。一次只能加入一个点和一条边。也保证了是最短的路径。求最短路径的。
2. 弗洛伊德 Floyd 全源(任意两点)
核心算法:
Dk[i][j] = min{Dk[i][j],Dk[i][k]+Dk[k][j]}
k = 0,1,2,3,n-1
实际上就是寻找已经相连的两个点 是否有另外一个点,走另外一个点,可以有更加简短的路径。