搜索
首页后端开发C++Tarjan算法和Kosaraju算法的比较

Tarjan算法和Kosaraju算法的比较

Tarjan 算法是在有向图中定位强链接组件,Robert Tarjan 在 1972 年创建了称为 Tarjan 算法的图遍历技术。它无需遍历先前处理的节点,而是使用深度有效地定位和处理每个高度相关的组件首先是搜索策略和栈数据结构。该算法经常用于计算机科学和图论,并具有多种用途,包括算法创建、网络分析和数据挖掘。

Kosaraju 的算法由对图的两次遍历组成。在第一遍中,以相反的顺序遍历图,并为每个节点分配“完成时间”。在第二遍中,按照节点的完成时间顺序访问节点,并识别和标记每个强连接组件。

Tarjan算法方法

在本例中,Graph 类在程序开头定义,其构造函数根据图中的顶点数初始化邻接表数组。通过将目标顶点包含在源顶点的邻接列表中,addEdge 函数向图中添加一条边。

SCCUtil 方法是 SCC 方法用来发现 SCC 的基于 DFS 的递归函数,它构成了该程序的基础。当前顶点u、发现时间disc的数组、低值low的数组、顶点堆栈st的数组以及跟踪顶点是否在堆栈中的布尔值数组stackMember是该函数的输入。

该过程将当前顶点放入堆栈,并在首先为其分配发现时间和低值后将其 stackMember 值更改为 true。之后,它以递归的方式将所有附近顶点的低值更新到当前顶点。

该技术迭代地访问未发现的顶点 vs 并修改 u 的低值。如果v已经在堆栈中,该方法根据v的发现时间修改u的低值。

Tarjan算法

  • 初始化算法

  • 开始遍历图表

  • 检查强连接组件

  • 重复直到所有节点都被访问过

  • 返回强连通分量

该方法将所有顶点从堆栈中弹出,直到当前顶点 u 被弹出,打印弹出的顶点,如果 u 是头节点(即,如果其低值等于其发现时间),则将其 stackMember 值设置为 false。

示例

// C++ program to find the SCC using
// Tarjan's algorithm (single DFS)
#include <iostream>
#include <list>
#include <stack>
#define NIL -1
using namespace std;

// A class that represents
// an directed graph
class Graph {
    // No. of vertices
    int V;

    // A dynamic array of adjacency lists
    list<int>* adj;

    // A Recursive DFS based function
    // used by SCC()
    void SCCUtil(int u, int disc[], int low[], stack<int>* st, bool stackMember[]);

    public:
    // Member functions
    Graph(int V);
    void addEdge(int v, int w);
    void SCC();
};

// Constructor
Graph::Graph(int V) {
    this->V = V;
    adj = new list<int>[V];
}

// Function to add an edge to the graph
void Graph::addEdge(int v, int w) {
    adj[v].push_back(w);
}

// Recursive function to finds the SCC
// using DFS traversal
void Graph::SCCUtil(int u, int disc[],  int low[], stack<int>* st,  bool stackMember[]) {
    static int time = 0;

    // Initialize discovery time
    // and low value
    disc[u] = low[u] = ++time;
    st->push(u);
    stackMember[u] = true;

    // Go through all vertices
    // adjacent to this
    list<int>::iterator i;

    for (i = adj[u].begin();
        i != adj[u].end(); ++i) {
        // v is current adjacent of 'u'
        int v = *i;

        // If v is not visited yet,
        // then recur for it
        if (disc[v] == -1) {
           SCCUtil(v, disc, low, st, stackMember);

           // Check if the subtree rooted
           // with 'v' has connection to
           // one of the ancestors of 'u'
            low[u] = min(low[u], low[v]);
        }

        // Update low value of 'u' only of
        // 'v' is still in stack
        else if (stackMember[v] == true)
            low[u] = min(low[u], disc[v]);
    }

    // head node found, pop the stack
    // and print an SCC

    // Store stack extracted vertices
    int w = 0;

    // If low[u] and disc[u]
    if (low[u] == disc[u]) {
    // Until stack st is empty
        while (st->top() != u) {
            w = (int)st->top();

            // Print the node
            cout << w << " ";
            stackMember[w] = false;
            st->pop();
        }
        w = (int)st->top();
        cout << w << "\n";
        stackMember[w] = false;
        st->pop();
    }
}

