首页 > 文章列表 > 打印有向图中不属于任何循环的节点

打印有向图中不属于任何循环的节点

462 2023-08-30

在协调图中,识别不属于任何循环的集线器对于不同的应用程序至关重要。这些中心构建了非循环子图的基础,并在理解一般图表结构方面发挥着重要作用。通过使用有效的图表交叉计算,例如 Profundity First Hunt (DFS) 或 Tarjan 对紧密关联部件的计算,我们可以毫不费力地决定并打印不参与任何循环的集线器。这些方法保证了没有循环合作的中心的特色,为图表的非循环部分提供了重要的知识,并支持与图表相关的不同批判性思维情况。

使用的方法

  • 带循环检测的深度优先搜索 (DFS)

  • Tarjan 的强连通分量算法

带循环检测的深度优先搜索 (DFS)

在此方法中,我们使用深度优先追踪 (DFS) 来导航协调图表并区分途中的周期。我们标记访问过的中心并保留一份清单,以便以持续的 DFS 方式跟踪中心。如果我们遇到后沿(以持续的 DFS 方式到达集线器的边缘),我们会区分一个周期。在 DFS 结束时,正在进行的 DFS 方式中的中心对于一个周期将很重要。不采用持续 DFS 方式的集线器不属于任何循环,可以打印。

算法

  • 在图表上从每个未访问过的中心开始进行深度首次狩猎 (DFS)。

  • 在 DFS 期间,标记访问过的集线器并将其添加到正在进行的 DFS 路径列表中。

  • 如果我们遇到后沿(当前 DFS 方式中到集线器的边缘),我们会区分一个周期,并将当前 DFS 方式中的所有集线器标记为周期的一部分。

  • 当集线器的 DFS 完成后,将其从正在进行的 DFS 路径列表中删除。

  • 完成所有轮毂的 DFS 后,不属于任何循环的轮毂将保持不变,我们可以打印它们。

示例

#include <iostream>
#include <vector>

class Graph {
public:
   Graph(int numVertices);
   void addEdge(int src, int dest);
   void DFS();
private:
   void DFSUtil(int v, std::vector<bool>& visited, std::vector<int>& dfsPath);
   int numVertices;
   std::vector<std::vector<int>> adjList;
};

Graph::Graph(int numVertices) : numVertices(numVertices) {
   adjList.resize(numVertices);
}

void Graph::addEdge(int src, int dest) {
   adjList[src].push_back(dest);
}

void Graph::DFSUtil(int v, std::vector<bool>& visited, std::vector<int>& dfsPath) {
   visited[v] = true;
   dfsPath.push_back(v);

   for (int neighbor : adjList[v]) {
      if (!visited[neighbor]) {
         DFSUtil(neighbor, visited, dfsPath);
      }
      else {
         std::cout << "Cycle found: ";
         for (size_t i = 0; i < dfsPath.size(); ++i) {
            if (dfsPath[i] == neighbor) {
               while (i < dfsPath.size()) {
                  std::cout << dfsPath[i] << " ";
                  ++i;
               }
               break;
            }
         }
         std::cout << std::endl;
      }
   }

   dfsPath.pop_back();
}

void Graph::DFS() {
   std::vector<bool> visited(numVertices, false);
   std::vector<int> dfsPath;

   for (int i = 0; i < numVertices; ++i) {
      if (!visited[i]) {
         DFSUtil(i, visited, dfsPath);
      }
   }
}

int main() {
   Graph graph(6);
   graph.addEdge(0, 1);
   graph.addEdge(1, 2);
   graph.addEdge(2, 3);
   graph.addEdge(3, 4);
   graph.addEdge(4, 1);
   graph.addEdge(4, 5);
   
   std::cout << "DFS traversal with cycle detection:n";
   graph.DFS();

   return 0;
}

输出

DFS traversal with cycle detection:
Cycle found: 1 2 3 4 

Tarjan 的强连通分量算法

