📅  最后修改于: 2023-12-03 15:23:05.414000             🧑  作者: Mango
图算法是指使用图作为数据结构,进行计算和分析的算法。图由节点和边组成,节点表示实体,边表示节点之间的关系。
图算法可以应用于多种问题,例如最短路径、最小生成树、流网络和匹配问题等。以下是一些常用的图算法:
最短路径算法用于计算从一个节点到另一个节点的最短路径。常用的最短路径算法有迪杰斯特拉算法和贝尔曼-福德算法。
迪杰斯特拉算法是一种贪心算法,用于计算从一个节点到其他所有节点的最短路径。该算法依次将与起点相邻的所有节点加入到集合 S 中,并计算它们到起点的距离。然后从 S 集合中选择距离最短的节点,将其移出 S 集合,并更新与其相邻的节点的距离。
import heapq
def dijkstra(graph, start):
distances = {vertex: float('inf') for vertex in graph}
distances[start] = 0
pq = [(0, start)]
while pq:
current_dist, current_vertex = heapq.heappop(pq)
if current_dist > distances[current_vertex]:
continue
for neighbor, weight in graph[current_vertex].items():
distance = current_dist + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distances
贝尔曼-福德算法用于计算从一个节点到其他所有节点的最短路径,与迪杰斯特拉算法不同的是,它可以处理负权边。该算法通过对所有边进行 V - 1 轮松弛操作,计算出所有节点的最短路径。
def bellman_ford(graph, start):
distances = {vertex: float('inf') for vertex in graph}
distances[start] = 0
for _ in range(len(graph) - 1):
for u in graph:
for v, weight in graph[u].items():
if distances[u] + weight < distances[v]:
distances[v] = distances[u] + weight
return distances
最小生成树算法用于计算一张图的最小生成树,即用最少的边连接所有节点。常用的最小生成树算法有克鲁斯卡尔算法和普林姆算法。
克鲁斯卡尔算法是一种贪心算法,用于计算一张图的最小生成树。该算法将所有边按照权值从小到大进行排序,依次考虑每条边,将能够连接两个不同连通块的边加入到最小生成树中。
def kruskal(graph):
parent = {}
rank = {}
for vertex in graph:
parent[vertex] = vertex
rank[vertex] = 0
def find(vertex):
if parent[vertex] != vertex:
parent[vertex] = find(parent[vertex])
return parent[vertex]
def union(v1, v2):
root1 = find(v1)
root2 = find(v2)
if root1 != root2:
if rank[root1] > rank[root2]:
parent[root2] = root1
else:
parent[root1] = root2
if rank[root1] == rank[root2]:
rank[root2] += 1
mst = set()
edges = list(graph.edges())
edges.sort()
for edge in edges:
weight, v1, v2 = edge
if find(v1) != find(v2):
union(v1, v2)
mst.add(edge)
return mst
普林姆算法是一种贪心算法,用于计算一张图的最小生成树。该算法以一个随意顶点开始,并依次将距离该顶点最近的边连接的节点加入生成树中。
import heapq
def prim(graph, start):
mst = set()
visited = set([start])
edges = [(weight, start, neighbor) for neighbor, weight in graph[start].items()]
heapq.heapify(edges)
while edges:
weight, v1, v2 = heapq.heappop(edges)
if v2 not in visited:
visited.add(v2)
mst.add((weight, v1, v2))
for neighbor, weight in graph[v2].items():
if neighbor not in visited:
heapq.heappush(edges, (weight, v2, neighbor))
return mst
流网络算法用于计算流网络的最大流,即从源节点到汇节点的最大流量。常用的流网络算法有福特-福尔克森算法和迪克斯特拉-卡拉菲尔算法。
福特-福尔克森算法是一种用于计算流网络的最大流的算法,通过迭代增广路径来计算最大流。
def bfs(graph, s, t, parent):
visited = [False] * len(graph)
queue = [s]
visited[s] = True
while queue:
u = queue.pop(0)
for ind, val in enumerate(graph[u]):
if not visited[ind] and val > 0:
queue.append(ind)
visited[ind] = True
parent[ind] = u
return visited[ind]
def ford_fulkerson(graph, source, sink):
parent = [-1] * len(graph)
max_flow = 0
while bfs(graph, source, sink, parent):
path_flow = float("Inf")
s = sink
while s != source:
path_flow = min(path_flow, graph[parent[s]][s])
s = parent[s]
max_flow += path_flow
v = sink
while v != source:
u = parent[v]
graph[u][v] -= path_flow
graph[v][u] += path_flow
v = parent[v]
return max_flow
迪克斯特拉-卡拉菲尔算法是一种用于计算流网络的最大流的算法,通过构造残量网络和运行多个最短路径算法来计算最大流。
def bfs(graph, s, t, parent):
visited = [False] * len(graph)
queue = [s]
visited[s] = True
while len(queue) != 0:
u = queue.pop(0)
for ind, val in enumerate(graph[u]):
if not visited[ind] and val > 0:
queue.append(ind)
visited[ind] = True
parent[ind] = u
return visited[t]
def edmonds_karp(graph, source, sink):
parent = [-1] * len(graph)
max_flow = 0
while bfs(graph, source, sink, parent):
path_flow = float("Inf")
s = sink
while q != source:
path_flow = min(path_flow, graph[parent[s]][s])
s = parent[s]
max_flow += path_flow
v = sink
while v != source:
u = parent[v]
graph[u][v] -= path_flow
graph[v][u] += path_flow
v = parent[v]
return max_flow
匹配问题算法用于计算一个二分图的最大匹配,即从左侧顶点集到右侧顶点集的最大匹配数。常用的匹配问题算法有匈牙利算法和 Kuhn-Munkres 算法。
匈牙利算法是一种用于计算二分图的最大匹配的算法,基于增广路径和交替路的思想。
def bpm(bgraph, u, matchR, seen):
for v in range(len(bgraph[0])):
if bgraph[u][v] and seen[v] == False:
seen[v] = True
if matchR[v] == -1 or bpm(bgraph, matchR[v], matchR, seen):
matchR[v] = u
return True
return False
def max_bpm(bgraph):
matchR = [-1] * len(bgraph[0])
result = 0
for i in range(len(bgraph)):
seen = [False] * len(bgraph[0])
if bpm(bgraph, i, matchR, seen):
result += 1
return result
Kuhn-Munkres 算法是一种用于计算二分图的最大匹配的算法,基于交错树和贪心匹配的思想。
def kuhn_munkres(graph):
m = len(graph)
n = len(graph[0])
label = [max([graph[i][j] for j in range(n)]) for i in range(m)]
matchX = [-1] * m
matchY = [-1] * n
slack = [float("Inf")] * n
for x in range(m):
while True:
visited = [False] * n
if dfs(graph, x, label, matchX, matchY, slack, visited):
break
d = min(slack[y] for y in range(n) if not visited[y])
for i in range(m):
if visited[i]:
label[i] -= d
for j in range(n):
if visited[j]:
label[matchY[j]] += d
slack[j] -= d
else:
slack[j] -= d
return matchX
def dfs(graph, x, label, matchX, matchY, slack, visited):
n = len(graph[0])
visited[x] = True
for y in range(n):
if visited[y]:
continue
if label[x] + label[matchY[y]] == graph[x][y]:
visited[y] = True
if matchY[y] == -1 or dfs(graph, matchY[y], label, matchX, matchY, slack, visited):
matchX[x] = y
matchY[y] = x
return True
else:
slack[y] = min(slack[y], label[x] + label[matchY[y]] - graph[x][y])
return False