5

I came across this question in which it was required to calculate in-degree of each node of a graph from its adjacency list representation.

for each u
   for each Adj[i] where i!=u
     if (i,u) ∈ E
         in-degree[u]+=1

Now according to me its time complexity should be O(|V||E|+|V|^2) but the solution I referred instead described it to be equal to O(|V||E|).

Please help and tell me which one is correct.

lennon310
  • 12,503
  • 11
  • 43
  • 61
silentseeker
  • 416
  • 5
  • 14

3 Answers3

8

Rather than O(|V||E|), the complexity of computing indegrees is O(|E|). Let us consider the following pseudocode for computing indegrees of each node:

for each u
  indegree[u] = 0;

for each u
  for each v \in Adj[u]
    indegree[v]++;

First loop has linear complexity O(|V|). For the second part: for each v, the innermost loop executes at most |E| times, while the outermost loop executes |V| times. Therefore the second part appears to have complexity O(|V||E|). In fact, the code executes an operation once for each edge, so a more accurate complexity is O(|E|).

eugen
  • 8,916
  • 11
  • 57
  • 65
Corneliu
  • 81
  • 1
  • 2
  • I am not bit clear. The outer loop in second part has to be executed |V| times and we cant avoid it right? May I know how then O|VE| becomes O|E| ? – vijayashankard Jan 21 '17 at 07:51
  • Yes the outer loop is bound to execute |V| times but the execution of inner loop depend on the no of edges adjacent to that vertex.. which would be less than |E| ( excluding complete graph case). To visualise this , think of finding indegree as looking at all edges. So the complexity is proportional is the no of edges in the graph is O( |E|) – smasher Oct 18 '17 at 05:32
  • 2
    Hi, the reasoning that it should be O(|E|) seems fine. But as you said, the outer loop does run |V| times. So, while doing complexity computations, do we not take into account the loops where nothing happens? Like `for i in (0,n): do nothing` has complexity as `n` or `1`? – Anshul Feb 12 '18 at 04:29
  • Also, should the complexity be `O(|E|)` or `O(|E| + |V|)`, since we need to visit all the vertex rows in `Adj(G)` at least once? – Anshul Feb 12 '18 at 04:36
2

According to http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Graphs.html, Section 4.2, with an adjacency list representation,

Finding predecessors of a node u is extremely expensive, requiring looking through every list of every node in time O(n+m), where m is the total number of edges.

So, in the notation used here, the time complexity of computing the in-degree of a node is O(|V| + |E|).

This can be reduced at the cost of additional space of using extra space, however. The Wiki also states that

adding a second copy of the graph with reversed edges lets us find all predecessors of u in O(d-(u)) time, where d-(u) is u's in-degree.

An example of a package which implements this approach is the Python package Networkx. As you can see from the constructor of the DiGraph object for directional graphs, networkx keeps track of both self._succ and self._pred, which are dictionaries representing the successors and predecessors of each node, respectively. This allows it to compute each node's in_degree efficiently.

Kurt Peek
  • 52,165
  • 91
  • 301
  • 526
0

O(|V|+|E|) is the correct answer, because you visit each vertex in O(|V|) and each time you visit a fraction of the edges so O(|E|) in total, also usually |E|>>|V| so O(|E|) is also correct

Alberto Sinigaglia
  • 12,097
  • 2
  • 20
  • 48