DSA · Topic 12 of 18 · Trees & Graphs

Advanced Graphs

Weighted edges? Dijkstra: always expand the cheapest known node next (a heap).

6 problems · Dijkstra O(E log V); Bellman-Ford O(k × E); Prim O(E log V) time, O(V + E) space

Step 1 Understand it

Remember it by

Weighted edges? Dijkstra: always expand the cheapest known node next (a heap).

Watch it happen

AnimationWeighted graphs: settle the closest first, or relax in rounds
Read the animation as text
  1. A signal starts at node 2. How long until every node hears it? Dijkstra: always settle the closest node not yet settled. A heap keeps the closest on top.
  2. Pop (0, 2): the closest unsettled node. Its time is final: 0. Push its neighbours: 1 at 0 + 1 = 1, 3 at 0 + 1 = 1.
  3. Pop (1, 1): the closest unsettled node. Its time is final: 1. Push its neighbours: 4 at 1 + 3 = 4.
  4. Pop (1, 3): the closest unsettled node. Its time is final: 1. Push its neighbours: 4 at 1 + 1 = 2.
  5. Pop (2, 4): settle node 4 at time 2. No unsettled neighbours to push.
  6. Pop (4, 4): node 4 is already settled at a shorter time. This old entry is stale — skip it.
  7. Every node is settled. The last to hear the signal is at time 2: the answer. Node 4 was pushed twice (4 via node 1, 2 via node 3) — the heap handed out the cheaper one first.

The idea

When edges have different costs, BFS isn't enough. Dijkstra uses a min-heap to always settle the cheapest unsettled node. Related tools: Bellman-Ford when there's a limit on the number of edges (or negative weights), Prim's or Kruskal's for the cheapest way to connect everything (minimum spanning tree), and topological sort for ordering.

Picture it

flowchart LR
  S(("2: source")) -->|"1"| A(("1"))
  S -->|"1"| B(("3"))
  B -->|"1"| D(("4"))
  A -->|"3"| D
Two routes to node 4: via 1 costs 1 + 3 = 4, via 3 costs 1 + 1 = 2. Dijkstra settles the cheaper one first.

Analogy

Water spreading from a source through pipes of different lengths. It reaches near places first, and the first time it reaches a place is via the shortest route.

Where the analogy breaks

Water spreads everywhere at once; Dijkstra settles one node at a time. It also fails with negative edge costs, which water has no equivalent of.

More detail

What it is

Advanced Graphs tackle problems where all paths are NOT created equal. Instead of just seeing if A connects to B, what if the road from A to B costs $10, but the road from A to C costs $5? Now we're dealing with *Weighted Cost* networks, *Directed* rules, or *Disjoint Sets*.

