--- tags: Coding --- # 簡介 [參考資料](https://alrightchiu.github.io/SecondRound/graph-introjian-jie.html) ### 表示方法 ![](https://github.com/alrightchiu/SecondRound/blob/master/content/Algorithms%20and%20Data%20Structures/Graph%20series/Intro_fig/f3.png?raw=true =500x) ![](https://github.com/alrightchiu/SecondRound/blob/master/content/Algorithms%20and%20Data%20Structures/Graph%20series/Intro_fig/f4.png?raw=true =500x) ### 儲存方法 無相圖就是每個邊都是雙向的 ![image](https://hackmd.io/_uploads/HJSPDru2ye.png =500x) ### 子圖 G1、G2都是子圖 可以自己定義範圍 ![image](https://hackmd.io/_uploads/B1TwuSdhyl.png =500x) ### 最短路逕 若一條path中,除了起點vertex與終點vertex之外,沒有vertex被重複經過,則稱這條path為simple path。 圖中,path:X-Y-Z即為simple path,path:W-Y-Z-V-W也是simple path,即使W有重複,但是因為分別是起點與終點,所以仍符合定義。而path:Y-X-Y-W就不是simple path,因為第二次經過Y時,Y不是終點。 ![image](https://hackmd.io/_uploads/rywUKHOnJx.png =500x) 圖六 ### 環狀與樹狀 環狀:有環 樹狀:無還 圖六中的path:W-Y-Z-V-W,稱為directed cycle(有向循環); ![image](https://hackmd.io/_uploads/ryx6dru3kx.png =500x) ### 權重 ![image](https://hackmd.io/_uploads/SyGeqB_3kg.png =500x) ### connect 兩個點互相任意在一個圖互通 但中間可以有其他點 ![image](https://hackmd.io/_uploads/S1v7qH_nJl.png =500x) **connected : (undirected graph)** 對任意兩個vertex都存在一條path(中間可以有多個點)連結這兩個vertex,則稱此undirected graph是connected。 1.圖1中,G1中的所有vertex都可以經過一條path到達其他vertex,因此G1為connected。 2.G2中,vertex:X、S、Z分別與vertex:Y、W、T之間皆不存在path,因此G2不是connected。 **connected component:(undirected graph最大的connect component)** 在一個connect的子圖中不能加入任意的邊或是點之後,使子圖還是維持任意兩個點還是connect。稱此subgraph為connected component **(最大集合的connected subgraph)**。 **注意:** component一定要最大的才成立 * 右上方為G1的其中一個subgraph。此subgraph不是connected component,原因在於,再加入vertex:W、T,以及edge:(Y,W)、(Y,T),也就是變回G1後,仍然維持connected特性,因此這個subgraph並不是「可以維持connected的最大集合」。 換句話說,在一個connected的undirected graph中,只會有一個connected component,就是graph本身。 * G2本身不是connected,而是由兩個connected component組成。 ![image](https://hackmd.io/_uploads/HyQfpS_3kl.png =500x) 圖1 **strongly connected : (directed graph)** 對任意兩個vertex都存在一條path(中間可以有多個點)連結這兩個vertex,則稱此undirected graph是connected。 1.圖2中,G3中的所有vertex都可以經過一條path到達其他vertex,因此G3為strongly connected。 2.G4並非strongly connected,例如,雖然path:S-X-T-Z可以從vertex(S)走到vertex(Z),但是從vertex(Z)卻無法經由任何一條path到達vertex(S)。 **strongly connected component:(directed graph最大的connect component)** 在一個strongly connect的子圖中不能加入任意的邊或是點之後,使子圖還是維持任意兩個點還是strongly connect。稱此subgraph為strongly connected component **(最大集合的strongly connected subgraph)**。 * 圖2中,右上方為G3的其中一個subgraph。此subgraph不是strongly connected component,原因在於,再加入edge:(W,Z)後(也就是變回G3),仍然維持connected特性,因此這個subgraph並不是「可以維持connected的最大集合」。 如同undirected graph,若一個directed graph本身是strongly sonnected,則本身也是唯一的strongly connected component。 * G4是由三個strongly connected component組成。 ![image](https://hackmd.io/_uploads/rJcy-UO3Je.png) 圖2 # BFS ## [文章網址](http://alrightchiu.github.io/SecondRound/graph-breadth-first-searchbfsguang-du-you-xian-sou-xun.html) 有用到樹的概念,但實作可以不用到指標。 ![](https://github.com/alrightchiu/SecondRound/blob/master/content/Algorithms%20and%20Data%20Structures/Graph%20series/BFS_fig/f5.png?raw=true =400x) ![](https://github.com/alrightchiu/SecondRound/blob/master/content/Algorithms%20and%20Data%20Structures/Graph%20series/BFS_fig/f13.png?raw=true =400x) ```cpp= #include <iostream> #include <vector> #include <list> #include <queue> class Graph{ private: int num_vertex; std::vector< std::list<int> > AdjList; int *color, // 0:白色, 1:灰色, 2:黑色 *distance, // 0:起點, 無限大:從起點走不到的vertex *predecessor; // -1:沒有predecessor, 表示為起點vertex public: Graph():num_vertex(0){}; // default constructor Graph(int N):num_vertex(N){ // constructor with input: number of vertex // initialize Adjacency List AdjList.resize(num_vertex); }; void AddEdgeList(int from, int to); void BFS(int Start); }; void Graph::AddEdgeList(int from, int to){ AdjList[from].push_back(to); } void Graph::BFS(int Start){ color = new int[num_vertex]; predecessor = new int[num_vertex]; distance = new int[num_vertex]; for (int i = 0; i < num_vertex; i++) { // 初始化,如圖二(b) color[i] = 0; // 0:白色; predecessor[i] = -1; // -1表示沒有predecessor distance[i] = num_vertex+1; // num_vertex個vertex, } // 最長距離 distance = num_vertex -1條edge std::queue<int> q; int i = Start; //設定i與j的原因是因為要方便設定起點 不然其實用j就可以了 for (int j = 0; j < num_vertex; j++) { // j從0數到num_vertex-1, 因此j會走過graph中所有vertex if (color[i] == 0) { // 第一次i會是起點vertex, 如圖二(c) color[i] = 1; // 1:灰色 distance[i] = 0; // 每一個connected component的起點之距離設成0 predecessor[i] = -1; // 每一個connected component的起點沒有predecessor q.push(i); while (!q.empty()) { int u = q.front(); // u 為新的搜尋起點 for (std::list<int>::iterator itr = AdjList[u].begin(); // for loop 太長 itr != AdjList[u].end(); itr++) { // 分成兩段 if (color[*itr] == 0) { // 若被「找到」的vertex是白色 color[*itr] = 1; // 塗成灰色, 表示已經被「找到」 distance[*itr] = distance[u] + 1; // 距離是predecessor之距離加一 predecessor[*itr] = u; // 更新被「找到」的vertex的predecessor q.push(*itr); // 把vertex推進queue } } q.pop(); // 把u移出queue color[u] = 2; // 並且把u塗成黑色 } } // 若一次回圈沒有把所有vertex走過, 表示graph有多個connected component // 就把i另成j, 繼續檢查graph中的其他vertex是否仍是白色, 若是, 重複while loop i = j; } } int main(){ Graph g1(9); // 建立出圖二(a)的Adjacency List g1.AddEdgeList(0, 1);g1.AddEdgeList(0, 2);g1.AddEdgeList(0, 3); g1.AddEdgeList(1, 0);g1.AddEdgeList(1, 4); g1.AddEdgeList(2, 0);g1.AddEdgeList(2, 4);g1.AddEdgeList(2, 5);g1.AddEdgeList(2, 6);g1.AddEdgeList(2, 7); g1.AddEdgeList(3, 0);g1.AddEdgeList(3, 7); g1.AddEdgeList(4, 1);g1.AddEdgeList(4, 2);g1.AddEdgeList(4, 5); g1.AddEdgeList(5, 2);g1.AddEdgeList(5, 4);g1.AddEdgeList(5, 8); g1.AddEdgeList(6, 2);g1.AddEdgeList(6, 7);g1.AddEdgeList(6, 8); g1.AddEdgeList(7, 2);g1.AddEdgeList(7, 3);g1.AddEdgeList(7, 6); g1.AddEdgeList(8, 5);g1.AddEdgeList(8, 6); g1.BFS(0); return 0; } ``` ## 例子 ### uva11624 https://zerojudge.tw/ShowProblem?problemid=e699 用queue,然後四個方向用迴圈去跑就可以大幅減短程式碼。 ```cpp= #include <bits/stdc++.h> #define int long long #define MAXN 1005 using namespace std; struct pos { int y, x; bool if_peo; int dis; }; char maze[MAXN][MAXN]; int step[MAXN][MAXN]; int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; queue<pos> que; int bfs(int h, int w) { while (!que.empty()) { pos a = que.front(); que.pop(); for (int i = 0; i < 4; i++) { pos b; b.y = a.y + dir[i][0]; b.x = a.x + dir[i][1]; b.if_peo = a.if_peo; b.dis = a.dis + 1; if ((b.y < h && b.y >= 0 && b.x < w && b.x >= 0) && maze[b.y][b.x] != '#' && step[b.y][b.x] == -1) { que.push(b); step[b.y][b.x] = b.dis; } else if ((b.y >= h || b.y < 0 || b.x >= w || b.x < 0) && b.if_peo == 1) { return b.dis; } } } return 0; } void solve() { int h, w; pos peo; cin >> h >> w; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { cin >> maze[i][j]; if (maze[i][j] == 'F') { que.push((struct pos){i, j, 0, 0}); // c++11以上,(struct pos){i, j, 0, 0}可以直接寫成{i, j, 0, 0} step[i][j] = 0; } if (maze[i][j] == 'J') { peo = (struct pos){i, j, 1, 0}; step[i][j] = 0; } } } que.push(peo); int sum = bfs(h, w); if (sum) cout << sum << endl; else cout << "IMPOSSIBLE" << endl; } signed main() { int n; cin >> n; while (n--) { memset(step, -1, sizeof(step)); while (!que.empty()) que.pop();//要記得清空 solve(); } return 0; } ``` # DFS ## [文章網址](http://alrightchiu.github.io/SecondRound/graph-depth-first-searchdfsshen-du-you-xian-sou-xun.html) ![](https://github.com/alrightchiu/SecondRound/blob/master/content/Algorithms%20and%20Data%20Structures/Graph%20series/DFS_fig/DFS_Flow.gif?raw=true =400x) ```cpp predecessor: 0 1 2 3 4 5 6 7 -1 0 0 1 3 3 -1 6 discover time: 0 1 2 3 4 5 6 7 1 2 10 3 4 6 13 14 finish time: 0 1 2 3 4 5 6 7 12 9 11 8 5 7 16 15 ``` ```cpp= #include <iostream> #include <vector> #include <list> #include <queue> #include <iomanip> // for std::setw() class Graph{ private: int num_vertex; std::vector< std::list<int> > AdjList; int *color, // 0:white, 1:gray, 2:black *predecessor, *discover, *finish; public: Graph():num_vertex(0){}; Graph(int N):num_vertex(N){ // initialize Adj List AdjList.resize(num_vertex); }; void AddEdgeList(int from, int to); void BFS(int Start); // 定義見上一篇文章 void DFS(int Start); void DFSVisit(int vertex, int &time); }; void Graph::DFS(int Start){ color = new int[num_vertex]; // 配置記憶體位置 discover = new int[num_vertex]; finish = new int[num_vertex]; predecessor = new int[num_vertex]; int time = 0; // 初始化, 如圖三(b) for (int i = 0; i < num_vertex; i++) { color[i] = 0; discover[i] = 0; finish[i] = 0; predecessor[i] = -1; } int i = Start; for (int j = 0; j < num_vertex; j++) { // 檢查所有Graph中的vertex都要被搜尋到 if (color[i] == 0) { // 若vertex不是白色, 則進行以該vertex作為起點之搜尋 DFSVisit(i, time); } i = j; // j會把AdjList完整走過一遍, 確保所有vertex都被搜尋過 } } void Graph::DFSVisit(int vertex, int &time){ // 一旦有vertex被發現而且是白色, 便進入DFSVisit() color[vertex] = 1; // 把vertex塗成灰色 discover[vertex] = ++time; // 更新vertex的discover時間 for (std::list<int>::iterator itr = AdjList[vertex].begin(); // for loop參數太長 itr != AdjList[vertex].end(); itr++) { // 分成兩段 if (color[*itr] == 0) { // 若搜尋到白色的vertex predecessor[*itr] = vertex; // 更新其predecessor DFSVisit(*itr, time); // 立刻以其作為新的搜尋起點, 進入新的DFSVisit() } } color[vertex] = 2; // 當vertex已經搜尋過所有與之相連的vertex後, 將其塗黑 finish[vertex] = ++time; // 並更新finish時間 } int main(){ // 定義一個具有八個vertex的Graph Graph g2(8); // 建立如圖三之Graph g2.AddEdgeList(0, 1);g2.AddEdgeList(0, 2); g2.AddEdgeList(1, 3); g2.AddEdgeList(2, 1);g2.AddEdgeList(2, 5); g2.AddEdgeList(3, 4);g2.AddEdgeList(3, 5); // AdjList[4] is empty g2.AddEdgeList(5, 1); g2.AddEdgeList(6, 4);g2.AddEdgeList(6, 7); g2.AddEdgeList(7, 6); g2.DFS(0); // 以vertex(0), 也就是vertex(A作為DFS()的起點 return 0; } ``` # connect component ![image](https://hackmd.io/_uploads/S1HVHCJ6yl.png) ![image](https://hackmd.io/_uploads/By_HH0y6yx.png) ```cpp= output: predecessor: 0 1 2 3 4 5 6 7 8 -1 0 -1 -1 1 4 3 5 6 predecessor: 0 1 2 3 4 5 6 7 8 -1 0 -1 -1 0 0 3 0 3 Component#1: 0 1 4 5 7 Component#2: 2 Component#3: 3 6 8 ``` ```cpp= // C++ code #include <iostream> #include <vector> #include <list> #include <queue> #include <iomanip> // for std::setw() class Graph{ private: int num_vertex; std::vector< std::list<int> > AdjList; int *color, // 0:white, 1:gray, 2:black *predecessor, *distance, // for BFS() *discover, // for DFS() *finish; public: Graph():num_vertex(0){}; Graph(int N):num_vertex(N){ // initialize Adj List AdjList.resize(num_vertex); }; void AddEdgeList(int from, int to); void BFS(int Start); void DFS(int Start); void DFSVisit(int vertex, int &time); void CCDFS(int vertex); // 利用DFS void CCBFS(int vertex = 0); // 利用BFS, 兩者邏輯完全相同 void SetCollapsing(int vertex); void PrintPredecessor(); // 印出predecessor, 供驗証用, 非必要 }; void Graph::SetCollapsing(int current){ int root; // root for (root = current; predecessor[root] >= 0; root = predecessor[root]);//先游到根源祖先那邊 while (current != root) { int parent = predecessor[current]; predecessor[current] = root; current = parent; } } void Graph::CCDFS(int vertex = 0){ DFS(vertex); // CCDFS與CCBFS只差這行都是為了找爸爸而已 PrintPredecessor(); for (int i = 0; i< num_vertex; i++){//對每個點做collapsing SetCollapsing(i); } PrintPredecessor(); int num_cc = 0; for (int i = 0; i < num_vertex; i++) { if (predecessor[i] < 0) { std::cout << "Component#" << ++num_cc << ": " << i << " "; for (int j = 0; j < num_vertex; j++) { if (predecessor[j] == i) { std::cout << j << " "; } } std::cout << std::endl; } } } void Graph::CCBFS(int vertex){ BFS(vertex); PrintPredecessor(); for (int i = 0; i< num_vertex; i++){ SetCollapsing(i); } PrintPredecessor(); int num_cc = 0; for (int i = 0; i < num_vertex; i++) { if (predecessor[i] < 0) { std::cout << "Component#" << ++num_cc << ": " << i << " "; for (int j = 0; j < num_vertex; j++) { if (predecessor[j] == i) { std::cout << j << " "; } } std::cout << std::endl; } } } void Graph::PrintPredecessor(){ std::cout << "predecessor:" << std::endl; for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << i; std::cout << std::endl; for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << predecessor[i]; std::cout << std::endl; } int main(){ Graph g3(9); g3.AddEdgeList(0, 1); g3.AddEdgeList(1, 0);g3.AddEdgeList(1, 4);g3.AddEdgeList(1, 5); // AdjList[2] empty g3.AddEdgeList(3, 6); g3.AddEdgeList(4, 1);g3.AddEdgeList(4, 5); g3.AddEdgeList(5, 1);g3.AddEdgeList(5, 4);g3.AddEdgeList(5, 7); g3.AddEdgeList(6, 3);g3.AddEdgeList(6, 8); g3.AddEdgeList(7, 5); g3.AddEdgeList(8, 6); g3.CCDFS(); std::cout << std::endl; g3.CCBFS(); std::cout << std::endl; return 0; } ``` # strongly connect component ![image](https://hackmd.io/_uploads/SJZkCSbpJx.jpg) 以component的角度來看只要是DAG那每個點的finish順序會是絕對的(以箭頭方向往下遞減) 所以只要找到最大的finish時間 再從反向的圖中做dfs 就一定會做完一個component的dfs才去下一個component的dfs(因為唯一的出口是被指向的 所以走不出去) =>每棵dfs的樹就是一個strongly connect component ```cpp= #include <iostream> #include <vector> #include <list> #include <queue> #include <iomanip> // for std::setw() class Graph{ private: int num_vertex; std::vector< std::list<int> > AdjList; int *color, // 0:white, 1:gray, 2:black *predecessor, *distance, // for BFS() *discover, // for DFS() *finish; public: Graph():num_vertex(0){}; Graph(int N):num_vertex(N){ // initialize Adj List AdjList.resize(num_vertex); }; int GetColor(int i){return color[i];}; // 取得private data: color int GetFinish(int i){return finish[i];}; // 取得private data: finish int GetPredecessor(int i){return predecessor[i];}; // 取得private data: predecessor void AddEdgeList(int from, int to); void BFS(int Start); void DFS(int Start); void DFSVisit(int vertex, int &time); void VariableInitializeDFS(); // 對DFS()需要的資料:color, discover, finish, predecessor // 進行「配置記憶體」與「初始化」 void CCDFS(int vertex); // 吃一個int, 表示起點vertex, 若沒給就從0開始 void CCBFS(int vertex = 0); void SetCollapsing(int vertex); void PrintDataArray(int *array); // 列印出array[] void PrintFinish(); // 列印出 finish[] void PrintPredecessor(); // 列印出 predecessor[] Graph GraphTranspose(); // 產生Transpose of Graph void PrintSCCs(int Start = 0); // 吃一個int, 表示起點vertex, 若沒給就從0開始 // 利用QuickSort()得到 finish[] 由大致小的vertex順序 friend void QuickSort(int *vec, int front, int end, int *vec2); friend int Partition(int *vec, int front, int end, int *vec2); friend void swap(int *x, int *y); }; void swap(int *x, int *y){ int temp = *x; *x = *y; *y = temp; } int Partition(int *vec, int front, int end, int *vec2){ int pivot = vec[end]; int i = front - 1; for (int j = front; j < end; j++) { if (vec[j] > pivot) { i++; swap(&vec[i], &vec[j]); swap(&vec2[i], &vec2[j]); } } swap(&vec[i + 1], &vec[end]); swap(&vec2[i + 1], &vec2[end]); return i + 1; // 把 i + 1 當成下一個 recurrsive call 的 中間斷點 } void QuickSort(int *vec, int front, int end, int *vec2){ if (front < end) { int pivot = Partition(vec, front, end, vec2); QuickSort(vec, front, pivot - 1, vec2); QuickSort(vec, pivot + 1, end, vec2); } } void Graph::PrintSCCs(int Start){ // 第一次DFS(), 目的是取得finish[] DFS(Start); // 顯示 第一次DFS()後的finish[] std::cout << "First DFS() on G, finish time:" << std::endl; PrintFinish(); // gT代表Transpose of Graph Graph gT(num_vertex); gT = GraphTranspose(); // 矩陣 finishLargetoSmall[] 用來儲存 finish[] 由大至小的vertex順序 int finishLargetoSmall[num_vertex]; for (int i = 0; i < num_vertex; i++) { finishLargetoSmall[i] = i; } // QuickSort()會更新 finishLargetoSmall[] 成 finish[] 由大至小的vertex順序 QuickSort(finish, 0, num_vertex-1, finishLargetoSmall); // 列印出 finish[] 由大至小的vertex順序 std::cout << "finish time Large to Small:" << std::endl; PrintDataArray(finishLargetoSmall); // 第二次DFS(), 執行在gT上, 先對四個資料「配置記憶體」且「初始化」 gT.VariableInitializeDFS(); int time = 0; for (int i = 0; i < num_vertex; i++){ if (gT.GetColor(finishLargetoSmall[i]) == 0) { gT.DFSVisit(finishLargetoSmall[i], time); } } // 顯示 第二次DFS()後的finish[] std::cout << "Second DFS() on gT, finish time:\n"; gT.PrintFinish(); // 顯示 第二次DFS()後的predecessor[] std::cout << "predecessor[] before SetCollapsing:\n"; gT.PrintPredecessor(); for (int i = 0; i< num_vertex; i++) gT.SetCollapsing(i); // 顯示 SetCollapsing後的predecessor[] std::cout << "predecessor after SetCollapsing:\n"; gT.PrintPredecessor(); // 如同在undirected graph中尋找connected component int num_cc = 0; for (int i = 0; i < num_vertex; i++) { if (gT.GetPredecessor(i) < 0) { std::cout << "SCC#" << ++num_cc << ": " << i << " "; for (int j = 0; j < num_vertex; j++) { if (gT.GetPredecessor(j) == i) { std::cout << j << " "; } } std::cout << std::endl; } } std::cout << std::endl; } void Graph::VariableInitializeDFS(){ color = new int[num_vertex]; discover = new int[num_vertex]; finish = new int[num_vertex]; predecessor = new int[num_vertex]; for (int i = 0; i < num_vertex; i++) { color[i] = 0; discover[i] = 0; finish[i] = 0; predecessor[i] = -1; } } Graph Graph::GraphTranspose(){ Graph gT(num_vertex); for (int i = 0; i < num_vertex; i++) { for (std::list<int>::iterator itr = AdjList[i].begin();itr != AdjList[i].end(); itr++) { gT.AddEdgeList(*itr, i); } } return gT; } void Graph::PrintDataArray(int *array){ for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << i; std::cout << std::endl; for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << array[i]; std::cout << std::endl; } void Graph::PrintFinish(){ for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << i; std::cout << std::endl; for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << finish[i]; std::cout << std::endl; } void Graph::PrintPredecessor(){ for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << i; std::cout << std::endl; for (int i = 0; i < num_vertex; i++) std::cout << std::setw(4) << predecessor[i]; std::cout << std::endl; } int main(){ Graph g4(9); g4.AddEdgeList(0, 1); g4.AddEdgeList(1, 2);g4.AddEdgeList(1, 4); g4.AddEdgeList(2, 0);g4.AddEdgeList(2, 3);g4.AddEdgeList(2, 5); g4.AddEdgeList(3, 2); g4.AddEdgeList(4, 5);g4.AddEdgeList(4, 6); g4.AddEdgeList(5, 4);g4.AddEdgeList(5, 6);g4.AddEdgeList(5, 7); g4.AddEdgeList(6, 7); g4.AddEdgeList(7, 8); g4.AddEdgeList(8, 6); std::cout << "Vertex(0) as starting point for the First DFS():\n\n"; g4.PrintSCCs(); std::cout << "Vertex(3) as starting point for the First DFS():\n\n"; g4.PrintSCCs(3); return 0; } ``` ```cpp= output: Vertex(0) as starting point for the First DFS(): First DFS() on G, finish time: 0 1 2 3 4 5 6 7 8 18 17 16 5 14 15 13 12 11 finish time Large to Small: 0 1 2 3 4 5 6 7 8 0 1 2 5 4 6 7 8 3 Second DFS() on gT, finish time: 0 1 2 3 4 5 6 7 8 8 4 7 6 11 12 18 16 17 predecessor[] before SetCollapsing: 0 1 2 3 4 5 6 7 8 -1 2 0 2 5 -1 -1 8 6 predecessor after SetCollapsing: 0 1 2 3 4 5 6 7 8 -1 0 0 0 5 -1 -1 6 6 SCC#1: 0 1 2 3 SCC#2: 5 4 SCC#3: 6 7 8 Vertex(3) as starting point for the First DFS(): First DFS() on G, finish time: 0 1 2 3 4 5 6 7 8 16 15 17 18 14 13 12 11 10 finish time Large to Small: 0 1 2 3 4 5 6 7 8 3 2 0 1 4 5 6 7 8 Second DFS() on gT, finish time: 0 1 2 3 4 5 6 7 8 5 6 7 8 12 11 18 16 17 predecessor[] before SetCollapsing: 0 1 2 3 4 5 6 7 8 1 2 3 -1 -1 4 -1 8 6 predecessor after SetCollapsing: 0 1 2 3 4 5 6 7 8 3 3 3 -1 -1 4 -1 6 6 SCC#1: 3 0 1 2 SCC#2: 4 5 SCC#3: 6 7 8 ``` ![image](https://hackmd.io/_uploads/S1yXUyma1g.png) ![image](https://hackmd.io/_uploads/HktXL1XTJg.png) ![image](https://hackmd.io/_uploads/S1VV8kmayg.png) ![image](https://hackmd.io/_uploads/rJySLkm6ye.png)