圖論 - 拓撲排序

簡介

給定一個包含 n 個節點的有向圖 G ,我們給出它的節點編號的一種排列,如果滿足:
對於圖 G 中的任意一條有向邊 (u, v),u 在排列中都出現在 v 的前面。
那麼稱該排列是圖 G 的「拓撲排序」。根據上述的定義,我們可以得出兩個結論:

  • 如果圖 G 中存在環,那麼不存在拓撲排序
  • 拓撲排序可能不止一種

代碼實現

結合LeetCode的一道題目來實現拓撲排序的代碼:課程表
在這裏插入圖片描述

方法一:廣度優先搜索

思路:找出入度爲0的結點,刪去他引出的邊,將他相鄰的新的入度爲0的點加入隊列,重複此操作。

時間複雜度爲:O(n + m) (n爲結點數,m爲有向邊數)

using System;
using System.Collections.Generic;

public class Solution {
    // 鏈式前向星
    struct Edge {
        public int dest, next;
    }

    int n;
    int edgesCnt = 0;
    Edge[] edges = new Edge[100000];
    int[] head = new int[100000];

    // 入度
    int[] inDegrees = new int[100000];

    void AddEdge(int src, int dest) {
        ++edgesCnt;
        edges[edgesCnt].dest = dest;
        edges[edgesCnt].next = head[src];
        head[src] = edgesCnt;
        ++inDegrees[dest];
    }

    int[] TopoSort() {
        int[] res = new int[n];
        Queue<int> q = new Queue<int>();

        // 將入度爲0的結點加入隊列中
        for (int i = 0; i < n; ++i) {
            if (inDegrees[i] == 0) {
                q.Enqueue(i);
            }
        }

        // 廣度優先搜索
        int resCount = 0;
        while (q.Count > 0) {
            int node = q.Dequeue();
            res[resCount++] = node;
            for (int i = head[node]; i > 0; i = edges[i].next) {
                int dest = edges[i].dest;
                --inDegrees[dest];
                if (inDegrees[dest] == 0) {
                    q.Enqueue(dest);
                }
            }
        }

        if (resCount == n) {
            return res;
        } else {
            return new int[0];
        }
    }

    public int[] FindOrder(int numCourses, int[][] prerequisites) {
        n = numCourses;
        foreach (int[] edge in prerequisites) {
            AddEdge(edge[1], edge[0]);
        }
        return TopoSort();
    }
}

方法二:深度優先搜索

思路:對於一個節點 u,如果它的所有相鄰節點都已經搜索完成,那麼在搜索回溯到 u 的時候,u 本身也會變成一個已經搜索完成的節點,將 u 入棧。全部搜索完畢後依次出棧即可。

時間複雜度:O(n + m) (n爲結點數,m爲有向邊數)

using System;
using System.Collections.Generic;

public class Solution {
    // 鏈式前向星
    struct Edge {
        public int dest, next;
    }

    int n;
    int edgesCnt = 0;
    Edge[] edges = new Edge[100000];
    int[] head = new int[100000];

    void AddEdge(int src, int dest) {
        ++edgesCnt;
        edges[edgesCnt].dest = dest;
        edges[edgesCnt].next = head[src];
        head[src] = edgesCnt;
    }

    // visited數組,用於DFS
    // 0:未搜索  1:已搜索  -1:正在搜索
    int[] visited = new int[100000];

    // 存放搜索序列
    Stack<int> sequence = new Stack<int>();

    bool DFS(int node) {
        // 將結點標記爲正在搜索
        visited[node] = -1;

        for (int i = head[node]; i > 0; i = edges[i].next) {
            int dest = edges[i].dest;
            if (visited[dest] == -1) {  // 有環
                return false;
            } else if (visited[dest] == 0 && !DFS(dest)) {
                return false;
            }
        }

        // 搜索完後標記爲已搜索
        visited[node] = 1;

        sequence.Push(node);

        return true;
    }

    public int[] FindOrder(int numCourses, int[][] prerequisites) {
        n = numCourses;
        foreach (int[] edge in prerequisites) {
            AddEdge(edge[1], edge[0]);
        }
        for (int i = 0; i < n; ++i) {
            if (visited[i] == 0) {
                if (!DFS(i)) {
                    return new int[0];
                }
            }
        }
        if (sequence.Count == n) {
            int[] res = new int[n];
            for (int i = 0; i < n; ++i) {
                res[i] = sequence.Pop();
            }
            return res;
        } else {
            return new int[0];
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章