When to use it

  • When finding the Absolute Cheapest/Shortest path in a Weighted Graph (Dijkstra's Algorithm).
  • When determining if a Directed Graph has a cycle (Course Schedule problems).
  • When grouping highly separate islands rapidly (Union-Find).
  • When finding the cheapest way to connect everything (Minimum Spanning Tree).

How it works

There are 3 major magic spells here:\n1. **Dijkstra's**: It's just BFS, but instead of a standard Queue, you use a PriorityQueue (Min-Heap) so it always explores the cheapest available path first!\n2. **Topological Sort**: Used to determine the valid order of tasks when 'Task A' must be completed before 'Task B'.\n3. **Union-Find**: An incredibly fast way to group components into separate 'sets' and check if two nodes belong to the same set.

Example walkthrough

Input: edges=[[0,1,cost:10], [0,2,cost:1], [2,1,cost:2]]. Find cheapest 0 to 1.

  1. Start 0 cost 0. MinHeap=[(cost 0, Node 0)].
  2. Pop 0. Neighbors: Node 1 cost 10, Node 2 cost 1. MinHeap=[(1, Node 2), (10, Node 1)].
  3. Pop (1, Node 2). Neighbors: 2 gets to 1 for cost 2. Total cost = 1 (from 0->2) + 2 (from 2->1) = 3. Add to Heap. MinHeap=[(3, Node 1), (10, Node 1)].
  4. Pop (3, Node 1). Reached Target 1! Total Cost is 3. (We completely ignored the expensive 10 cost path!)

Result: 3

Related patterns

  • Heap / Priority Queue: Dijkstra's is essentially useless without a highly efficient PriorityQueue.

Step 2 Recognise it

Words that signal it

  • 'Minimum cost / time / delay' with weighted edges
  • 'Within k stops' → Bellman-Ford (k + 1 rounds)
  • 'Connect all points with minimum total cost' → minimum spanning tree
  • 'Order of letters / tasks from rules' → topological sort
  • 'Minimise the maximum along a path' (Swim in Rising Water) → Dijkstra on the max

Don't confuse it with

  • Graphs (BFS): All edges cost the same → plain BFS. Different costs → Dijkstra.
  • Dynamic Programming: Bellman-Ford is DP over the number of edges used — reach for it when the number of steps is limited.

Gut check

Shortest path, but the edges have different weights? → Dijkstra with a heap.

How to recognise it in an interview

The problem talks about 'Prerequisites', 'Cheapest network', 'Connecting all points', or gives an edges array WITH a third weight value.

Step 3 The template

The general shape. Adapt it to each problem rather than learning one solution per problem.

python
import heapq

def dijkstra(adj, source):
    # adj[u] = list of (v, cost)
    dist = {}
    heap = [(0, source)]
    while heap:
        d, u = heapq.heappop(heap)
        if u in dist:
            continue                   # already settled with a cheaper cost
        dist[u] = d
        for v, cost in adj[u]:
            if v not in dist:
                heapq.heappush(heap, (d + cost, v))
    return dist
The same template in Java
text
class Solution {
    // Union-Find (Disjoint Set) Template
    int[] parent;
    public int find(int i) {
        // Find root and compress path
        if (parent[i] == i) return i;
        return parent[i] = find(parent[i]); 
    }
    public boolean union(int i, int j) {
        int rootI = find(i), rootJ = find(j);
        if (rootI == rootJ) return false; // Already connected! (CYCLE FOUND)
        parent[rootI] = rootJ; // Connect them
        return true;
    }
}
TimeDijkstra O(E log V); Bellman-Ford O(k × E); Prim O(E log V)
SpaceO(V + E)

Common mistakes

  • Not skipping a node popped a second time (a stale, more expensive entry).
  • Updating prices in place during a Bellman-Ford round, which lets one round use more than one extra edge — copy the array first.
  • Using Dijkstra with a 'k stops' limit: the cheapest path may use too many stops.
  • Confusing Dijkstra's algorithm (Weighted Shortest Path) with standard BFS (Unweighted Shortest Path).
  • Forgetting to initialize the Parent array to parent[i] = i in a Union-Find object.

Step 4 Watch it run

Two problems, traced step by step exactly as the code runs.

Easy example: Network Delay Time (Dijkstra)

Input: times = [[2,1,1],[2,3,1],[3,4,1],[1,4,3]], n = 4, k = 2

poppedactionsettled times
node 2 (time 0)settle it; push 1 at 1, 3 at 1{'2': 0}
node 1 (time 1)settle it; push 4 at 4{'1': 1, '2': 0}
node 3 (time 1)settle it; push 4 at 2{'1': 1, '2': 0, '3': 1}
node 4 (time 2)settle it; no new neighbours{'1': 1, '2': 0, '3': 1, '4': 2}
node 4 (time 4)already settled → skip{'1': 1, '2': 0, '3': 1, '4': 2}

Result: 2

Notice: Node 4 is pushed twice (cost 4 and cost 2); the heap pops the cheaper one first, and the stale entry is skipped.

python
import heapq
from collections import defaultdict

def network_delay_time(times, n, k):
    adj = defaultdict(list)
    for u, v, w in times:
        adj[u].append((v, w))
    dist, heap = {}, [(0, k)]
    while heap:
        d, u = heapq.heappop(heap)
        if u in dist:
            continue
        dist[u] = d
        for v, w in adj[u]:
            if v not in dist:
                heapq.heappush(heap, (d + w, v))
    return max(dist.values()) if len(dist) == n else -1

Medium example: Cheapest Flights Within K Stops (Bellman-Ford)

Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1

roundimprovementsprices
startonly the source is free[0, ∞, ∞, ∞]
round 1 (≤ 0 stops)0→1: 0 + 100 = 100[0, 100, ∞, ∞]
round 2 (≤ 1 stops)1→2: 100 + 100 = 200; 1→3: 100 + 600 = 700[0, 100, 200, 700]

Result: 700

Notice: Round r allows r edges. 0 → 1 → 2 → 3 costs only 400 but needs 2 stops, so with k = 1 the answer is 700.

python
def find_cheapest_price(n, flights, src, dst, k):
    prices = [float('inf')] * n
    prices[src] = 0
    for _ in range(k + 1):                 # k stops = k + 1 flights
        tmp = prices[:]                    # copy: one more flight per round
        for u, v, w in flights:
            if prices[u] + w < tmp[v]:
                tmp[v] = prices[u] + w
        prices = tmp
    return prices[dst] if prices[dst] != float('inf') else -1

Step 5 Practise

Read each question and say the pattern and the trick out loud before you open the answer. Then solve it on LeetCode. Tick a problem once you've solved it on your own: it comes back for review tomorrow, then after 3, 7, 14 and 30 days, then every month, on the DSA page.

Medium

  1. Network Delay Time Medium

    A signal starts at node k; how long until every node receives it, with weighted travel times?

    Show answer

    Technique: Dijkstra

    The clue: Shortest times with weighted edges, from one source.

    The trick: Dijkstra from k; the answer is the largest shortest time (−1 if some node is never reached).

  2. Min Cost to Connect All Points Medium

    Connect all points with the smallest total Manhattan distance.

    Show answer

    Technique: Minimum spanning tree (Prim's)

    The clue: 'Connect everything with minimum total cost'.

    The trick: Prim's: grow a tree from one point, always adding the cheapest edge to a new point (min-heap).

  3. Cheapest Flights Within K Stops Medium

    Find the cheapest price from src to dst using at most k stops.

    Show answer

    Technique: Bellman-Ford with k + 1 rounds

    The clue: Cheapest path with a limit on the number of edges.

    The trick: Run k + 1 rounds of edge relaxation, copying the prices each round so each round adds only one flight.

Hard

  1. Reconstruct Itinerary Hard

    Use every ticket exactly once to build the itinerary from JFK, choosing the alphabetically smallest when there's a choice.

    Show answer

    Technique: Hierholzer's algorithm (Eulerian path)

    The clue: 'Use every edge exactly once'.

    The trick: Sort destinations; DFS taking the smallest ticket each time; add an airport to the route when it has no tickets left, then reverse.

  2. Swim In Rising Water Hard

    Water rises by 1 each second; find the earliest time you can swim from the top-left to the bottom-right.

    Show answer

    Technique: Dijkstra on the maximum along the path

    The clue: Minimise the highest cell you must pass through.

    The trick: Dijkstra where a path's cost is the largest height on it; pop cells in order of that cost.

  3. Alien Dictionary Hard

    Given words sorted in an unknown alphabet, work out the letter order.

    Show answer

    Technique: Build a graph + topological sort

    The clue: An order between letters from comparisons = dependencies.

    The trick: Compare neighbouring words at their first different letter to get an edge; topologically sort; watch for 'abc' before 'ab' (invalid).

What to say in the interview

'Because the edges have weights (or strict prerequisites), standard BFS is insufficient. I'll need to use Dijkstra's Algorithm (or Union-Find) to solve this optimally.'

Follow-up prep

Can Dijkstra's handle negative weights? (No, you'd need Bellman-Ford).