简体   繁体   English

在C ++中为有向图创建邻接列表

[英]Making an adjacency list in C++ for a directed graph

Hello all :) Today I am refining my skills on graph theory and data structures. 各位大家好:)今天我在图论和数据结构方面提炼技巧。 I decided to do a small project in C++ because it's been a while since I've worked in C++. 我决定用C ++做一个小项目,因为我在C ++工作已经有一段时间了。

I want to make an adjacency list for a directed graph. 我想为有向图制作一个邻接列表。 In other words, something which looks like: 换句话说,看起来像:

0-->1-->3
1-->2
2-->4
3-->
4-->

This would be a directed graph with V0 (vertex 0) having an edge to V1 and V3, V1 having an edge to V2, and V2 having an edge to V4, like this: 这将是有向图,其中V0(顶点0)具有到V1和V3的边缘,V1具有到V2的边缘,V2具有到V4的边缘,如下所示:

V0----->V1---->V2---->V4
 |
 |
 v
 V3 

I know that in order to do this, I will need to create an adjacency list in C++. 我知道为了做到这一点,我需要在C ++中创建一个邻接列表。 An adjacency list is basically an array of linked lists . 邻接列表基本上是链表的数组 Okay, let's see some pseudo C++ code: 好的,让我们看一些伪C ++代码:

#include <stdio>
#include <iostream>
using namespace std;

struct graph{
//The graph is essentially an array of the adjList struct.  
node* List[];

};

struct adjList{
//A simple linked list which can contain an int at each node in the list.

};

struct node {
int vertex;
node* next;
};

int main() {
//insert cool graph theory sorting algorithm here
}

As you can tell, this pseudocode is currently far from the mark. 如你所知,这个伪代码目前还远远不够。 And that is what i wanted some help with -- pointers and structs in C++ have never been my strong suit. 这就是我想要的一些帮助 - C ++中的指针和结构从来都不是我的强项。 First of all, this takes care of the vertices that a vertex points to -- but what about the vertex itself? 首先,这会处理顶点指向的顶点 - 但顶点本身呢? How can I keep track of that vertex? 如何跟踪该顶点? When I loop over the array, it will do me no good to only know what vertices are being pointed to, rather than also knowing what points to them. 当我圈在阵列上,这对我没什么好只知道被什么指向顶点,而不是还知道点什么他们。 The first element in each list should probably be that vertex, and then the elements after that are the vertices it points to. 每个列表中的第一个元素应该是该顶点,然后是之后的元素是它指向的顶点。 But then, how can I access this first element of the list in my main program? 但是,如何在主程序中访问列表的第一个元素? (sorry if this is convoluted or confusing, I would happy to rephrase). (对不起,如果这是令人费解或混乱,我很乐意改写)。

I would like to be able to loop over this adjacency list to do some cool things with graphs. 我希望能够遍历这个邻接列表,用图表做一些很酷的事情。 For example, to implement some graph theory algorithms (sorts, shortest paths, etc) using the adjacency list representation. 例如,使用邻接列表表示来实现一些图论理论算法(排序,最短路径等)。

