📜  图算法(1)

📅  最后修改于: 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 算法

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