Ir para o conteúdo

Classe de Grafos

FIXME este tutorial precisa de revisão para estar de acordo com as Regras de Estilo e Formatação.

Esta é uma classe de grafos bastante simples. Tem um pequeno exemplo na função web() e main().

O resulado devolvido pelo membro add_node é um id do nó (um pouco como o int é usado nos file descriptors).

Nota: Faltam ainda alguns métodos importantes da classe.

graph.h:

/*
 *  GPL
 *
 *  graph - Written by Diogo Sousa aka orium
 *  Copyright (C) 2008 Diogo Sousa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef GRAPH_H_
#define GRAPH_H_

#include <iostream>
#include <vector>
#include <set>

class graph
{
private:

    struct node_info
    {
        void *data;

        int node_no;

        struct neighbour_info
        {
            node_info *node;
            double weight;
            bool walkable;
        };

        std::vector<struct neighbour_info> neighbour;

        int neighbours;
    };

    std::set<node_info *> main_node;
    int main_nodes;

    int total_nodes;
    int nodes_seq_no;

    void (*for_each_node_free_callback)(void *);

    node_info *depth_first_search_recursive(node_info *node,
                        std::set<int> &visited,
                        bool (*cmp)(const node_info *, const void *),
                        const void *needle) const;

    node_info *depth_first_search(bool (*cmp)(const node_info *, const void *),
                      const void *needle) const;
    node_info *depth_first_search_in_sub_graph(graph::node_info *node,
                           bool (*cmp)(const node_info *, const void *),
                           const void *needle) const;

    bool are_nodes_in_connected_graph(node_info *node1, int node2) const;
    bool are_nodes_adjacent(node_info *node1, int node2) const;

    void set_node_in_main_graphs(node_info *node);

    void link_node(node_info *n1, node_info *n2, bool walkable, double weight);
    void unlink_node(node_info *node1, node_info *node2);

    void print_graph_recursive(std::ostream &out, graph::node_info *node, std::set<int> &visited) const;

    void free_everything_recursive(node_info *node, std::set<node_info *> &visited);


    static bool search_by_node_no(const node_info *node, const void *needle);

public:
    enum link_type
    {
        UNIDIRECTIONAL,
        BIDIRECTIONAL
    };

    graph(void);
    graph(void (*for_each_node_callback)(void *));
    // Copy constructor
    // List of lastest nodes to be used as cache
    ~graph(void);

    int add_node(void *data=NULL);
    int link_nodes(int node1, int node2, enum link_type link_type=BIDIRECTIONAL, double weight=0.0);
    //* rm_node(int node);
    int unlink_nodes(int node1, int node2);

    // operator =
    // operator ==
    // operator !=

    //std::vector<int> &get_shortest_path(int node1, int node2, float &distance) const;
    //std::vector<int> &get_shortest_path(int node1, int node2) const;
    //+ get_node_data(int node) const;
    //get_shortest_distance(int node1, int node2) const;
    //+ std::set get_adjacent_nodes(int node) const;
    //+ get_node_degree(int node) const;
    //+ get_node_by_data(void *data, bool (*cmp)(void *d1, void *d2)) const;
    void print_graph(std::ostream &out=std::cout) const;
    bool are_nodes_in_connected_graph(int node1, int node2) const;
    bool are_nodes_adjacent(int node1, int node2) const;
};

#endif

graph.cc:

/*
 *  GPL
 *
 *  graph - Written by Diogo Sousa aka orium
 *  Copyright (C) 2008 Diogo Sousa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

#include "graph.h"

graph::graph(void)
{
    main_nodes=0;
    total_nodes=0;
    nodes_seq_no=0;

    for_each_node_free_callback=NULL;
}

graph::graph(void (*for_each_node_free_callback)(void *))
{
    main_nodes=0;
    total_nodes=0;
    nodes_seq_no=0;

    this->for_each_node_free_callback=for_each_node_free_callback;
}

graph::~graph(void)
{
    std::set<node_info *> visited;
    std::set<node_info *>::const_iterator i;

    for (i=main_node.begin(); i != main_node.end(); i++)
    {
        visited.erase(visited.begin(),visited.end()); // This is an independent graph
        visited.insert(*i);

        free_everything_recursive(*i,visited);

        if (for_each_node_free_callback)
            for_each_node_free_callback((*i)->data);

        delete *i;
    }
}

void graph::free_everything_recursive(node_info *node,
                      std::set<node_info *> &visited)
{
    std::vector<node_info::neighbour_info>::const_iterator i;

    for (i=node->neighbour.begin(); i != node->neighbour.end(); i++)
        if (!binary_search(visited.begin(),visited.end(),i->node))
        {
            visited.insert(i->node);
            free_everything_recursive(i->node,visited);

            if (for_each_node_free_callback)
                for_each_node_free_callback(i->node->data);

            delete i->node;
        }
}

graph::node_info *graph::depth_first_search_recursive(node_info *node,
                              std::set<int> &visited,
                              bool (*cmp)(const node_info *, const void *),
                              const void *needle) const
{
    std::vector<node_info::neighbour_info>::const_iterator i;
    node_info *r;

    for (i=node->neighbour.begin(); i != node->neighbour.end(); i++)
    {
        if (cmp(i->node,needle))
            return i->node;

        if (!binary_search(visited.begin(),visited.end(),i->node->node_no))
        {
            visited.insert(i->node->node_no);
            if ((r=depth_first_search_recursive(i->node,visited,cmp,needle)))
                return r;
        }
    }

    return NULL;
}

graph::node_info *graph::depth_first_search(bool (*cmp)(const node_info *, const void *),
                        const void *needle) const 
{
    std::set<int> visited;
    std::set<node_info *>::const_iterator i;
    node_info *r;

    for (i=main_node.begin(); i != main_node.end(); i++)
    {
        visited.erase(visited.begin(),visited.end()); // This is an independent graph
        visited.insert((*i)->node_no);

        if (cmp(*i,needle))
            return *i;

        if ((r=depth_first_search_recursive(*i,visited,cmp,needle)))
            return r;
    }

    return NULL;
}

graph::node_info *graph::depth_first_search_in_sub_graph(graph::node_info *node,
                             bool (*cmp)(const node_info *, const void *),
                             const void *needle) const
{
    std::set<int> visited;
    node_info *r;

    visited.erase(visited.begin(),visited.end());
    visited.insert(node->node_no);

    if (cmp(node,needle))
        return node;

    if ((r=depth_first_search_recursive(node,visited,cmp,needle)))
        return r;

    return NULL;
}

bool graph::search_by_node_no(const node_info *node, const void *needle)
{
    return node->node_no == *static_cast<const int *>(needle);
}

bool graph::are_nodes_in_connected_graph(node_info *node1, int node2) const
{
    return depth_first_search_in_sub_graph(node1,&search_by_node_no,(const void *)&node2);
}

bool graph::are_nodes_in_connected_graph(int node1, int node2) const
{
    node_info *n1;

    n1=depth_first_search(&search_by_node_no,(const void *)&node1);

    return are_nodes_in_connected_graph(n1,node2);
}

// Links node1 to node2 
// Ex:
//   node1 -------> node2
//   node2 - ||| -> node1
void graph::link_node(node_info *n1, node_info *n2, bool walkable, double weight)
{
    node_info::neighbour_info neighbour;

    if (are_nodes_adjacent(n1,n2->node_no))
        return;

    neighbour.node=n2;
    neighbour.weight=weight;
    neighbour.walkable=walkable;

    n1->neighbour.push_back(neighbour);

    n1->neighbours++;
}

void graph::set_node_in_main_graphs(node_info *node)
{
    main_node.insert(node);

    main_nodes++;
}

int graph::add_node(void *data)
{
    node_info *new_node;

    new_node=new node_info;

    new_node->data=data;
    new_node->node_no=nodes_seq_no;
    new_node->neighbours=0;

    set_node_in_main_graphs(new_node);

    total_nodes++;

    return nodes_seq_no++;
}

int graph::link_nodes(int node1, int node2, enum link_type link_type, double weight)
{
    node_info *n1;
    node_info *n2;

    n1=depth_first_search(&search_by_node_no,(const void *)&node1);
    n2=depth_first_search(&search_by_node_no,(const void *)&node2);

    if (!n1 || !n2)
        return -1;

    if (!are_nodes_in_connected_graph(n1,node2))
    {
        // The graph n2 must be in one of the main graphs
        std::set<node_info *>::iterator i;

        for (i=main_node.begin(); i != main_node.end(); i++)
            if (depth_first_search_in_sub_graph(*i,&search_by_node_no,(const void *)&node2))
            {
                                // We can remove the entire graph because it will now be all connected to node1
                main_node.erase(i);
                main_nodes--;

                break;
            }
    }

    link_node(n1,n2,link_type == UNIDIRECTIONAL || link_type == BIDIRECTIONAL,weight);
    link_node(n2,n1,link_type == BIDIRECTIONAL,weight);

    return 0;
}

bool graph::are_nodes_adjacent(node_info *node1, int node2) const
{
    std::vector<node_info::neighbour_info>::const_iterator i;

    for (i=node1->neighbour.begin(); i != node1->neighbour.end(); i++)
        if (i->node->node_no == node2)
            return true;

    return false;
}

bool graph::are_nodes_adjacent(int node1, int node2) const
{
    return are_nodes_adjacent(depth_first_search(&search_by_node_no,(const void *)&node1),node2);
}

// Unlinks node1 from node2 
// Ex:
//   node1 - ||| -> node2
//   node2 -------> node1
void graph::unlink_node(node_info *node1, node_info *node2)
{
    std::vector<node_info::neighbour_info>::iterator i;

    for (i=node1->neighbour.begin(); i != node1->neighbour.end(); i++)
        if (i->node == node2)
        {
            node1->neighbour.erase(i);
            node1->neighbours--;
            break;
        }
}

int graph::unlink_nodes(int node1, int node2)
{
    node_info *n1;
    node_info *n2;

    n1=depth_first_search(&search_by_node_no,(const void *)&node1);
    n2=depth_first_search(&search_by_node_no,(const void *)&node2);

    if (!n1 || !n2)
        return -1;

    if (!are_nodes_adjacent(n1,node2))
        return 0; // If you want to make this an error return -1 instead

    unlink_node(n1,n2);
    unlink_node(n2,n1);

    if (!depth_first_search(&search_by_node_no,(const void *)&node1))
        set_node_in_main_graphs(n1);

    if (!depth_first_search(&search_by_node_no,(const void *)&node2))
        set_node_in_main_graphs(n2);

    return 0;
}

void graph::print_graph_recursive(std::ostream &out, graph::node_info *node, std::set<int> &visited) const
{
    std::vector<node_info::neighbour_info>::const_iterator i;

    if (!node->neighbours)
        std::cout << "Node " << node->node_no << " links nowhere" << std::endl;

    for (i=node->neighbour.begin(); i != node->neighbour.end(); i++)
    {
        std::cout << "Node " << node->node_no << " links to " << i->node->node_no << std::endl;

        if (!binary_search(visited.begin(),visited.end(),i->node->node_no))
        {
            visited.insert(i->node->node_no);           
            print_graph_recursive(out,i->node,visited);
        }
    }
}

void graph::print_graph(std::ostream &out) const
{
    std::set<int> visited;
    std::set<node_info *>::const_iterator i;

    for (i=main_node.begin(); i != main_node.end(); i++)
    {
        visited.erase(visited.begin(),visited.end()); // This is an independent graph
        visited.insert((*i)->node_no);

        print_graph_recursive(out,*i,visited);
    }
}

void web(void) // Just for fun
{
    graph my_graph;
    std::vector<int> n;
    int i;

    for (i=0; i < 11; i++)
        n.push_back(my_graph.add_node());

    for (i=1; i <= 5; i++) // A web :)
    {
        my_graph.link_nodes(n[0],n[i]);
        my_graph.link_nodes(n[i],n[i%5+1]);
        my_graph.link_nodes(n[i],n[i+5]);
        my_graph.link_nodes(n[i+5],n[(i+5)%10+1]);
    }

    my_graph.print_graph();
}

int main(void)
{
    graph my_graph;
    int a;
    int b;
    int c;
    int d;
    int e;
    int f;
    int g;
    int h;
    int i;

    a=my_graph.add_node();
    b=my_graph.add_node();
    c=my_graph.add_node();
    d=my_graph.add_node();
    e=my_graph.add_node();
    f=my_graph.add_node();
    g=my_graph.add_node();
    h=my_graph.add_node();
    i=my_graph.add_node();

    my_graph.link_nodes(a,b,graph::BIDIRECTIONAL,7.0);
    my_graph.link_nodes(b,c,graph::BIDIRECTIONAL,8.0);
    my_graph.link_nodes(a,d,graph::BIDIRECTIONAL,5.0);
    my_graph.link_nodes(d,b,graph::BIDIRECTIONAL,9.0);
    my_graph.link_nodes(b,e,graph::BIDIRECTIONAL,7.0);
    my_graph.link_nodes(c,e,graph::BIDIRECTIONAL,5.0);
    my_graph.link_nodes(d,e,graph::BIDIRECTIONAL,15.0);
    my_graph.link_nodes(d,f,graph::BIDIRECTIONAL,6.0);
    my_graph.link_nodes(e,f,graph::BIDIRECTIONAL,8.0);
    my_graph.link_nodes(e,g,graph::BIDIRECTIONAL,9.0);
    my_graph.link_nodes(f,g,graph::BIDIRECTIONAL,11.0);

    my_graph.unlink_nodes(f,g);

    my_graph.link_nodes(h,i);

    my_graph.unlink_nodes(h,i);

    my_graph.print_graph();

    return 0;
}