Skip to content

Graphs

Definition

Graphs Definition


Graph Representations

Graph Representations


Big O Complexity

Graphs Complexity


When to Use

Graphs When to Use


Pros and Cons

Graphs Pros and Cons


Implementation

Adjacency List Graph

public class Graph {
    private Map<Integer, List<Integer>> adjList;
    private boolean directed;

    public Graph(boolean directed) {
        this.adjList = new HashMap<>();
        this.directed = directed;
    }

    public void addVertex(int v) {
        adjList.putIfAbsent(v, new ArrayList<>());
    }

    public void addEdge(int src, int dest) {
        addVertex(src);
        addVertex(dest);
        adjList.get(src).add(dest);
        if (!directed) {
            adjList.get(dest).add(src);
        }
    }

    public void removeEdge(int src, int dest) {
        adjList.get(src).remove(Integer.valueOf(dest));
        if (!directed) {
            adjList.get(dest).remove(Integer.valueOf(src));
        }
    }

    public List<Integer> getNeighbors(int v) {
        return adjList.getOrDefault(v, Collections.emptyList());
    }

    public boolean hasEdge(int src, int dest) {
        return adjList.containsKey(src) && adjList.get(src).contains(dest);
    }

    public int getVertexCount() {
        return adjList.size();
    }

    public Set<Integer> getVertices() {
        return adjList.keySet();
    }
}

Weighted Graph

public class WeightedGraph {
    private Map<Integer, List<int[]>> adjList;  // int[] = {neighbor, weight}

    public WeightedGraph() {
        adjList = new HashMap<>();
    }

    public void addEdge(int src, int dest, int weight) {
        adjList.putIfAbsent(src, new ArrayList<>());
        adjList.putIfAbsent(dest, new ArrayList<>());
        adjList.get(src).add(new int[]{dest, weight});
        adjList.get(dest).add(new int[]{src, weight});  // Undirected
    }

    public List<int[]> getNeighbors(int v) {
        return adjList.getOrDefault(v, Collections.emptyList());
    }
}

Graph Traversals

// Level-by-level traversal using queue
public List<Integer> bfs(int start) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    Queue<Integer> queue = new LinkedList<>();

    queue.offer(start);
    visited.add(start);

    while (!queue.isEmpty()) {
        int node = queue.poll();
        result.add(node);

        for (int neighbor : adjList.getOrDefault(node, List.of())) {
            if (!visited.contains(neighbor)) {
                visited.add(neighbor);
                queue.offer(neighbor);
            }
        }
    }
    return result;
}

// BFS with levels
public List<List<Integer>> bfsWithLevels(int start) {
    List<List<Integer>> levels = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    Queue<Integer> queue = new LinkedList<>();

    queue.offer(start);
    visited.add(start);

    while (!queue.isEmpty()) {
        int levelSize = queue.size();
        List<Integer> currentLevel = new ArrayList<>();

        for (int i = 0; i < levelSize; i++) {
            int node = queue.poll();
            currentLevel.add(node);

            for (int neighbor : adjList.getOrDefault(node, List.of())) {
                if (!visited.contains(neighbor)) {
                    visited.add(neighbor);
                    queue.offer(neighbor);
                }
            }
        }
        levels.add(currentLevel);
    }
    return levels;
}
// Recursive DFS
public List<Integer> dfsRecursive(int start) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    dfsHelper(start, visited, result);
    return result;
}

private void dfsHelper(int node, Set<Integer> visited, List<Integer> result) {
    visited.add(node);
    result.add(node);

    for (int neighbor : adjList.getOrDefault(node, List.of())) {
        if (!visited.contains(neighbor)) {
            dfsHelper(neighbor, visited, result);
        }
    }
}

// Iterative DFS using stack
public List<Integer> dfsIterative(int start) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    Deque<Integer> stack = new ArrayDeque<>();

    stack.push(start);

    while (!stack.isEmpty()) {
        int node = stack.pop();
        if (visited.contains(node)) continue;

        visited.add(node);
        result.add(node);

        for (int neighbor : adjList.getOrDefault(node, List.of())) {
            if (!visited.contains(neighbor)) {
                stack.push(neighbor);
            }
        }
    }
    return result;
}

Common Graph Algorithms

Detect Cycle

// Undirected graph - using DFS
public boolean hasCycleUndirected() {
    Set<Integer> visited = new HashSet<>();
    for (int node : adjList.keySet()) {
        if (!visited.contains(node)) {
            if (hasCycleHelper(node, -1, visited)) {
                return true;
            }
        }
    }
    return false;
}

private boolean hasCycleHelper(int node, int parent, Set<Integer> visited) {
    visited.add(node);
    for (int neighbor : adjList.getOrDefault(node, List.of())) {
        if (!visited.contains(neighbor)) {
            if (hasCycleHelper(neighbor, node, visited)) return true;
        } else if (neighbor != parent) {
            return true;  // Back edge found
        }
    }
    return false;
}