Tarjan 的计算是一种强大的计算,用于追踪协调图中所有重点关联的部分。明确关联的部分是集线器的子集,其中子集中的任意两个集线器之间存在协调方式。不属于任何紧密关联部件的轮毂也不属于任何循环。通过查找重点关联的零件,我们可以识别不属于任何循环的轮毂并打印它们

算法

  • 将 Tarjan 的计算应用于引导图,以追踪所有重点关联的部分。

  • 在追踪所有重要关联部分后,区分对于紧密关联部分至关重要的中心。

  • 不属于任何明确关联部件的集线器不属于任何循环,并且可以打印。

  • 这两种方法确实区分并打印了不属于协调图表中任何周期的中心。 DFS 方法提供了更简单且更直接的执行,而 Tarjan 的计算更复杂,但提供了有关重点关联部分的额外数据,这对于特定的图表相关任务很有帮助。方法的决定取决于具体的需要和主要紧迫问题的背景。

示例

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;

class Graph {
   int V;
   vector<vector<int>> adj;
   vector<bool> visited;
   vector<int> disc, low;
   stack<int> st;
   vector<vector<int>> SCCs;
   vector<bool> essentialNodes;

public:
   Graph(int V) : V(V) {
      adj.resize(V);
      visited.resize(V, false);
      disc.resize(V, -1);
      low.resize(V, -1);
      essentialNodes.resize(V, true);
   }

   void addEdge(int u, int v) {
      adj[u].push_back(v);
   }

   void tarjanDFS(int u) {
      static int time = 0;
      disc[u] = low[u] = ++time;
      st.push(u);
      visited[u] = true;

      for (int v : adj[u]) {
         if (disc[v] == -1) {
            tarjanDFS(v);
            low[u] = min(low[u], low[v]);
         } else if (visited[v]) {
            low[u] = min(low[u], disc[v]);
         }
      }

      if (low[u] == disc[u]) {
         vector<int> SCC;
         int v;
         do {
            v = st.top();
            st.pop();
            SCC.push_back(v);
            visited[v] = false;
         } while (v != u);

         SCCs.push_back(SCC);
      }
   }

   void tarjan() {
      for (int i = 0; i < V; ++i) {
         if (disc[i] == -1) {
            tarjanDFS(i);
         }
      }
   }

   void identifyEssentialNodes() {
      for (const vector<int>& SCC : SCCs) {
         for (int v : SCC) {
            for (int u : adj[v]) {
               if (find(SCC.begin(), SCC.end(), u) == SCC.end()) {
                  essentialNodes[u] = false;
               }
            }
         }
      }
   }

   void printEssentialNodes() {
      cout << "Essential Nodes for Each SCC:n";
      for (int i = 0; i < V; ++i) {
         if (essentialNodes[i]) {
            cout << i << " ";
         }
      }
      cout << endl;
   }
};

int main() {
   Graph g(6);
   g.addEdge(0, 1);
   g.addEdge(1, 2);
   g.addEdge(2, 0);
   g.addEdge(1, 3);
   g.addEdge(3, 4);
   g.addEdge(4, 5);
   g.addEdge(5, 3);

   g.tarjan();
   g.identifyEssentialNodes();
   g.printEssentialNodes();

   return 0;
}

输出

Essential Nodes for Each SCC:
0 1 2 4 5

结论

这两种方法确实解决了识别不属于协调图表中任何周期的中心的问题。 DFS 方法易于执行,并且不需要太多额外的信息结构。另一方面,Tarjan 的计算提供了有关重点关联部分的额外数据,这在特定情况下可能会有所帮助。

两种方法之间的决定取决于问题的特定先决条件以及对经过与周期无关的区分中心的额外数据的要求。一般来说,如果唯一的目标是找到不属于任何循环的集线器,则 DFS 方法可能会因其简单性而受到青睐。尽管如此,如果需要进一步检查重点相关部分,Tarjan 的计算可能是一个重要的工具。这两种方法提供了熟练的安排,并且可以根据协调图表的属性和考试的理想结果进行调整