DFS:如何在C++中指示连通分量的节点

发布于 2024-12-12 08:16:08 字数 2875 浏览 0 评论 0原文

我正在制作 ACM 竞赛问题,以确定具有无向图 G 和属于每个组件的顶点的连接组件的数量。已经用DFS算法完成了,计算无向图的连通分量的数量(问题的困难部分),但我想不出任何东西来指示属于每个分量的节点或有节点的记录。

输入: 第一行输入一个整数C,表示测试用例的数量。每个测试用例的第一行包含两个整数 N 和 E,其中 N 表示图中的节点数,E 表示图中的边数。然后是 E 行,每行有 2 个整数 I 和 J,其中 I 和 J 表示节点 I 和节点 J 之间是否存在边(0 ≤ I, J

输出:在每个测试用例的第一行必须显示以下字符串“Case G: P component (s) linked (s)”,其中G代表连接的组件数量测试用例(从 1 开始)和 P 图中连接的组件数量。然后是 X 行,每行包含属于由空格分隔的连接组件的节点(按从最小到最大的顺序)。 每个测试用例之后应该打印一个空行。输出应该写在“output.out”中。

示例:

输入:

2
6 9
0 1
0 2
1 2
5 4
3 1
2 4
2 5
3 4
3 5
8 7
0 1
2 1
2 0
3 4
4 5
5 3
7 6

输出:

Case 1: 1 component (s) connected (s)
0 1 2 3 4 5

Case 2: 3 component (s) connected (s)
0 1 2
3 4 5
6 7

这是我的代码:

#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <string.h>
using namespace std;
vector<int> adjacency[10000];
bool visited[10000];

/// @param Standard algorithm DFS
void dfs(int u){
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

    int main(int argc, char *argv []){
    #ifndef ONLINE_JUDGE
    #pragma warning(disable: 4996)
        freopen("input.in", "r", stdin);
            freopen("output.out", "w", stdout);
    #endif

         ///enumerate vertices from 1 to vertex
        int vertex, edges , originNode ,destinationNode, i, j,cont =1;
        ///number of test cases
        int testCases;
        int totalComponents;
        scanf ("%d", &testCases);

        for (i=0; i<testCases; i++){

        memset( visited , 0 , sizeof( visited ) );
        scanf("%d %d" , &vertex , &edges );
        for (j=0; j<edges; j++){
            scanf("%d %d" , &originNode ,&destinationNode );
            adjacency[ originNode ].push_back( destinationNode );
            adjacency[ destinationNode ].push_back( originNode );
        }
            totalComponents =0;
            for( int i = 0 ; i < vertex ; ++i ){    // Loop through all possible vertex
                if( !visited[ i ] ){          //if we have not visited any one component from that node
                    dfs( i );                  //we travel from node i the entire graph is formed
                    totalComponents++;                   //increased amount of components
                }
            }
            printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);

            for (j=0;j<total;j++){
        /*here should indicate the vertices of each connected component*/
    }
        memset( adjacency , 0 , sizeof( adjacency ) );
        }
    return 0;
    }

我对如何携带属于每个连接组件或结构的节点的内存应该用于存储有疑问,我应该如何修改我的代码来做到这一点?,我想听听建议、想法或任何伪代码的实现。感谢大家

I am making a problem of ACM competitions to determine the number of connected components that have an undirected graph G and vertices belonging to each component. 've Already done with a DFS algorithm, counting the number of connected components of undirected graph (the hard part of problem), but I can not think of anything to indicate the nodes belonging to each component or have a record of the nodes.

Input: The first line of input will an integer C, which indicates the number of test cases. The first line of each test case contains two integers N and E, where N represents the number of nodes in the graph and E the number of edges in it. Then follow E lines, each with 2 integers I and J, where I and J represent the existence of an edge between node I and node J (0 ≤ I, J

Output: In the first line of each test case must display the following string "Case G: P component (s) connected (s)", where G represents the number of test case (starting at 1) and P the number of components connected in the graph. Then X lines, each containing the nodes belonging to a connected component (in order from smallest to largest) separated by spaces.
After each test case should print a blank line. The output should be written in the "output.out."

Example:

Input:

2
6 9
0 1
0 2
1 2
5 4
3 1
2 4
2 5
3 4
3 5
8 7
0 1
2 1
2 0
3 4
4 5
5 3
7 6

Output:

Case 1: 1 component (s) connected (s)
0 1 2 3 4 5

Case 2: 3 component (s) connected (s)
0 1 2
3 4 5
6 7

Here's my code:

#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <string.h>
using namespace std;
vector<int> adjacency[10000];
bool visited[10000];

/// @param Standard algorithm DFS
void dfs(int u){
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

    int main(int argc, char *argv []){
    #ifndef ONLINE_JUDGE
    #pragma warning(disable: 4996)
        freopen("input.in", "r", stdin);
            freopen("output.out", "w", stdout);
    #endif

         ///enumerate vertices from 1 to vertex
        int vertex, edges , originNode ,destinationNode, i, j,cont =1;
        ///number of test cases
        int testCases;
        int totalComponents;
        scanf ("%d", &testCases);

        for (i=0; i<testCases; i++){

        memset( visited , 0 , sizeof( visited ) );
        scanf("%d %d" , &vertex , &edges );
        for (j=0; j<edges; j++){
            scanf("%d %d" , &originNode ,&destinationNode );
            adjacency[ originNode ].push_back( destinationNode );
            adjacency[ destinationNode ].push_back( originNode );
        }
            totalComponents =0;
            for( int i = 0 ; i < vertex ; ++i ){    // Loop through all possible vertex
                if( !visited[ i ] ){          //if we have not visited any one component from that node
                    dfs( i );                  //we travel from node i the entire graph is formed
                    totalComponents++;                   //increased amount of components
                }
            }
            printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);

            for (j=0;j<total;j++){
        /*here should indicate the vertices of each connected component*/
    }
        memset( adjacency , 0 , sizeof( adjacency ) );
        }
    return 0;
    }

I have doubts about how to carry memory of the nodes belonging to each connected component or structure should be used to store, how I should modify my code to do this?, I would like to hear suggestions, ideas or any implementation in pseudocode. Thanks to all

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

So要识趣 2024-12-19 08:16:08

算法大致是:

  • 获取一个图节点。
  • 查找与其直接或间接连接的所有节点(双向)。
  • 将它们全部标记为“已遍历”并将它们放入新组件中。
  • 找到下一个遍历的节点并重复该过程。

结果是一组“组件”数据结构(我的实现中的 std::vector ),每个数据结构都包含一组专门互连的节点。

注意事项:

  • 我们需要将图存储在一个结构中,该结构可以有效地“向下”(从父母到孩子)和“向上”(从孩子到父母)遍历,并递归地找到所有连接的节点(在两个方向上),标记节点随着我们的移动而“遍历”。由于节点是由连续范围的整数标识的,因此我们只需使用随机访问属性 std::vector 即可高效地构建此结构。
  • 边和节点的概念是分开的,因此一个单个“遍历”标志可以存在于一个节点的级别,无论有多少其他节点连接到它(即无论有多少个父节点和有子边缘)。这使我们能够有效地减少已经到达的节点的递归。

这是工作代码。请注意,使用了一些 C++11 功能,但如果使用较旧的编译器,它们应该很容易替换。错误处理留给读者作为练习。

#include <iostream>
#include <vector>
#include <algorithm>

// A set of inter-connected nodes.
typedef std::vector<unsigned> Component;

// Graph node.
struct Node {
    Node() : Traversed(false) {
    }
    std::vector<unsigned> Children;
    std::vector<unsigned> Parents;
    bool Traversed;
};

// Recursive portion of the FindGraphComponents implementation.
//   graph: The graph constructed in FindGraphComponents().
//   node_id: The index of the current element of graph.
//   component: Will receive nodes that comprise the current component.
static void FindConnectedNodes(std::vector<Node>& graph, unsigned node_id, Component& component) {

    Node& node = graph[node_id];
    if (!node.Traversed) {

        node.Traversed = true;
        component.push_back(node_id);

        for (auto i = node.Children.begin(); i != node.Children.end(); ++i)
            FindConnectedNodes(graph, *i, component);

        for (auto i = node.Parents.begin(); i != node.Parents.end(); ++i)
            FindConnectedNodes(graph, *i, component);

    }

}

// Finds self-connected sub-graphs (i.e. "components") on already-prepared graph.
std::vector<Component> FindGraphComponents(std::vector<Node>& graph) {

    std::vector<Component> components;
    for (unsigned node_id = 0; node_id < graph.size(); ++node_id) {
        if (!graph[node_id].Traversed) {
            components.push_back(Component());
            FindConnectedNodes(graph, node_id, components.back());
        }
    }

    return components;

}

// Finds self-connected sub-graphs (i.e. "components") on graph that should be read from the input stream.
//   in: The input test case.
std::vector<Component> FindGraphComponents(std::istream& in) {

    unsigned node_count, edge_count;
    std::cin >> node_count >> edge_count;

    // First build the structure that can be traversed recursively in an efficient way.
    std::vector<Node> graph(node_count); // Index in this vector corresponds to node ID.
    for (unsigned i = 0; i < edge_count; ++i) {
        unsigned from, to;
        in >> from >> to;
        graph[from].Children.push_back(to);
        graph[to].Parents.push_back(from);
    }

    return FindGraphComponents(graph);

}

void main() {

    size_t test_case_count;
    std::cin >> test_case_count;

    for (size_t test_case_i = 1; test_case_i <= test_case_count; ++test_case_i) {

        auto components = FindGraphComponents(std::cin);

        // Sort components by descending size and print them.
        std::sort(
            components.begin(),
            components.end(),
            [] (const Component& a, const Component& b) { return a.size() > b.size(); }
        );

        std::cout << "Case " << test_case_i <<  ": " << components.size() << " component (s) connected (s)" << std::endl;
        for (auto components_i = components.begin(); components_i != components.end(); ++components_i) {
            for (auto edge_i = components_i->begin(); edge_i != components_i->end(); ++edge_i)
                std::cout << *edge_i << ' ';
            std::cout << std::endl;
        }
        std::cout << std::endl;

    }

}

将此程序称为...

GraphComponents.exe < input.in > output.out

...其中 input.in 包含问题中描述的格式的数据,它将在 output.out 中产生所需的结果。

The algorithm is roughly:

  • Get a graph node.
  • Find all nodes directly or indirectly connected to it (in both directions).
  • Mark all of them as "traversed" and put them into a new component.
  • Find the next node that is not traversed and repeat the process.

The result is a set of "component" data structures (std::vectors in my implementation), each containing a set of exclusively inter-connected nodes.

Considerations:

  • We need to store the graph in a structure that can be efficiently traversed both "down" (from parents to children) and "up" (from children to parents) and recursively find all the connected nodes (in both directions), marking the nodes as "traversed" as we go. Since nodes are identified by a continuous range of integers, we can build this structure efficiently just by using random-access properties std::vector.
  • The concept of edges and nodes are separated, so a single "traversed" flag can exist at the level of a node, no matter how many other nodes are connected to it (i.e. no matter how many parent and child edges there are). This enables us to cut the recursion efficiently for already-reached nodes.

Here is the working code. Note that some C++11 features were used, but they should be easy to replace if older compiler is used. The error handling is left as exercise for the reader.

#include <iostream>
#include <vector>
#include <algorithm>

// A set of inter-connected nodes.
typedef std::vector<unsigned> Component;

// Graph node.
struct Node {
    Node() : Traversed(false) {
    }
    std::vector<unsigned> Children;
    std::vector<unsigned> Parents;
    bool Traversed;
};

// Recursive portion of the FindGraphComponents implementation.
//   graph: The graph constructed in FindGraphComponents().
//   node_id: The index of the current element of graph.
//   component: Will receive nodes that comprise the current component.
static void FindConnectedNodes(std::vector<Node>& graph, unsigned node_id, Component& component) {

    Node& node = graph[node_id];
    if (!node.Traversed) {

        node.Traversed = true;
        component.push_back(node_id);

        for (auto i = node.Children.begin(); i != node.Children.end(); ++i)
            FindConnectedNodes(graph, *i, component);

        for (auto i = node.Parents.begin(); i != node.Parents.end(); ++i)
            FindConnectedNodes(graph, *i, component);

    }

}

// Finds self-connected sub-graphs (i.e. "components") on already-prepared graph.
std::vector<Component> FindGraphComponents(std::vector<Node>& graph) {

    std::vector<Component> components;
    for (unsigned node_id = 0; node_id < graph.size(); ++node_id) {
        if (!graph[node_id].Traversed) {
            components.push_back(Component());
            FindConnectedNodes(graph, node_id, components.back());
        }
    }

    return components;

}

// Finds self-connected sub-graphs (i.e. "components") on graph that should be read from the input stream.
//   in: The input test case.
std::vector<Component> FindGraphComponents(std::istream& in) {

    unsigned node_count, edge_count;
    std::cin >> node_count >> edge_count;

    // First build the structure that can be traversed recursively in an efficient way.
    std::vector<Node> graph(node_count); // Index in this vector corresponds to node ID.
    for (unsigned i = 0; i < edge_count; ++i) {
        unsigned from, to;
        in >> from >> to;
        graph[from].Children.push_back(to);
        graph[to].Parents.push_back(from);
    }

    return FindGraphComponents(graph);

}

void main() {

    size_t test_case_count;
    std::cin >> test_case_count;

    for (size_t test_case_i = 1; test_case_i <= test_case_count; ++test_case_i) {

        auto components = FindGraphComponents(std::cin);

        // Sort components by descending size and print them.
        std::sort(
            components.begin(),
            components.end(),
            [] (const Component& a, const Component& b) { return a.size() > b.size(); }
        );

        std::cout << "Case " << test_case_i <<  ": " << components.size() << " component (s) connected (s)" << std::endl;
        for (auto components_i = components.begin(); components_i != components.end(); ++components_i) {
            for (auto edge_i = components_i->begin(); edge_i != components_i->end(); ++edge_i)
                std::cout << *edge_i << ' ';
            std::cout << std::endl;
        }
        std::cout << std::endl;

    }

}

Call this program as...

GraphComponents.exe < input.in > output.out

...where input.in contains data in format described in your question and it will produce the desired result in output.out.

你对谁都笑 2024-12-19 08:16:08

解决方案更简单,您必须声明两个大小为顶点数的数组

int vertexNodes  [vertex] / / / array to store the nodes
int vertexComponents [vertex] / / / array to store the number of components

然后,当您调用DFS时,每个顶点都存储在顶点数组中,并存储在该组件所属的位置

for( int i = 0 ; i < vertex ; ++i ) //iterate on all vertices
        {
                vertexNodes [i]=i;  //fill the array with the vertices of the graph
            if( !visited[ i ] )
            { ///If any node is visited DFS call
                    dfs(i);
                totalComponents++; ///increment number of components
            }
            vertexComponents [i]=totalComponents; ///is stored at each node component belongs to
        }

最后,它打印总组件并创建一个标记为与每个顶点的分量进行比较的第一个分量的值

printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);
int flag = vertexComponents[0]; ///Create a flag with the value of the first component
            for (k=0; k <totalComponents; ++k) ///do a cycle length of the number of components
            {
                if (flag == vertexComponents [k] ) ///check if the vertex belongs to the first component
                {
                    printf ("%d ", vertexComponents[k]); ///print on the same line as belonging to the same component
                }else {
                    printf ("\n"); ///else  we make newline and update the flag to the next component
                    flag = vertexComponents[k];
                    printf ("%d ", vertexComponents[k]);///and print the vertices of the new connected component
                }
            }

the solution is much easier, you must declare two arrays of size the number of vertices

int vertexNodes  [vertex] / / / array to store the nodes
int vertexComponents [vertex] / / / array to store the number of components

Then, when you call to DFS each vertex is stored in the array of vertices, and stored at that component belongs

for( int i = 0 ; i < vertex ; ++i ) //iterate on all vertices
        {
                vertexNodes [i]=i;  //fill the array with the vertices of the graph
            if( !visited[ i ] )
            { ///If any node is visited DFS call
                    dfs(i);
                totalComponents++; ///increment number of components
            }
            vertexComponents [i]=totalComponents; ///is stored at each node component belongs to
        }

Finally, it prints the total components and created a flag with the value of the first component which is compared with the component of each vertex

printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);
int flag = vertexComponents[0]; ///Create a flag with the value of the first component
            for (k=0; k <totalComponents; ++k) ///do a cycle length of the number of components
            {
                if (flag == vertexComponents [k] ) ///check if the vertex belongs to the first component
                {
                    printf ("%d ", vertexComponents[k]); ///print on the same line as belonging to the same component
                }else {
                    printf ("\n"); ///else  we make newline and update the flag to the next component
                    flag = vertexComponents[k];
                    printf ("%d ", vertexComponents[k]);///and print the vertices of the new connected component
                }
            }