(Also, I had a question about the adjacency list. What is different than just using a list of arrays? Why can't I just have a list with an array at each element in the list?) (另外,我有一个关于邻接列表的问题。与使用数组列表有什么不同?为什么我不能在列表中的每个元素上都有一个带有数组的列表?)

You may use a vector in node, as a adjacency list. 您可以在节点中使用向量作为邻接列表。

class node {
  int value;
  vector<node*> neighbors;
 };

If the graph is known at compile time, you can use array , but it's "a little bit" harder. 如果图形在编译时已知,则可以使用数组 ,但它“有点”难度。 If you know just size of graph (at compile time) you can do something like that. 如果您只知道图形的大小(在编译时),您可以执行类似的操作。

template<unsigned int N>
class graph {
  array<node, N> nodes;
 }

To add a neighbor, you doing something like that (do not forget numbering from zero): 要添加邻居,您可以执行类似的操作(不要忘记从零开始编号):

nodes[i].neighbors.push_back(nodes+j); //or &nodes[j]

Of course, you can do no-pointer adjacency list and work "above" a table. 当然,你可以做无指针邻接列表并在表格“上方”工作。 Than you have vector<int> in node and you pushing number of neighbour. 比你在节点中有vector<int>并且推送邻居的数量。 With both representation of the graph, you can realize all algorithms which use adjacency list. 通过图表的两种表示,您可以实现使用邻接列表的所有算法。

And finally, I might add. 最后,我可以补充一下。 Some use a list instead of a vector, because the removal is in O(1) time. 有些使用列表而不是向量,因为删除时间为O(1) Mistake. 错误。 For most algorithms, the order in the adjacency list is not important. 对于大多数算法,邻接列表中的顺序并不重要。 So you can erase any element from vector in O(1) time. 因此,您可以在O(1)时间内从向量中删除任何元素。 Just swap it with last element, pop_back is O(1) complexity. 只需将它与最后一个元素交换, pop_back就是O(1)复杂度。 Something like that: 像这样的东西:

if(i != number_of_last_element_in_list) //neighbors.size() - 1
 swap(neighbors[i], neighbor.back());
neighbors.pop_back();

Specific example (you have vector in node, C++11 (!)): 具体示例(节点中有向量,C ++ 11(!)):

//creation of nodes, as previously
constexpr unsigned int N = 3;
array<node,N> nodes; //or array<node, 3> nodes;
//creating edge (adding neighbors), in the constructor, or somewhere
nodes[0].neighbors = {&nodes[1]};
nodes[1].neighbors = {&nodes[0], &nodes[1]};
//adding runtime, i,j from user, eg. i = 2, j = 0
nodes[i].neighbors.push_back(&nodes[j]); //nodes[2].neighbors = {&nodes[0]};

I believe it's clear. 我相信这很清楚。 From 0 you can go to 1 , from 1 to 0 and to itself, and (as in eg.) from 2 to 0 . 0您可以转到1 ,从10 ,再到自身,并且(如例如)从20 It's directed graph. 这是有向图。 If you want undirected, you should add to both nodes neighbour's addresses. 如果您想要无向,则应该向两个节点添加邻居的地址。 You can use numbers instead of pointers. 您可以使用数字而不是指针。 vector<unsigned int> in class node and pushing back numbers, no addresses. class node vector<unsigned int>并推回数字,没有地址。


As we know, you do not need to use pointers. 我们知道,您不需要使用指针。 Here is an example of it, too. 这也是一个例子。

When the number of vertexes may change, you can use vector of nodes ( vector<node> ) instead array, and just resizing . 当顶点数量可能发生变化时,您可以使用节点vector<node>vector<node> )代替数组,只需调整大小 The rest remains unchanged. 其余的保持不变。 For example: 例如:

vector<node> nodes(n); //you have n nodes
nodes.emplace_back(); //you added new node, or .resize(n+1)
//here is place to runtime graph generate
//as previously, i,j from user, but now you have 'vector<unsigned int>' in node
nodes[i].neighbors.push_back(j);

But you can't erase a node, this breaches numbering! 但你无法删除一个节点,这违反了编号! If you want to erase something, you should use list ( list<node*> ) of pointers. 如果要删除某些内容,则应使用指针的list( list<node*> )。 Otherwise you must keep non-existent vertexes. 否则,您必须保留不存在的顶点。 Here, the order matters! 在这里,订单很重要!


Regarding the line nodes.emplace_back(); //adding node 关于行nodes.emplace_back(); //adding node nodes.emplace_back(); //adding node , It is safe with graph without pointers. nodes.emplace_back(); //adding node ,没有指针的图表是安全的。 If you want use pointers, you predominately shouldn't change size of graph. 如果要使用指针,则主要不应更改图形的大小。 You can accidentally change address of some nodes, while adding vertex, when vector will be copied to new place (out of space). vector将被复制到新位置(空间外)时,您可能会意外地更改某些节点的地址,同时添加顶点。

One way to deal with it is using reserve , although you have to know maximal size of graph! 处理它的一种方法是使用reserve ,尽管你必须知道图形的最大尺寸! But in fact I encourage you not to use vector to keep vertexes, when you are using pointers. 但事实上,我鼓励你在使用指针时不要使用vector来保持顶点。 If you don't know implementation, more safe could be self memory management (smart pointers eg. shared_ptr or just new ). 如果您不了解实现,更安全的可能是自我内存管理(智能指针,例如shared_ptr新的 )。

node* const graph = new node[size]; //<-- It is your graph.
//Here no address change accidentally.

Using vector as adjacency list is always fine ! 使用vector作为邻接列表总是很好 There's no chance to change node's address. 没有机会改变节点的地址。

This may not be very general approach but thats how I handle adjacency list in most of the cases. 这可能不是一般的方法,但这就是我在大多数情况下如何处理邻接列表。 C++ has STL library which supports a data structure for linked list named as list . C ++有STL库,它支持名为list链表的数据结构。

Say you have N nodes in the graph, create a linked list for every node. 假设图中有N节点,为每个节点创建一个链表。

list graph[N];

Now graph[i] represent the neighbours of node i. 现在graph[i]代表节点i的邻居。 For every edge i to j, do 对于每个边缘i到j,做