// Function to find the SCC in the graph
void Graph::SCC() {
    // Stores the discovery times of
    // the nodes
    int* disc = new int[V];

    // Stores the nodes with least
    // discovery time
    int* low = new int[V];

    // Checks whether a node is in
    // the stack or not
    bool* stackMember = new bool[V];

    // Stores all the connected ancestors
    stack<int>* st = new stack<int>();

    // Initialize disc and low,
    // and stackMember arrays
    for (int i = 0; i < V; i++) {
        disc[i] = NIL;
        low[i] = NIL;
        stackMember[i] = false;
    }

    // Recursive helper function to
    // find the SCC in DFS tree with
    // vertex 'i'
    for (int i = 0; i < V; i++) {

        // If current node is not
        // yet visited
        if (disc[i] == NIL) {
            SCCUtil(i, disc, low, st, stackMember);
        }
    }
}

// Driver Code
int main() {
    // Given a graph
    Graph g1(5);
    g1.addEdge(3, 5);
    g1.addEdge(0, 2);
    g1.addEdge(2, 1);
    g1.addEdge(4, 3);
    g1.addEdge(3, 4);

    // Function Call to find SCC using
    // Tarjan's Algorithm
    g1.SCC();

    return 0;
} 

输出

1
2
0
4 3 

方法2-Kosaraju

Kosaraju算法

  • 启动时创建已访问节点的集合和空堆栈。

  • 从图中每个节点尚未访问过的第一个节点开始深度优先搜索。将整个搜索过程中访问过的每个节点推入堆栈。

  • 每个图形边缘的方向都应该反转。

  • 如果堆栈中仍充满节点,则将节点从堆栈中弹出。 如果尚未访问该节点,则从该节点执行深度优先搜索。将搜索过程中访问过的每个节点标记为当前高度链接组件的成员。

  • 重复直到所有节点都被访问过

  • 每个高度关联的组件都将在程序结束时得到识别和注释。

下面的 C++ 代码使用 Kosaraju 算法来查找有向图中的强连通分量 (SCC)。软件定义了一个名为Graph的类,它有以下成员函数:

Graph(int V):构造函数,输入顶点数量 V 并初始化图的邻接列表。

Void addEdge(int v, int w):使用两个整数 v 和 w 作为输入,此方法创建一条连接图中顶点 v 到顶点 w 的边。

void printSCCs() 函数使用 Kosaraju 算法来打印图中的每个 SCC。

Graph getTranspose() 方法提供了图的转置。

使用递归函数 void fillOrder(int v, bool Visited[, stack& Stack], int v) 将顶点按照其完成时间的顺序添加到堆栈中。

示例 2

// C++ program to print the SCC of the 
// graph using Kosaraju's Algorithm 
#include <iostream> 
#include <list>
#include <stack> 
using namespace std; 
 
class Graph {
   // No. of vertices 
   int V; 
    
   // An array of adjacency lists 
   list<int>* adj; 
    
   // Member Functions 
   void fillOrder(int v, bool visited[], 
   stack<int>& Stack); 
   void DFSUtil(int v, bool visited[]); 
    
   public: 
   Graph(int V); 
   void addEdge(int v, int w); 
   void printSCCs(); 
   Graph getTranspose(); 
}; 
 
// Constructor of class 
Graph::Graph(int V) { 
   this->V = V; 
   adj = new list<int>[V]; 
} 
 
// Recursive function to print DFS 
// starting from v 
void Graph::DFSUtil(int v, bool visited[])  { 
   // Mark the current node as 
   // visited and print it 
   visited[v] = true; 
   cout << v <<" "; 
    
   // Recur for all the vertices 
   // adjacent to this vertex 
   list<int>::iterator i; 
    
   // Traverse Adjacency List of node v 
   for (i = adj[v].begin(); 
   i != adj[v].end(); ++i) { 
       
      // If child node *i is unvisited 
      if (!visited[*i]) 
      DFSUtil(*i, visited); 
   } 
} 
 
// Function to get the transpose of 
// the given graph 
Graph Graph::getTranspose()  { 
   Graph g(V); 
   for (int v = 0; v < V; v++) { 
      // Recur for all the vertices 
      // adjacent to this vertex 
      list<int>::iterator i; 
      for (i = adj[v].begin(); 
      i != adj[v].end(); ++i) { 
         // Add to adjacency list 
         g.adj[*i].push_back(v); 
      } 
   } 
    
   // Return the reversed graph 
   return g; 
} 
 