// Directed graph - using colors (White/Gray/Black)
public boolean hasCycleDirected() {
    Set<Integer> white = new HashSet<>(adjList.keySet());
    Set<Integer> gray = new HashSet<>();
    Set<Integer> black = new HashSet<>();

    while (!white.isEmpty()) {
        int node = white.iterator().next();
        if (hasCycleDirectedHelper(node, white, gray, black)) {
            return true;
        }
    }
    return false;
}

private boolean hasCycleDirectedHelper(int node, Set<Integer> white,
                                        Set<Integer> gray, Set<Integer> black) {
    white.remove(node);
    gray.add(node);

    for (int neighbor : adjList.getOrDefault(node, List.of())) {
        if (black.contains(neighbor)) continue;
        if (gray.contains(neighbor)) return true;  // Back edge
        if (hasCycleDirectedHelper(neighbor, white, gray, black)) return true;
    }

    gray.remove(node);
    black.add(node);
    return false;
}

Topological Sort (DAG only)

// Using DFS
public List<Integer> topologicalSort() {
    Set<Integer> visited = new HashSet<>();
    Deque<Integer> stack = new ArrayDeque<>();

    for (int node : adjList.keySet()) {
        if (!visited.contains(node)) {
            topSortHelper(node, visited, stack);
        }
    }

    return new ArrayList<>(stack);
}

private void topSortHelper(int node, Set<Integer> visited, Deque<Integer> stack) {
    visited.add(node);
    for (int neighbor : adjList.getOrDefault(node, List.of())) {
        if (!visited.contains(neighbor)) {
            topSortHelper(neighbor, visited, stack);
        }
    }
    stack.push(node);
}

// Using Kahn's Algorithm (BFS)
public List<Integer> topologicalSortKahn() {
    Map<Integer, Integer> inDegree = new HashMap<>();
    for (int node : adjList.keySet()) {
        inDegree.putIfAbsent(node, 0);
        for (int neighbor : adjList.get(node)) {
            inDegree.merge(neighbor, 1, Integer::sum);
        }
    }

    Queue<Integer> queue = new LinkedList<>();
    for (int node : inDegree.keySet()) {
        if (inDegree.get(node) == 0) {
            queue.offer(node);
        }
    }

    List<Integer> result = new ArrayList<>();
    while (!queue.isEmpty()) {
        int node = queue.poll();
        result.add(node);
        for (int neighbor : adjList.getOrDefault(node, List.of())) {
            inDegree.merge(neighbor, -1, Integer::sum);
            if (inDegree.get(neighbor) == 0) {
                queue.offer(neighbor);
            }
        }
    }

    return result.size() == adjList.size() ? result : Collections.emptyList();
}

Shortest Path - Dijkstra

public Map<Integer, Integer> dijkstra(int start) {
    Map<Integer, Integer> distances = new HashMap<>();
    PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);

    for (int node : adjList.keySet()) {
        distances.put(node, Integer.MAX_VALUE);
    }
    distances.put(start, 0);
    pq.offer(new int[]{start, 0});

    while (!pq.isEmpty()) {
        int[] current = pq.poll();
        int node = current[0];
        int dist = current[1];

        if (dist > distances.get(node)) continue;

        for (int[] edge : getWeightedNeighbors(node)) {
            int neighbor = edge[0];
            int weight = edge[1];
            int newDist = dist + weight;

            if (newDist < distances.get(neighbor)) {
                distances.put(neighbor, newDist);
                pq.offer(new int[]{neighbor, newDist});
            }
        }
    }
    return distances;
}

Number of Connected Components

public int countComponents(int n, int[][] edges) {
    // Build graph
    Map<Integer, List<Integer>> graph = new HashMap<>();
    for (int i = 0; i < n; i++) {
        graph.put(i, new ArrayList<>());
    }
    for (int[] edge : edges) {
        graph.get(edge[0]).add(edge[1]);
        graph.get(edge[1]).add(edge[0]);
    }

    // Count components using DFS
    Set<Integer> visited = new HashSet<>();
    int count = 0;

    for (int i = 0; i < n; i++) {
        if (!visited.contains(i)) {
            dfs(i, graph, visited);
            count++;
        }
    }
    return count;
}

private void dfs(int node, Map<Integer, List<Integer>> graph, Set<Integer> visited) {
    visited.add(node);
    for (int neighbor : graph.get(node)) {
        if (!visited.contains(neighbor)) {
            dfs(neighbor, graph, visited);
        }
    }
}

Clone Graph

public Node cloneGraph(Node node) {
    if (node == null) return null;

    Map<Node, Node> visited = new HashMap<>();
    return cloneHelper(node, visited);
}

private Node cloneHelper(Node node, Map<Node, Node> visited) {
    if (visited.containsKey(node)) {
        return visited.get(node);
    }

    Node clone = new Node(node.val);
    visited.put(node, clone);

    for (Node neighbor : node.neighbors) {
        clone.neighbors.add(cloneHelper(neighbor, visited));
    }
    return clone;
}

Common Graph Problems

Common Graph Problems