Skip to content

Graph

Topological sorting

Template

对于图 G 中的任意一条有向边 (u,v),u 在排列中都出现在 v 的前面

  • 有环则没有拓扑排序
  • 拓扑排序不唯一 BFS
Q = deque()
for i in range(1, n+1):
    if ind[i] == 0:
      d  Q.append(i)
while Q:
    u = Q.popleft()
    topo.append(u)
    for v in graph[u]:
        ind[v] -= 1
        if ind[v] == 0:
            Q.append(v)
if len(topo_order) != numCourses:
    return[] # 有环

DFS

def dfs(x):
    visited[x] = 1
    for v in edges[x]:
        if visited[v] == 0:
            visited[v] = 1
            if not dfs(v):
                return False
        elif visited[v] == 1:
            return False
    visited[x] = 2
    topo.append(x)

for i in range(numCourses):
    if valid and not visited[i]:
        valid &= dfs(i)

if valid:
    topo.reverse()
    return topo
return []
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph(numCourses);
        vector<int> visited(numCourses, 0);

        for (vector<int> prerequisite : prerequisites) {
            graph[prerequisite[1]].push_back(prerequisite[0]);
        }

        for (int i = 0; i < numCourses; ++i)
            if (!hasCycle(graph, i, visited))
                return false;
        return true;
  }

private:
    bool hasCycle(const vector<vector<int>>& graph, int u, vector<int>& visited) {
        visited[u] = 1;
        for (const int v : graph[u])
            if (visited[v] == 0) {
                visited[v] = 1;
                if (!hasCycle(graph, v, visited)) return false;
            }
            else if (visited[v] == 1) return false;
        visited[u] = 2;
        return true;
    }
};

What I have done

207. Course Schedule
210. Course Schedule II
1462. Course Schedule IV
2192. All Ancestors of a Node in a Directed Acyclic Graph
2392. Build a Matrix With Conditions

with graph 684. Redundant Connection
924. Minimize Malware Spread

Has Cycle

Template

two pointer space complexity -> O(1)

while True:
    slow = next_index(slow)
    fast = next_index(fast)
    if nums[fast] * nums[i] <= 0 or nums[next_index(fast)] * nums[i] <= 0:
        break
    fast = next_index(fast)
    if slow == fast:
        if slow == next_index(slow): 
            break
        return True

What I have done

457. Circular Array Loop🌟
802. Find Eventual Safe States
1591. Strange Printer II🌟
2115. Find All Possible Recipes from Given Supplies

Union-Find

Template

简易版

def find(x):
    if p[x] != x:
        p[x] = find(p[x])
    return p[x]

完整版(按秩)

class UnionFind:
    def __init__(self, size):
        self.parent = list(range(size))  
        self.rank = [1] * size  # height

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x]) 
        return self.parent[x]

    def union(self, x, y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            if self.rank[rootX] > self.rank[rootY]:
                self.parent[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.parent[rootX] = rootY
            else:
                self.parent[rootY] = rootX
                self.rank[rootX] += 1

What I have done

547. Number of Provinces
684. Redundant Connection
685. Redundant Connection II
947. Most Stones Removed with Same Row or
990. Satisfiability of Equality Equations

Shortest Path

Template

Dijkstra

while Q:
    u = Q.popleft()
    for v in graph[u]:
        dist[v] = max(dist[v], dist[u] + time[v])
        ind[v] -= 1
        if ind[v] == 0:
            Q.append(v)

Dijkstra with heap

def Dijkstra(self, graph, src):
    dis = [float('inf')] * len(graph)
    dis[src] = 0
    minHeap = [(dis[src], src)]

    while minHeap:
        d, u = heapq.heappop(minHeap)
        if d > dis[u]:
            continue
        for v, w in graph[u]:
            if d + w < dis[v]:
                dis[v] = d + w
                heapq.heappush(minHeap, (dis[v], v))

What I have done

797. All Paths From Source to Target
1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance
1514. Path with Maximum Probability
1631. Path With Minimum Effort变一下
2050. Parallel Courses III

BFS Variants

What I have done

310. Minimum Height Trees

Tarjan

Template

find cut vertex

dfn = [0] * n
low = [0] * n
ans = []
cur_time = 0

def tarjan(node, parent):
    nonlocal cur_time
    cur_time += 1
    dfn[node] = low[node] = cur_time
    for neighbor in graph[node]:
        if neighbor == parent:
            continue
        if not dfn[neighbor]:
            tarjan(neighbor, node)
            low[node] = min(low[node], low[neighbor])
            if low[neighbor] > dfn[node]:
                ans.append([node, neighbor])
            low[node] = min(low[node], dfn[neighbor])

graph = [[] for _ in range(n)]
for a, b in connections:
    graph[a].append(b)
    graph[b].append(a)
tarjan(0, -1)
return ans  

What I have done

1192. Critical Connections in a Network

MST

Template

Kruskal

def find(self, parent, node):
    if parent[node] == node:
        return node
    parent[node] = self.find(parent, parent[node])
    return parent[node]

def union(self, parent, rank, x, y):
    root_x = self.find(parent, x)
    root_y = self.find(parent, y)

    if root_x != root_y:
        if rank[root_x] < rank[root_y]:
            parent[root_x] = root_y
        elif rank[root_x] > rank[root_y]:
            parent[root_y] = root_x
        else:
            parent[root_y] = root_x
            rank[root_x] += 1

edges = sorted(edges)

parent = list(range(len(points)))
rank = [0] * len(points)
min_cost = 0

for edge in edges:
    distance, u, v = edge
    if self.find(parent, u) != self.find(parent, v):
        min_cost += distance
        self.union(parent, rank, u, v)

What I have done

1584. Min Cost to Connect All Points