我想知道用c ++快速编写图表的实现。我需要数据结构易于操作和使用图形算法(例如BFS,DFS,Kruskal,Dijkstra ......)。 我需要这个算法Olympiad的实现,所以更容易编写数据结构。
你能否提出这样的DS(主要结构或类别及其中的内容)。我知道邻接列表和邻接矩阵是主要的可能性,但我的意思是更详细的代码样本。
例如,上次我必须为DFS实现图表时,我想到了这个DS:
struct Edge {
int start;
int end;
struct Edge* nextEdge;
}
然后使用一个大小为n的数组,在第i个位置包含表示从第i个节点开始的边的边缘列表(struct Edge)。
但是当在这个图上尝试DFS时,我不得不用大约10个while循环编写一个50行代码。
有什么'好'的实施?
答案 0 :(得分:40)
下面是C ++中图形数据结构作为邻接列表的实现。
我使用STL向量表示顶点,使用STL对表示边缘和目标顶点。
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
struct vertex {
typedef pair<int, vertex*> ve;
vector<ve> adj; //cost of edge, destination vertex
string name;
vertex(string s) : name(s) {}
};
class graph
{
public:
typedef map<string, vertex *> vmap;
vmap work;
void addvertex(const string&);
void addedge(const string& from, const string& to, double cost);
};
void graph::addvertex(const string &name)
{
vmap::iterator itr = work.find(name);
if (itr == work.end())
{
vertex *v;
v = new vertex(name);
work[name] = v;
return;
}
cout << "\nVertex already exists!";
}
void graph::addedge(const string& from, const string& to, double cost)
{
vertex *f = (work.find(from)->second);
vertex *t = (work.find(to)->second);
pair<int, vertex *> edge = make_pair(cost, t);
f->adj.push_back(edge);
}
答案 1 :(得分:32)
这实际上取决于你需要实现什么算法,没有银弹(这不应该是一个惊喜......关于编程的一般规则是没有一般规则;-))。
我经常最终使用带指针的节点/边结构来表示有向多图...更具体地说:
struct Node
{
... payload ...
Link *first_in, *last_in, *first_out, *last_out;
};
struct Link
{
... payload ...
Node *from, *to;
Link *prev_same_from, *next_same_from,
*prev_same_to, *next_same_to;
};
换句话说,每个节点都有一个双向链接的传入链接列表和一个双向链接的传出链接列表。每个链接都知道from
和to
个节点,并且同时位于两个不同的双向链接列表中:来自同一from
节点的所有链接列表以及所有链接列表到达同一to
节点的链接。
在跟随从同一节点出现的所有链接链时,会使用指针prev_same_from
和next_same_from
;在管理指向到同一节点的所有链接的链时,会使用指针prev_same_to
和next_same_to
。
这是很多指针的错误(因此,除非你喜欢指针,否则只是忘记这一点)但查询和更新操作是有效的;例如,添加节点或链接是O(1),删除链接是O(1)并删除节点x是O(deg(x))。
当然,根据问题,有效负载大小,图形大小,图形密度,这种方法可能过度使用或对内存要求过高(除了有效负载,每个节点有4个指针,每个链接有6个指针)。 / p>
可以找到类似的结构完整实现here。
答案 2 :(得分:10)
这个问题很古老,但由于某些原因,我似乎无法理解它。
虽然所有解决方案都提供了图表的实现,但它们也非常冗长。它们简直不优雅。
而不是发明自己的图表类,而真正需要的是一种告诉一个点与另一个点连接的方法 - 为此,std::map
和std::unordered_map
工作非常好。简单地说,将图形定义为节点和边缘列表之间的映射。如果您不需要边缘的额外数据,那么终端节点列表就可以了。
因此,C ++中的简洁图可以像这样实现:
using graph = std::map<int, std::vector<int>>;
或者,如果您需要其他数据,
struct edge {
int nodes[2];
float cost; // add more if you need it
};
using graph = std::map<int, std::vector<edge>>;
现在你的图形结构将很好地插入到语言的其余部分,你不必记住任何新的笨重的界面 - 旧的笨重的界面会很好。
没有基准,但我觉得这也会胜过其他建议。
注意:int
不是索引 - 它们是标识符。
答案 3 :(得分:8)
最常见的陈述可能是这两个:
在这两个中,adjacency matrix是最简单的,只要你不介意有一个(可能是巨大的)n * n
数组,其中n
是顶点的数量。根据阵列的基本类型,您甚至可以存储边缘权重以用于例如最短路径发现算法。
答案 4 :(得分:3)
我更喜欢使用指数(非指针)的邻接列表
typedef std::vector< Vertex > Vertices;
typedef std::set <int> Neighbours;
struct Vertex {
private:
int data;
public:
Neighbours neighbours;
Vertex( int d ): data(d) {}
Vertex( ): data(-1) {}
bool operator<( const Vertex& ref ) const {
return ( ref.data < data );
}
bool operator==( const Vertex& ref ) const {
return ( ref.data == data );
}
};
class Graph
{
private :
Vertices vertices;
}
void Graph::addEdgeIndices ( int index1, int index2 ) {
vertices[ index1 ].neighbours.insert( index2 );
}
Vertices::iterator Graph::findVertexIndex( int val, bool& res )
{
std::vector<Vertex>::iterator it;
Vertex v(val);
it = std::find( vertices.begin(), vertices.end(), v );
if (it != vertices.end()){
res = true;
return it;
} else {
res = false;
return vertices.end();
}
}
void Graph::addEdge ( int n1, int n2 ) {
bool foundNet1 = false, foundNet2 = false;
Vertices::iterator vit1 = findVertexIndex( n1, foundNet1 );
int node1Index = -1, node2Index = -1;
if ( !foundNet1 ) {
Vertex v1( n1 );
vertices.push_back( v1 );
node1Index = vertices.size() - 1;
} else {
node1Index = vit1 - vertices.begin();
}
Vertices::iterator vit2 = findVertexIndex( n2, foundNet2);
if ( !foundNet2 ) {
Vertex v2( n2 );
vertices.push_back( v2 );
node2Index = vertices.size() - 1;
} else {
node2Index = vit2 - vertices.begin();
}
assert( ( node1Index > -1 ) && ( node1Index < vertices.size()));
assert( ( node2Index > -1 ) && ( node2Index < vertices.size()));
addEdgeIndices( node1Index, node2Index );
}
答案 5 :(得分:1)
假设一个人只需要测试图算法而不使用它们(图形),那么可以有一个更简单的表示。这可以是从顶点到其邻接列表的映射,如下所示: -
#include<bits/stdc++.h>
using namespace std;
/* implement the graph as a map from the integer index as a key to the adjacency list
* of the graph implemented as a vector being the value of each individual key. The
* program will be given a matrix of numbers, the first element of each row will
* represent the head of the adjacency list and the rest of the elements will be the
* list of that element in the graph.
*/
typedef map<int, vector<int> > graphType;
int main(){
graphType graph;
int vertices = 0;
cout << "Please enter the number of vertices in the graph :- " << endl;
cin >> vertices;
if(vertices <= 0){
cout << "The number of vertices in the graph can't be less than or equal to 0." << endl;
exit(0);
}
cout << "Please enter the elements of the graph, as an adjacency list, one row after another. " << endl;
for(int i = 0; i <= vertices; i++){
vector<int> adjList; //the vector corresponding to the adjacency list of each vertex
int key = -1, listValue = -1;
string listString;
getline(cin, listString);
if(i != 0){
istringstream iss(listString);
iss >> key;
iss >> listValue;
if(listValue != -1){
adjList.push_back(listValue);
for(; iss >> listValue; ){
adjList.push_back(listValue);
}
graph.insert(graphType::value_type(key, adjList));
}
else
graph.insert(graphType::value_type(key, adjList));
}
}
//print the elements of the graph
cout << "The graph that you entered :- " << endl;
for(graphType::const_iterator iterator = graph.begin(); iterator != graph.end(); ++iterator){
cout << "Key : " << iterator->first << ", values : ";
vector<int>::const_iterator vectBegIter = iterator->second.begin();
vector<int>::const_iterator vectEndIter = iterator->second.end();
for(; vectBegIter != vectEndIter; ++vectBegIter){
cout << *(vectBegIter) << ", ";
}
cout << endl;
}
}
答案 6 :(得分:0)
这是图表的基本实现。 注意:我使用链接到下一个顶点的顶点。每个顶点都有一个指向相邻节点的列表。
#include <iostream>
using namespace std;
// 1 ->2
// 1->4
// 2 ->3
// 4->3
// 4 -> 5
// Adjacency list
// 1->2->3-null
// 2->3->null
//4->5->null;
// Structure of a vertex
struct vertex {
int i;
struct node *list;
struct vertex *next;
};
typedef struct vertex * VPTR;
// Struct of adjacency list
struct node {
struct vertex * n;
struct node *next;
};
typedef struct node * NODEPTR;
class Graph {
public:
// list of nodes chained together
VPTR V;
Graph() {
V = NULL;
}
void addEdge(int, int);
VPTR addVertex(int);
VPTR existVertex(int i);
void listVertex();
};
// If vertex exist, it returns its pointer else returns NULL
VPTR Graph::existVertex(int i) {
VPTR temp = V;
while(temp != NULL) {
if(temp->i == i) {
return temp;
}
temp = temp->next;
}
return NULL;
}
// Add a new vertex to the end of the vertex list
VPTR Graph::addVertex(int i) {
VPTR temp = new(struct vertex);
temp->list = NULL;
temp->i = i;
temp->next = NULL;
VPTR *curr = &V;
while(*curr) {
curr = &(*curr)->next;
}
*curr = temp;
return temp;
}
// Add a node from vertex i to j.
// first check if i and j exists. If not first add the vertex
// and then add entry of j into adjacency list of i
void Graph::addEdge(int i, int j) {
VPTR v_i = existVertex(i);
VPTR v_j = existVertex(j);
if(v_i == NULL) {
v_i = addVertex(i);
}
if(v_j == NULL) {
v_j = addVertex(j);
}
NODEPTR *temp = &(v_i->list);
while(*temp) {
temp = &(*temp)->next;
}
*temp = new(struct node);
(*temp)->n = v_j;
(*temp)->next = NULL;
}
// List all the vertex.
void Graph::listVertex() {
VPTR temp = V;
while(temp) {
cout <<temp->i <<" ";
temp = temp->next;
}
cout <<"\n";
}
// Client program
int main() {
Graph G;
G.addEdge(1, 2);
G.listVertex();
}
使用上面的代码,您可以扩展为DFS / BFS等。