R

E

A

C

T

O

xamples 

epeat

ode

pproach

ptimize

est

{Graph: Does Path Exist?}

Some Terminology

Graph: a data structure that has a set of vertices connected by a set of edges

Tree: a special type of graph. Ex) a binary search tree, where a vertex is a node of that tree, and an edge is a link between a parent and one of it's children.

Undirected Graph

Directed Graph

The Question

Write a function that determines if a path exists between two vertices of a graph (a directed graph).

Ex)

A     D, should return true

C     A, should return false

The function should take a graph, a starting point (ie. A) and a target (ie. B), and should return a boolean.

The Question (continued.)

The graph can be represented as an object, each of whose keys represents a vertex of the graph and whose values represent all vertices that can be reached from the aforementioned key.

In the example above, there is a connection from vertex a to vertex b and a connection from vertex b to vertices c and d, but not a connection from vertex b to vertex a.

const graph = {
    a: ['b'],
    b: ['c', 'd'],
    c: ['d'],
    d: []
}

a

b

c

d

Suggested Hints

Note: the interviewee does not have to make a function to make the graph.

If the interviewee has trouble coming up with a representation for the graph first have them draw out an example graph. If they still have trouble coming up with the JS representation, you can give them the object structure.

const graph = {
    a: ['b'],
    b: ['c', 'd'],
    c: ['d']
}

a

b

c

d

1)

2)

Examples

doesPathExist(graph, 'a', 'c') // true
doesPathExist(graph, 'c', 'a') // true
doesPathExist(graph, 's', 'r') // false
const graph = {
    a: ['a', 'c'],
    c: ['r', 's'],
    r: ['a'],
    s: []
}

a

c

s

r

Approach

This problem is essentially a DFS/BFS problem, either algorithm is sufficient. The only catch is that graphs can be cyclic. In other words, it's possible for a loop to exist in the graph.

const graph = {
    a: ['a', 'c'],
    c: ['r', 's'],
    r: ['a'],
    s: []
}

a

c

s

r

Imagine we started traversing at vertex a. Eventually we would reach vertex r, but notice that it points right back to vertex a. If you were to unleash an unmodified BFS/DFS traversal algorithm on this input, the algorithm would proceed in an infinite loop. This means that the algorithm must be changed to keep track of all vertices that it has seen.

If a vertex has been seen, we know to not consider it's edges a second time. The algorithm completes when either it has found the target vertex or when it has exhausted all possible vertices without finding it's target.

The Solution

const doesPathExist = function(graph, start, target, visited = {}) {
    visited[start] = true
    return graph[start].some(function (vertex) {
        if (start === target) {
            return true
        } else if (!visited[vertex]) {
            return doesPathExist(graph, vertex, target, visited)
        } else {
            return false
        }
    }
}

Note: some() executes the callback function once for each element present in the array until it finds one where callback returns a truthy value (MDN).

Try it out @ https://repl.it/JVhs/2

const graph = {
    a: ['a', 'c'],
    c: ['r', 's'],
    r: ['a'],
    s: []
}

a

c

s

r

Discussion

There is another data structure we could use to model a graph. So far we have seen the adjacency list representation. An alternative data structure is called an adjacency matrices. The cyclic graph from before could have been modeled as the following:

a c r s
a 1 1 0 0
c 0 0 1 1
r 0 0 0 0
s 1 0 0 0

Visually

Adjacency List

Adjacency Matrix

In JavaScript, this table would be represented using an array of arrays or object of objects. A 1 indicates that a given vertex has an edge pointing to another vertex, and a 0 indicates that it does not.

const graph = [
    [1, 1, 0, 0]
    [0, 0, 1, 1]
    [0, 0, 0, 0]
    [1, 0, 0, 0]
]

Discussion

a c r s
a 1 1 0 0
c 0 0 1 1
r 0 0 0 0
s 1 0 0 0

In JavaScript, this table would be represented using an array of arrays or object of objects. A 1 indicates that a given vertex has an edge pointing to another vertex, and a 0 indicates that it does not.

Discussion

Consider the tradeoffs between using one of these data structures over the other. How do they compare for the following ways:

Attribute Answer
Testing if a given edge is in the graph Adjacency matrix (AM)
Finding the degree (# of edges) of a vertex Adjacency list (AL)
Insertion/deletion of edges AM: O(1) vs. AL: O(d) where d=degree of vertex
Memory usage for sparse graphs AL: (m+n) vs. AM: n^2
Memory usage for dense graphs AL
Graph traversal AL: O(m+n) vs. AM: O(n^2)
Better overall AL

The Solution

const doesPathExist = function(graph, start, target, visited = {}) {
    visited[start] = true
    return graph[start].some(function (vertex) {
        if (start === target) {
            return true
        } else if (!visited[vertex]) {
            return doesPathExist(graph, vertex, target, visited)
        } else {
            return false
        }
    }
}

Try it out @ https://repl.it/JVhs/2

Reacto: Graph - Does Path Exist?

By sarahdherr

Reacto: Graph - Does Path Exist?

  • 826