graph[i].push_back(j);

The best comfort is no handling of pointers so as segmentation fault errors. 最好的舒适性是不处理指针以便分割错误。

For more reference http://www.cplusplus.com/reference/list/list/ 更多参考http://www.cplusplus.com/reference/list/list/

I suggest you adding in the node structure, the Adjacency List And define the graph structure as List of Nodes instead of List of Adjacency Lists :) 我建议你添加节点结构,邻接列表并将图形结构定义为节点列表而不是邻接列表列表:)

struct node {
    int vertex;
    node* next;
    adjList m_neighbors;
};
struct graph{
    //List of nodes
};

I would recommend the more general and simple approach of using vector and pairs: #include #include 我建议使用vector和pair的更一般和简单的方法:#include #include

typedef std::pair<int, int> ii; /* the first int is for the data, and the second is for the weight of the Edge - Mostly usable for Dijkstra */
typedef std::vector<ii> vii;
typedef std::vector <vii> WeightedAdjList; /* Usable for Dijkstra -for example */
typedef std::vector<vi> AdjList; /*use this one for DFS/BFS */

Or alias style (>=C++11): 或别名样式(> = C ++ 11):

using ii = std::pair<int,int>;
using vii = std::vector<ii>;
using vi = std::vector<int>;
using WeightedAdjList = std::vector<vii>;
using AdjList = std::vector<vi>;

From here: using vector and pairs (from tejas's answer) 从这里: 使用矢量和对(来自tejas的答案)

For additional information you can refer to a very good summary of topcoder: Power up c++ with STL 有关其他信息,您可以参考topcoder的一个非常好的摘要: 使用STL启动c ++

My approach would be to use a hash map to store the list of nodes in the graph 我的方法是使用哈希映射来存储图中的节点列表

class Graph {
private:
  unordered_map<uint64_t, Node> nodeList;
  ...
}

The map takes the node ID as key, and the node itself as value. 映射将节点ID作为键,将节点本身作为值。 This way you could search for a node in the graph in constant time. 这样,您可以在恒定时间内搜索图中的节点。

The node contains the adjacency list, in this case as a c++11 vector. 该节点包含邻接列表,在本例中为c ++ 11向量。 It could also be a linked list, although for this use case I would not see a difference in efficiency. 它也可能是一个链表,虽然对于这个用例我不会看到效率上的差异。 Maybe the list would be better if you would like to keep it sorted somehow. 如果您希望以某种方式对其进行排序,列表可能会更好。

class Node{
    uint64_t id;     // Node ID
    vector<uint64_t> adjList;  
...
}

With this approach you have to go through the adjacency list and then search the map on the ID to get the node. 使用此方法,您必须通过邻接列表,然后在ID上搜索地图以获取节点。

As an alternative, you could have a vector of pointers to the neighbor nodes itself. 作为替代方案,您可以拥有指向邻居节点本身的指针向量。 That would give you a direct access to the neighbor nodes, but then you could not use a map to keep all your nodes in the graph, and you would loose the possibility to search entries easily in your graph. 这将使您可以直接访问邻居节点,但是您无法使用地图将所有节点保留在图表中,并且您将无法在图表中轻松搜索条目。

As you can see, there is a lot of trade-off decisions you have to make when implementing a graph, all depends on your use cases. 正如您所看到的,在实现图表时您必须做出许多权衡决定,这些决定都取决于您的使用案例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 C++ 中将邻接列表转换为有向无环图的邻接矩阵 - Converting an Adjacency List to a an Adjacency Matrix for a Directed Acyclic Graph in C++ 使用C ++向量的有向图的邻接表表示 - Adjacency list representation of a directed graph using c++ vector 将边添加到使用C ++中的链接列表实现的有向图(邻接列表)中 - Add edge to a directed graph (Adjacency list) implemented using Linked List in C++ 如何找到用c ++实现的有向图的所有路径作为邻接表? - How to find all paths for a directed graph implemented in c++ as adjacency list? 考虑到遍历的路径每次都是相同的,如何在C ++中实现有向图而不使用邻接表? - How do I implement a directed graph in C++ without using an adjacency list, given that the path of traversal will be the same every time? 尝试在C ++中创建带有邻接表的图 - Trying to create a graph with adjacency list in C++ 在有向图中有效表示邻接表和权重 - Efficient representation for adjacency list and weights in directed graph 复制带有邻接表的有向图的构造函数 - Copy constructor for a directed graph with adjacency list 使用邻接矩阵在C ++上的有向图中查找所有循环的算法 - Algorithm for finding all cycles in a directed graph on C++ using Adjacency matrix 邻接表C ++ - Adjacency list c++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM