Time/Space complexity of adjacency matrix and adjacency list - algorithm

I am reading "Algorithms Design" By Eva Tardos and in chapter 3 it is mentioned that adjacency matrix has the complexity of O(n^2) while adjacency list has O(m+n) where m is the total number of edges and n is the total number of nodes. It says that in-case of adjacency list we will need only lists of size m for each node.
Won't we end up with something similar to matrix in case of adjacency list as we have lists,which are also 1D arrays. So basically it is O(m*n) according to me. Please guide me.

An adjacency matrix keeps a value (1/0) for every pair of nodes, whether the edge exists or not, so it requires n*n space.
An adjacency list only contains existing edges, so its length is at most the number of edges (or the number of nodes in case there are fewer edges than nodes).
It says that in-case of adjacency list we will need only lists of size
m for each node.
I think you misunderstood that part. An adjacency list does not hold a list of size m for every node, since m is the number of edges overall.
In a fully connected graph, there is an edge between every pair of nodes so both adjacency list and matrix will require n*n of space, but for every other case - an adjacency list will be smaller.

Related

Time complexity of deleting an edge in an adjacency list

For a graph(V,E) where V is the total number of vertices and E is the total number of edges, what is the time complexity of deleting an edge? I thought it would be O(V) worst case since the max number of edges any vertex can have is V-1. But I have been told the time complexity is O(M) where M is the number of edges a vertex has. Which is correct?
Depends on the structure of your graph.
If you choose to implement the graph as an adjacency list, removing an element from a list is O(V), since you may have to iterate through the list.
However, you can implement the graph as a list of sets (each set being the list of adjacent nodes of a node), and hence the time complexity can be O(logV) if the set is sorted or O(1) if it is a hash set.
If your graph is represented as an adjacency matrix, it is also O(1), since you just have to erase E[u][v] and E[v][u].

Adjacency list with O(1) look up time using HashSet?

In my algorithms class I've been told that a draw back of Adjacency Lists for graph representation is the O(n) look up time for iterating through the array of adjacent nodes corresponding to each node. I implement my adjacency list by using a HashMap that maps nodes to a HashSet of their adjacent nodes, wouldn't that only take O(1) look up time? Is there something I'm missing?
As you know look up for value using key in HashMap is O(1). However, in adjacency list the value of the HashMap is also a list of its adjacent nodes. The main purpose of the adjacency list is to iterate the adjacent nodes. For example: graph traversal algorithms like DFS and BFS. In your case HashSet. Suppose number of elements in HashSet is n. Then for iterating all the elements even in HashSet is O(n).
So, total complexity would be O(1)+O(n).
Where O(1)= look up in HashMap
O(n)= iterate all the elements
Generally, Adjacency List is preferable for sparse graph because it is the graph with only a few edges. It means the number of adjacent elements in each node(key of HashMap) is less. So the look up for a element wont cost more.
I implement my adjacency list by using a HashMap that maps nodes to a HashSet of their adjacent nodes, wouldn't that only take O(1) look up time? [emphasis mine]
Right — but "adjacency list" normally implies a representation as an array or a linked-list rather than a HashSet: in other words, adjacency lists are optimized for iterating over a vertex's neighbors rather than for querying if two vertices are neighbors.
It may be possible to produce more time-efficient graph representations than adjacency lists, particularly for graphs where vertices vertex often have many edges.
With a map of vertices where each vertex contains a map of neighbor vertices and/or edge objects, we can look if nodes are connected in O(1) time by indexing a vertex id and then indexing a neighbor. That's potentially a big savings over an adjacency list where we might have to loop over many edges to find specific neighbors. Furthermore, a map-of-maps data structure can allow us to store arbitrary data in edge objects. That's useful for weighted graphs and features of actions/edges

does DFS and BFS in O(n+m) change?