// Function to add an Edge to the given 
// graph 
void Graph::addEdge(int v, int w)  { 
   // Add w to v’s list 
   adj[v].push_back(w); 
} 
 
// Function that fills stack with vertices 
// in increasing order of finishing times 
void Graph::fillOrder(int v, bool visited[], 
stack<int>& Stack)  { 
   // Mark the current node as 
   // visited and print it 
   visited[v] = true; 
    
   // Recur for all the vertices 
   // adjacent to this vertex 
   list<int>::iterator i; 
    
   for (i = adj[v].begin(); 
   i != adj[v].end(); ++i) { 
    
      // If child node *i is unvisited 
      if (!visited[*i]) { 
         fillOrder(*i, visited, Stack); 
      } 
   } 
    
   // All vertices reachable from v 
   // are processed by now, push v 
   Stack.push(v); 
} 
 
// Function that finds and prints all 
// strongly connected components 
void Graph::printSCCs()  { 
   stack<int> Stack; 
    
   // Mark all the vertices as 
   // not visited (For first DFS) 
   bool* visited = new bool[V]; 
   for (int i = 0; i < V; i++) 
   visited[i] = false; 
    
   // Fill vertices in stack according 
   // to their finishing times 
   for (int i = 0; i < V; i++) 
   if (visited[i] == false) 
   fillOrder(i, visited, Stack); 
    
   // Create a reversed graph 
   Graph gr = getTranspose(); 
    
   // Mark all the vertices as not 
   // visited (For second DFS) 
   for (int i = 0; i < V; i++) 
   visited[i] = false; 
    
   // Now process all vertices in 
   // order defined by Stack 
   while (Stack.empty() == false) { 
    
      // Pop a vertex from stack 
      int v = Stack.top(); 
      Stack.pop(); 
       
      // Print SCC of the popped vertex 
      if (visited[v] == false) { 
         gr.DFSUtil(v, visited); 
         cout << endl; 
      } 
   } 
} 
 
// Driver Code 
int main()  { 
   // Given Graph 
   Graph g(5); 
   g.addEdge(1, 0); 
   g.addEdge(0, 2); 
   g.addEdge(2, 1); 
   g.addEdge(1, 3); 
   g.addEdge(2, 4); 
    
   // Function Call to find the SCC 
   // using Kosaraju's Algorithm 
   g.printSCCs(); 
    
   return 0; 
} 

输出

0 1 2 
4
3</p><p>

结论

总之,Tarjan 和 Kosaraju 方法的时间复杂度为 O(V + E),其中 V 表示图中的顶点集,E 表示图中的边集。 Tarjan 算法中的常数因子明显低于 Kosaraju 方法中的常数因子。 Kosaraju 的方法至少遍历该图两次,因此常数因子可能是两倍。当我们完成第二个 DFS 时,我们可以使用 Kosaraju 的方法来生成现在处于活动状态的 SCC。一旦找到 SCC 子树的头部,用 Tarjan 算法打印 SCC 需要更长的时间。

尽管两种方法的线性时间复杂度相同,但 SCC 计算所使用的方法或流程存在一些差异。虽然 Kosaraju 的技术在图上运行两个 DFS(如果我们希望保持原始图不被修改,则运行三个 DFS),并且与确定图的拓扑排序的方法非常相似,但 Tarjan 的方法仅依赖于节点记录DFS 对图进行分区。

以上是Tarjan算法和Kosaraju算法的比较的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:tutorialspoint。如有侵权,请联系admin@php.cn删除
Golang中最好的缓存库是什么?我们来一一比较。Golang中最好的缓存库是什么?我们来一一比较。Jun 19, 2023 pm 07:51 PM

Golang中最好的缓存库是什么?我们来一一比较。在编写Go代码时,经常需要使用缓存,例如存放一些比较耗时的计算结果或者从数据库中读取的数据等,缓存能够大大提高程序的性能。但是,Go语言没有提供原生的缓存库,所以我们需要使用第三方的缓存库。在这篇文章中,我们将一一比较几个比较流行的Go缓存库,找到最适合我们的库。GocacheGocache是一个高效的内存缓

