Hi.
Introduction.
We have a graph, we want to save it (and maybe run DFS on it for example); simple? Yes, but I want to show different methods for this, you can choose any of them depending on problem.
Adjacency matrix.
Simple, if graph is not weighted, save in G[v][u] if there is an edge between v and u or not.
If graph is weighted, if there is an edge between v and u save it’s weight in G[v][u], save - 1 otherwise (if weight can be negative, use inf instead of - 1).
Memory usage : .
Overall time complexity : (for running dfs).
Vectors.
It's the most common method for saving graph. For each vertex keep a vector of it’s edges, now for each edge just save it in related vectors. Use pair (or another struct) for saving weighted graph. It works similar for directed graph.
Memory usage : (Note that vector uses more memory than you need).
Overall time complexity : (for running dfs).
Dynamic array.
This method is rarely used. For each vertex count edges connected to it at first, then allocate a enough space for each vertex to save it’s adjacency-list. Note that this method is Offline.
Memory usage : .
Overall time complexity : (for running dfs).
Linked list.
This method is used when id of edges are important and works only for directed graphs (or if you can convert the undirected graph to directed). We use array for saving edges. Then, for each vertex we keep index of last added edge and for each edge consider it’s from Fromi to Toi, we keep the index of last edge before this edge that Fromindex = Fromi and name it prvi. Then we can use headv and prv array to find adjacency-list.
Memory usage : .
Overall time complexity : (for running dfs).
Application: Solving flow network problems. Wikipedia: Maximum flow problem.
Usage of this method in above codes are where we use cap[e] -= x, cap[e ^ 1] += x
. Because of special adding edge method, it is guaranteed that if edge e is from v to u, is its pair and it’s from u to v.
Related submission : 23115110.
Index keeping.
This method is used when index of edges is important (i.e. in queries we need to change something about edges, i.e. change weight, delete edge). Keep index of edges related to each vertex in its vector, and for each edge keep the vertices connecting with this edge (see code below).
Note that index keeping is possible and maybe easier using map (or unordered_map), but it is faster using this method. Also note that dynamic array method could be used here as well.
Memory usage : (Note that vector uses more memory than you need).
Overall time complexity : (for running dfs).
Example: Consider you need to change weight of edges online, this is simple using index keeping method, but it isn’t possible with other methods (at least it will be harder). Related submission : 20776118.
Application: Finding Euler tour. Wikipedia : Eulerian path. Blocking edges is easier with this method, that’s what we need in Euler tour finding. Related submissions : 21395473, 21636007.
Comparison.
I used Codeforces polygon for comparing and generated 16 random graphs:
#1 to #4 : n = 106, m = 106
#5 to #4 : n = 105, m = 106
#9 to #8 : n = 104, m = 106
#13 to #16 : n = 103, m = 106
# | dynamic array | linked list | vector |
---|---|---|---|
1 | OK 1637 / 44 | OK 1123 / 29 | OK 1606 / 39 |
2 | OK 1637 / 44 | OK 1247 / 29 | OK 1559 / 39 |
3 | OK 1590 / 44 | OK 1107 / 29 | OK 1560 / 39 |
4 | OK 1669 / 44 | OK 1169 / 29 | OK 1622 / 39 |
5 | OK 1060 / 29 | OK 1060 / 31 | OK 1169 / 36 |
6 | OK 1106 / 29 | OK 1045 / 31 | OK 1123 / 36 |
7 | OK 1060 / 30 | OK 1029 / 31 | OK 1122 / 36 |
8 | OK 1091 / 29 | OK 1044 / 31 | OK 1169 / 36 |
9 | OK 1029 / 26 | OK 1013 / 29 | OK 1044 / 29 |
10 | OK 982 / 26 | OK 998 / 29 | OK 1013 / 29 |
11 | OK 1013 / 26 | OK 982 / 29 | OK 1201 / 29 |
12 | OK 1123 / 26 | OK 1029 / 29 | OK 1029 / 29 |
13 | OK 998 / 26 | OK 951 / 29 | OK 982 / 27 |
14 | OK 935 / 26 | OK 982 / 29 | OK 950 / 27 |
15 | OK 951 / 26 | OK 951 / 29 | OK 982 / 27 |
16 | OK 966 / 26 | OK 1060 / 29 | OK 1013 / 27 |
Σ | 16 | 16 | 16 |
max. | 1669ms / 44MB | 1247ms / 31MB | 1622ms / 39MB |
It's really strange for me that vector uses less memory than dynamic array while testing, can someone explain the reason? Here is the link to codes and generator.
UPD: I found the answer, I missed that in dynamic array method we are saving edges in pairs, and keeping the sz
array in addition (thanks to ATofighi).
As usual I’d like to finish the post with a poem:
بی سبب هرگز نمیگردد کسی یار کسی یار بسیار است تا گرم است بازار کسی
By: Parto biza’ee arani.
Translation: No one becomes friend with another one without reason. People are like shops, a shop is full of customers while it has goods.
P.S. Kindly write in comments (or use private chat in case of necessary) if something is wrong.
It really doesn't seem right that the vector representation uses less memory. Can you provide both codes (for the tests generators and for the representations alone).
I'll add them now.
Edit: Done.
Maybe storing the edges in the dynamic array method (you have 106 edges) eats additionally 8 MB of memory? And using dynamic arrays instead of vectors saves some memory (~3MB) so the dynamic array variant eats 5MB bigger.
There's a way to store trees only which is by having an array
par
and saving the parent of the i-th node intpar[i]
.:)