如何用c ++表示图形

时间:2016-04-24 19:28:50

标签: c++ graph

我想写prims和dijkstra algoritms创建一个MST。但我不知道用c ++表示图形的最佳方法是什么。

我可以通过两个整数对表示一个边,例如,向量0到1将是对(0,1);

typedef pair<int, int> Edge;

然后prims函数将采用由边和它的权重组成的对的Vector。

void prims(vector<pair<Edge, int>>);

我认为这种方式不是最好的方式,有人能告诉我哪种方式最能代表图表吗?

3 个答案:

答案 0 :(得分:1)

我前段时间一直在实施Dijkstra,用于在二进制图像中查找路径。我将图表表示为结构GraphNodes的向量,其中包含Connections向量,其中包含节点与其他节点的所有连接。每个连接都有其距离属性,即边缘的权重。以下是我使用的两种结构:

//forward declaration
struct GraphNode;
struct Connection {
    Connection() : distance(1) { };
    Connection(GraphNode* ptr, double distance) : ptr(ptr), distance(distance) { };
    bool operator==(const Connection &other) const;
    GraphNode* ptr;
    double distance;
};

struct GraphNode {
    GraphNode() : connections(8), predecessor(NULL), distance(-1) { };
    cv::Point point;
    double distance;
    GraphNode* predecessor;
    std::vector<Connection> connections;
};

bool Connection::operator==(const Connection &other) const {
    return ptr == other.ptr && distance == other.distance;
}

GraphNode的距离属性是它目前在Dijkstra算法中的距离,因此是当前已知距离与起始节点之间的距离。在开始时,这会使用-1初始化。

然后我实现了Dijkstra算法:

std::vector<cv::Point> findShortestPathDijkstra(std::vector<GraphNode>& graph, int startNodeIndex, int destNodeIndex) const {
    GraphDistanceSorter sorter(graph);
    std::set<GraphNode*, GraphDistanceSorter> unusedNodes(sorter);
    for (int i = 0; i < graph.size(); ++i) {
        unusedNodes.insert(&graph[i]);
    }

    while (unusedNodes.size() > 0) {

        GraphNode* currentNode = *unusedNodes.begin();
        if (currentNode->distance == -1) {
            return std::vector<cv::Point>();
        }
        if (currentNode == &graph[destNodeIndex]) break;
        unusedNodes.erase(currentNode);
        //update distances of connected nodes
        for (Connection const& con : currentNode->connections) {
            /*here we could check if the element is really in unusedNodes (search, O(log n)), but this would
            actually take longer than calculating the new distance (O(1)), which will in this case always be greater
            than the old one, so the distance is never updated for nodes not in unusedNodes ()*/
            double newDistance = currentNode->distance + con.distance;
            if (newDistance < con.ptr->distance || con.ptr->distance == -1) {
                unusedNodes.erase(con.ptr);
                con.ptr->distance = newDistance;
                con.ptr->predecessor = currentNode;
                unusedNodes.insert(con.ptr);
            }
        }
    }

    //now trace back the path as a list of points
    std::vector<cv::Point> points;
    GraphNode* current = &graph[destNodeIndex];
    points.push_back(current->point);
    while (current != &graph[startNodeIndex]) {
        if (current->predecessor == NULL) return std::vector<cv::Point>();
        current = current->predecessor;
        points.push_back(current->point);
    }

    return points;

}

如您所见,到目前为止,有一个集合unusedNodes包含所有未使用的节点。它只包含graphNodes上的指针。实际的图形表示在向量中。具有集合的优点在于,它总是根据特定标准进行排序。我实现了自己的排序器GraphDistanceSorter,它根据Dijkstra算法的距离标准对GraphNodes进行排序。这样我只需从集合中选择第一个节点,并知道它是距离最小的节点:

struct GraphDistanceSorter {
    bool operator() (const GraphNode* lhs, const GraphNode* rhs) const;
};

bool GraphDistanceSorter::operator() (const GraphNode* lhs, const GraphNode* rhs) const {
    if (lhs->distance == rhs->distance) {
        return lhs < rhs;
    } else {
        if (lhs->distance != -1 && rhs->distance != -1) {
            if (lhs->distance != rhs->distance) {
                return lhs->distance < rhs->distance;
            }
        } else if (lhs->distance != -1 && rhs->distance == -1) {
            return true;
        }
        return false;
    }
}

答案 1 :(得分:1)

在理论计算机科学中学习图表的两种主要方式是adjacency matrixadjacency lists

邻接矩阵如下图所示是n * n矩阵,而[i] [j]表示节点i和节点j之间的边缘,因此如果它是加权图,则它可以是整数而不是布尔值未加权图的值。

adjacency matrix (photo source: google)

另一方面,邻接列表是一组链表(确切地说是n组),第i组具有我连接的节点。 在这种情况下,您将需要一些额外的方法来保存边距,例如您可以构建自己的类Edge,如下所示

class Edge
{
     int destination, length;
     Edge* next = 0;
}

并将其用于您的链接列表。我怎么习惯std::vector<std::pair<int, int>> a[N]来定义对列表,而a[i][j].first将是nod i的第j个邻居,a[i][j].second是它们之间边缘的长度。 对于无向图,您也可以将i添加到j个邻居。 因此,它也是一种表示图形的灵活方式。

adjacency lists (image source: google photos)

现在让我们谈谈复杂性,我会尽量保持简单:

我们有n个列表,每个都有#(边缘走出节点i) 所以总数是这个数字的总和,即边数的总数E. 这意味着与邻接矩阵中的O(N ^ 2)相比,位置复杂度为O(E),其在稀疏图中最多为5 * n。 (我们需要一个线性因子E来表示它)。 现在让我们考虑访问点头x的所有邻居: 在邻接矩阵中,我们将遍历整个第x行,如果它不是0,则在那里有一个边是O(N)。 在邻接列表中,它再次恰好是x的邻居的数量,其可以达到O(N)。 但是如果我们访问所有节点的所有邻居(更新dis数组时Dijkstra就是这种情况),则需要在邻接列表中访问n个元素n次,这也是邻接列表中的O(N ^ 2)时间复杂度它正好是邻居数量的总和 - 再次是E.这意味着我们还需要O(E)来访问所有边缘的所有邻居。 并且通常在输入O(E)中给出的所有边缘将作为计算时间传递,但是对于例如N <= 10 ^ 6的约束,O(N ^ 2)将是高复杂度。 最后,我将通过使用邻接列表(矢量作为列表)通常实现图表的不同变体:

#include<iostream>
#include<vector>
int main(){
    const int N = 5;
    int n, e;
    std::vector<std::pair<int, int>> graph[N], inverse[N];
    std::vector<int> unweighted[N], undirectedUnweighted[N];
    std::cin >> n >> e;
    for(int i = 0; i < e; i++)
    {
        int x, y, z;//z is length of edge
        std::cin >> x >> y >> z;
        //substitute 1 from x, y if they starts from 1
        graph[x].push_back(std::make_pair(y, z));
        inverse[y].push_back(std::make_pair(x, z));
        unweighted[x].push_back(y);
        undirectedUnweighted[x].push_back(y);
        undirectedUnweighted[y].push_back(x);
    }
    return 0;
}

答案 2 :(得分:0)

表示图的简单形式(查找顶点的邻域和度)


#include<iostream>

/** Representing graphs in c++ programming language */

using namespace std;

int main() {

    cout << "\033[1;33mNote: if there are no neighbourhood between vertices write '-' symbol!\033[0m\n"<<endl;

    int number_of_vertices;
    cout<<"\033[1;32mPlease enter number of vertices: \033[0m";
    cin>>number_of_vertices;

    int max_num_of_neighbours;
    cout<<"\033[1;32mPlease enter maximum number of neighbours: \033[0m";
    cin>>max_num_of_neighbours;

    char array[number_of_vertices][max_num_of_neighbours];

    char vertices[number_of_vertices];

    cout<<"\033[1;33mPlease sign vertices with lowercase alphabet letters: \033[0m"<<endl;

    for(int i = 0; i < number_of_vertices; i ++) {

        cout<<(i+1)<<" = ";
        cin>>vertices[i];

    }

    for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {

        cout<<"\033[1;32mPlease enter neighbours for \033[0m"<<vertices[i]<<" --> ";

        for(int j = 0; j < max_num_of_neighbours; j ++) {

            cin>>array[i][j];

        }

    }


    for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {

        cout<<"\033[1;34mNeighbours for \033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<" --> ";

        int deg = 0;

        for(int j = 0; j < max_num_of_neighbours; j ++) {

            if(array[i][j] != '-') {
                deg ++;
            }

            if(array[i][j] == '-') {
                cout<<"\033[1;31m"<<array[i][j]<<"\033[0m"<<"\t";
            } else {
                cout<<"\033[1;32m"<<array[i][j]<<"\033[0m"<<"\t";
            }

        }

        cout<<"\033[1;36m"<<"deg["<<"\033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<"\033[1;36m"<<"] = "<<"\033[0m"<<deg;

    }

    cout<<endl<<"\033[1;33mRemember that '\033[1;31m-\033[0m\033[1;33m' shows when two vertices aren't adjacent!\033[0m"<<endl;


}


为了增加交互性,我使用了How do I output coloured text to a Linux terminal?来更改文本的颜色