如何通过PHP在FTP服务器上进行目录和文件的比较如何通过PHP在FTP服务器上进行目录和文件的比较Jul 28, 2023 pm 02:09 PM

如何通过PHP在FTP服务器上进行目录和文件的比较在web开发中,有时候我们需要比较本地文件与FTP服务器上的文件,以确保两者之间的一致性。PHP提供了一些函数和类来实现这个功能。本文将介绍如何使用PHP在FTP服务器上进行目录和文件的比较,并提供相关的代码示例。首先,我们需要连接到FTP服务器。PHP提供了ftp_connect()函数来建立与FTP服务器

Go语言Web框架对比:gin vs. echo vs. irisGo语言Web框架对比:gin vs. echo vs. irisJun 17, 2023 pm 07:44 PM

随着Web开发的需求不断增加,各种语言的Web框架也逐渐多样化,Go语言也不例外。在许多Go语言的Web框架中,gin、echo和iris是三个最受欢迎的框架。在这篇文章中,我们将比较这三个框架的优缺点,以帮助您选择适合您的项目的框架。gingin是一个轻量级的Web框架,它具有高性能和灵活性的特点。它支持中间件和路由功能,这使得它非常适合构建RESTful

比较Java爬虫框架:哪个是最佳选择?比较Java爬虫框架:哪个是最佳选择?Jan 09, 2024 am 11:58 AM

探寻最佳Java爬虫框架:哪个更胜一筹?在当今信息时代,大量的数据在互联网中不断产生和更新。为了从海量数据中提取有用的信息,爬虫技术应运而生。而在爬虫技术中,Java作为一种强大且广泛应用的编程语言,拥有许多优秀的爬虫框架可供选择。本文将探寻几个常见的Java爬虫框架,并分析它们的特点和适用场景,最终找到最佳的一种。JsoupJsoup是一种非常受欢迎的Ja

深度对比Flutter和uniapp:探究它们的异同和特点深度对比Flutter和uniapp:探究它们的异同和特点Dec 23, 2023 pm 02:16 PM

在移动应用开发领域,Flutter和uniapp是两个备受关注的跨平台开发框架。它们的出现使得开发者能够快速且高效地开发同时支持多个平台的应用程序。然而,尽管它们有着相似的目标和用途,但在细节和特性方面存在一些差异。接下来,我们将深入比较Flutter和uniapp,并探讨它们各自的特点。Flutte是由Google推出的开源移动应用开发框架。Flutter

在Java中,我们如何比较StringBuilder和StringBuffer?在Java中,我们如何比较StringBuilder和StringBuffer?Aug 28, 2023 pm 03:57 PM

StringBuffer对象通常可以安全地在多线程环境中使用,其中多个线程可能会尝试访问同一个StringBuffer对象同时。StringBuilder是线程安全的StringBuffer类的替代品,它的工作速度要快得多,因为它没有同步>方法。如果我们在单个线程中执行大量字符串操作,则使用此类可以提高性能。示例publicclassCompareBuilderwithBufferTest{&nbsp;&nbsp;publicstaticvoidmain(String[]a

C程序用于比较两个矩阵是否相等C程序用于比较两个矩阵是否相等Aug 31, 2023 pm 01:13 PM

用户必须输入两个矩阵的顺序以及两个矩阵的元素。然后,比较这两个矩阵。如果矩阵元素和大小都相等,则表明两个矩阵相等。如果矩阵大小相等但元素相等不相等,则显示矩阵可以比较,但不相等。如果大小和元素不匹配,则显示矩阵无法比较。程序以下是C程序,用于比较两个矩阵是否相等-#include<stdio.h>#include<conio.h>main(){&nbsp;&nbsp;intA[10][10],B[10][10];&nbsp;&nbsp;in

MySQL和Oracle:对于数据加密和安全传输的支持程度比较MySQL和Oracle:对于数据加密和安全传输的支持程度比较Jul 12, 2023 am 10:29 AM

MySQL和Oracle:对于数据加密和安全传输的支持程度比较引言:数据安全在如今的信息时代中变得愈发重要。从个人隐私到商业机密,保持数据的机密性和完整性对于任何组织来说都至关重要。在数据库管理系统(DBMS)中,MySQL和Oracle是两个最受欢迎的选项。在本文中,我们将比较MySQL和Oracle在数据加密和安全传输方面的支持程度,并提供一些代码示例。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具