[英]Graph implementation C++
我想知道在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行代碼。
有什么'好'的實施?
下面是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);
}
這實際上取決於你需要實現什么算法,沒有靈丹妙葯(這不應該是一個驚喜......關於編程的一般規則是沒有一般規則;-))。
我經常最終使用帶指針的節點/邊結構來表示有向多圖...更具體地說:
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個指針)。
可以在此處找到類似的結構完整實現。
這個問題很古老,但由於某種原因,我似乎無法理解它。
雖然所有解決方案都提供了圖表的實現,但它們也非常冗長。 它們根本不優雅。
所有你真正需要的是一種方法來告訴一個點連接到另一個 - 而不是發明你自己的圖形類 - 為此, 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
不是索引 - 它們是標識符。
我更喜歡使用Indices的鄰接列表(不是指針)
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 );
}
可以有一個更簡單的表示,假設一個人必須只測試圖算法不使用它們(圖)其他地方。 這可以是從頂點到其鄰接列表的映射,如下所示: -
#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;
}
}
這是圖的基本實現。 注意:我使用鏈接到下一個頂點的頂點。 每個頂點都有一個指向相鄰節點的列表。
#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等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.