执手闯天涯 2024-12-19 08:16:08

您可以像这样存储组件:

typedef vector<int> Component;
vector<Component> components;

并修改代码:

void dfs(int u){
    components.back().push_back(u);
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

for( int i = 0 ; i < vertex ; ++i ){    // Loop through all possible vertex
    if( !visited[ i ] ){          //if we have not visited any one component from that node
        components.push_back(Component());
        dfs( i );                  //we travel from node i the entire graph is formed
    }
}

现在totalComponents是components.size():

printf("Case %d: %d component (s) connected (s)\n" ,cont++, components.size());

        for (j=0;j<components.size();j++){
           Component& component = components[j];
           std::sort(component.begin(), component.end());
           for(int k=0; k<component.size(); k++) {
             printf("%d ", component[k]);
           }
           printf("\n");
        }
        components.clear();

请注意,代码未经测试。包含 来获取排序函数。

You could store the components like this:

typedef vector<int> Component;
vector<Component> components;

and modify the code:

void dfs(int u){
    components.back().push_back(u);
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

for( int i = 0 ; i < vertex ; ++i ){    // Loop through all possible vertex
    if( !visited[ i ] ){          //if we have not visited any one component from that node
        components.push_back(Component());
        dfs( i );                  //we travel from node i the entire graph is formed
    }
}

now totalComponents is components.size() :

printf("Case %d: %d component (s) connected (s)\n" ,cont++, components.size());

        for (j=0;j<components.size();j++){
           Component& component = components[j];
           std::sort(component.begin(), component.end());
           for(int k=0; k<component.size(); k++) {
             printf("%d ", component[k]);
           }
           printf("\n");
        }
        components.clear();

Note that the code is not tested. Include <algorithm> to get the sort function.

不知在何时 2024-12-19 08:16:08

测试 2 个节点是否连接的通用算法:

  1. 将整个图分割成边。将每条边添加到一个集合中。
  2. 在下一次迭代中,在步骤 2 中创建的边的 2 个外部节点之间绘制边。这意味着将新节点(及其相应的集合)添加到原始边所在的集合中。 (基本上是集合合并)
  3. 重复2,直到您要查找的2个节点位于同一集合中。您还需要在步骤 1 之后进行检查(以防 2 个节点相邻)。

首先,您的节点将各自位于其集合中,

o   o1   o   o   o   o   o   o2
 \ /     \ /     \ /     \ /
 o o     o o     o o     o o
   \     /         \     /
   o o o o         o o o o 
      \               /
       o o1 o o o o o o2

随着算法的进展并合并集合,它相对将输入减半。

在上面的示例中,我想查看 o1 和 o2 之间是否存在路径。我仅在合并所有边后才在最后找到这条路径。有些图表可能有单独的组件(断开连接),这意味着您最终无法拥有一组。在这种情况下,您可以使用此算法来测试连通性,甚至计算图中的组件数量。组件的数量是算法完成时您能够获得的集合的数量。

一个可能的图表(对于上面的树):

o-o1-o-o-o2
  |    |
  o    o
       |
       o

General algorithm to test if 2 nodes are connected:

  1. Split your entire graph into edges. Add each edge to a set.
  2. On next iteration, draw edges between the 2 outer nodes of the edge you made in step 2. This means adding new nodes (with their corresponding sets) to the set the original edge was from. (basically set merging)
  3. Repeat 2 until the 2 nodes you're looking for are in the same set. You will also need to do a check after step 1 (just in case the 2 nodes are adjacent).

At first your nodes will be each in its set,

o   o1   o   o   o   o   o   o2
 \ /     \ /     \ /     \ /
 o o     o o     o o     o o
   \     /         \     /
   o o o o         o o o o 
      \               /
       o o1 o o o o o o2

As the algorithm progresses and merges the sets, it relatively halves the input.

In the example above I was looking to see if there was a path between o1 and o2. I found this path only at the end after merging all edges. Some graphs may have separate components (disconnected) which entails that you will not be able to have one set at the end. In such a case you can use this algorithm to test for connectedness and even count the number of components in a graph. The number of components is the number of sets you are able to get when the algorithm finishes.

A possible graph (for the tree above):

o-o1-o-o-o2
  |    |
  o    o
       |
       o
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文