We know there is an O(n+m)‌ ‌solution (DFS ‌or BFS) for checking if there is a path from s to t in a Undirected Graph G with n vertexes and m edges... that would be implemented via an adjacency List.
If I implement my program with Adjacency Matrix, will the runtime be affected? Is this a good or bad choice?
Edit: I‌ Need to calculate the time complexity, in this way, any idea?
Assuming that the input to your code will be n and m ( number of nodes and the number of edges ) followed by m lines of the type a b signifying there is an edge between vertex a and vertex b. Now you take an adjacency matrix M[][] such that M[i][j]=1 if there is an edge between i and j otherwise M[i][j]=0 ( as graph is undirected the matrix will be symmetric, thus you can only store the upper/lower half matrix reducing memory by half ). Now you will have to take the matrix and initialize it to 0 ( all the cells ) and while scanning the edges mark M[a][b]=M[b][a]=1. Now the initializing part is O(n^2). Scanning and marking the edges is O(m). Now lets look at the BFS/DFS routine. When you are at a node you try to see all its unvisited vertices. Now say we want to know the neighbors of vertex a, you will have to do for(int i=0;i<n;i++) if (M[a][i]==1) ( assuming 0 based indexing ). Now this has to be done for each vertex and thus the complexity of routine becomes O(n^2) even if m < (n*(n-1))/2 ( assuming simple graph with no multiple edges and loops m can at maximum be (n*(n-1))/2 ). Thus overall your complexity becomes O(n^2). Then whats the use of adjacency matrix ? well the DFS/BFS might be just a part of a big algorithm, your algorithm might also require one tell if there is an edge between node a and b at which adjacency matrix takes O(1) time. Thus whether to choose adjacency list or adjacency matrix really depends on your algorithm ( such as maximum memory you can take, time complexity for things like DFS/BFS routine or answering queries whether two vertices are connected etc. ) .
Hope I answered your query.

Edge lists vs adjacency lists vs and adjacency matrix

I'm preparing to create a maze solving program. As with stated in these two questions: graphs representation : adjacency list vs matrix &&
Size of a graph using adjacency list versus adjacency matrix? they give an explanation of the differences in using the adjacency lists vs adjacency matrices. Unfortunately, I cannot decide on the pros and cons of an edge lists compared to these other two since I have found very little on adjacency matrices and edge lists.
An example of going through the adjacent list for the maze (I think) would be:
insertVertex(V) : O(1)
insertEdge(Vertex, Vertex, E) : O(1)
removeVertex(Vertex) : O(deg(v))
removeEdge(Edge) : O(m)
vertices() : O(n)
edges() : O(m)
areAdjacent(Vertex, Vertex) : O(min(deg(v),deg(w))
endVertices(Edge) : O(1)
incidentEdges(Vertex) : O(deg(v))
space complexity : O(n+m)
So my question is, which has the best time cost an edge list, adjacency list, or adjacency matrix for this maze solving problem?
Let's start from "classical" mazes. They are defined as rectangular grid, each cell of which is either corridor or wall. Player can move one cell at the time in one of four directions (top, left, bottom, right). Maze example:
S..#.##E
.#.#.#..
.#...#.#
.#.###.#
##.....#
Player starts at position marked S and should reach position E.
For now let's present each blank cell as graph vertex. Then each vertex can have at most 4 neighbours. In terms of space usage adjacency list clearly wins - 4*V vs V^2.
Simplest efficient shortest path algorithm for grid maze would be BFS. For huge mazes it can be replaced by A*. Both of these algorithms have only one "edge related" operation: take all neighbours for given node. This is O(1) (we have at most 4 neighbours) for adjacency list and O(V) for adjacency matrix.
To save space we can create vertices only for crossroads. However this has no impact on calculations above (number of vertices will go down but it will be still greater than 4).
As a conclusion, for grid representation of a maze adjacency list wins in terms of both time and space usage.
General case
Every maze can be modelled as a set of rooms (vertices) with corridors (edges) that lead to different rooms. Usually number of rooms is much bigger than number of corridors for single room. In this case arguments for adjacency lists still holds.
Additional note. For grid maze it's often more easy just to use grid representation as is (2-dimensional array with booleans) without creation of additional graph structures.

Directed graph (topological sort)

Say there exists a directed graph, G(V, E) (V represents vertices and E represents edges), where each edge (x, y) is associated with a weight (x, y) where the weight is an integer between 1 and 10.
Assume s and tare some vertices in V.
I would like to compute the shortest path from s to t in time O(m + n), where m is the number of vertices and n is the number of edges.
Would I be on the right track in implementing topological sort to accomplish this? Or is there another technique that I am overlooking?
The algorithm you need to use for finding the minimal path from a given vertex to another in a weighted graph is Dijkstra's algorithm. Unfortunately its complexity is O(n*log(n) + m) which may be more than you try to accomplish.
However in your case the edges are special - their weights have only 10 valid values. Thus you can implement a special data structure(kind of a heap, but takes advantage of the small dataset for the wights) to have all operations constant.
One possible way to do that is to have 10 lists - one for each weight. Adding an edge in the data structure is simply append to a list. Finding the minimum element is iteration over the 10 lists to find the first one that is non-empty. This still is constant as no more than 10 iterations will be performed. Removing the minimum element is also pretty straight-forward - simple removal from a list.
Using Dijkstra's algorithm with some data structure of the same asymptotic complexity will be what you need.

Resources