{
  "id": "graphs/union-find",
  "version": 1,
  "deterministic": true,
  "url": "/dsa/graphs/union-find",
  "topic": {
    "slug": "graphs",
    "title": "Graphs"
  },
  "title": "Union-Find",
  "tagline": "Merge groups and ask 'are these two connected?' — in effectively constant time.",
  "vizKind": "graph",
  "complexity": {
    "time": {
      "best": "O(1)",
      "average": "O(α(n))",
      "worst": "O(α(n))"
    },
    "space": "O(n)",
    "growth": "constant",
    "note": "α is the inverse Ackermann function, which is below 5 for any n that could physically exist. Not technically O(1) — but you may treat it as constant with a completely clear conscience. Without the two optimisations it would degrade to O(n) per operation."
  },
  "code": {
    "javascript": "const parent = [...Array(n).keys()];   // everyone is their own boss\nconst rank = new Array(n).fill(0);\n\nfunction find(x) {\n  if (parent[x] !== x) {\n    // Path compression: point straight at the boss on the way back.\n    parent[x] = find(parent[x]);\n  }\n  return parent[x];\n}\n\nfunction union(a, b) {\n  const ra = find(a), rb = find(b);\n  if (ra === rb) return false;        // already together\n\n  // Union by rank: hang the shorter tree under the taller one.\n  if (rank[ra] < rank[rb]) parent[ra] = rb;\n  else if (rank[ra] > rank[rb]) parent[rb] = ra;\n  else { parent[rb] = ra; rank[ra]++; }\n\n  return true;\n}",
    "python": "parent = list(range(n))          # everyone is their own boss\nrank = [0] * n\n\ndef find(x):\n    if parent[x] != x:\n        # Path compression: point straight at the boss on the way back.\n        parent[x] = find(parent[x])\n\n    return parent[x]\n\n\ndef union(a, b):\n    ra, rb = find(a), find(b)\n    if ra == rb: return False        # already together\n\n    # Union by rank: hang the shorter tree under the taller one.\n    if rank[ra] < rank[rb]: parent[ra] = rb\n    elif rank[ra] > rank[rb]: parent[rb] = ra\n    else:\n        parent[rb] = ra\n        rank[ra] += 1\n\n    return True\n",
    "java": "int[] parent = new int[n];       // everyone is their own boss\nint[] rank = new int[n];\n\nint find(int x) {\n  if (parent[x] != x) {\n    // Path compression: point straight at the boss on the way back.\n    parent[x] = find(parent[x]);\n  }\n  return parent[x];\n}\n\nboolean union(int a, int b) {\n  int ra = find(a), rb = find(b);\n  if (ra == rb) return false;      // already together\n\n  // Union by rank: hang the shorter tree under the taller one.\n  if (rank[ra] < rank[rb]) parent[ra] = rb;\n  else if (rank[ra] > rank[rb]) parent[rb] = ra;\n  else { parent[rb] = ra; rank[ra]++; }\n\n  return true;\n}",
    "cpp": "vector<int> parent(n), rnk(n, 0);\n// iota(parent.begin(), parent.end(), 0);  everyone is their own boss\n\nint find(int x) {\n  if (parent[x] != x) {\n    // Path compression: point straight at the boss on the way back.\n    parent[x] = find(parent[x]);\n  }\n  return parent[x];\n}\n\nbool unite(int a, int b) {\n  int ra = find(a), rb = find(b);\n  if (ra == rb) return false;      // already together\n\n  // Union by rank: hang the shorter tree under the taller one.\n  if (rnk[ra] < rnk[rb]) parent[ra] = rb;\n  else if (rnk[ra] > rnk[rb]) parent[rb] = ra;\n  else { parent[rb] = ra; rnk[ra]++; }\n\n  return true;\n}"
  },
  "defaultInput": [
    1
  ],
  "defaultTarget": null,
  "inputHint": "Arrows are parent pointers — the forest IS the data structure.",
  "pitfalls": [
    {
      "wrong": "Skipping path compression.",
      "right": "Without it, the trees grow into long chains and every find() walks them. It's two lines of code and it's most of the performance."
    },
    {
      "wrong": "Hanging the taller tree under the shorter one.",
      "right": "That deepens the tree for no reason. Union by rank always puts the shorter under the taller, keeping everything flat."
    },
    {
      "wrong": "Comparing the two items directly instead of their roots.",
      "right": "Two items in the same group usually have different parents. Sameness is decided at the *root* — always compare find(a) with find(b)."
    },
    {
      "wrong": "Trying to un-merge two groups.",
      "right": "You can't. Union-Find is one-way — it merges, and it cannot split. If you need to remove connections, you need a different structure entirely."
    }
  ],
  "quiz": [
    {
      "q": "How do you tell whether two items are in the same group?",
      "options": [
        "Compare them directly",
        "Climb to each one's root and check whether the roots are the same",
        "Search the whole structure",
        "Compare their parents"
      ],
      "answer": 1,
      "why": "The root is the group's identity. Two items are in the same group exactly when they climb to the same root — their immediate parents are usually different."
    },
    {
      "q": "What does path compression do?",
      "options": [
        "Deletes unused nodes",
        "Re-points every node you walked past straight at the root, so the next lookup is one hop",
        "Compresses the memory",
        "Sorts the tree"
      ],
      "answer": 1,
      "why": "On the way back up from a find(), everything you passed is re-attached directly to the root. The structure flattens itself a little more every time you use it."
    },
    {
      "q": "What can Union-Find NOT do?",
      "options": [
        "Merge two groups",
        "Split a group back apart",
        "Check whether two items are connected",
        "Count the groups"
      ],
      "answer": 1,
      "why": "It's strictly one-way. Once merged, always merged — because the merge only re-pointed a root and didn't record anything about how the group was assembled."
    }
  ],
  "problems": [
    {
      "name": "Number of Provinces",
      "difficulty": "Medium",
      "url": "https://leetcode.com/problems/number-of-provinces/"
    },
    {
      "name": "Redundant Connection",
      "difficulty": "Medium",
      "url": "https://leetcode.com/problems/redundant-connection/"
    },
    {
      "name": "Accounts Merge",
      "difficulty": "Medium",
      "url": "https://leetcode.com/problems/accounts-merge/"
    },
    {
      "name": "Number of Islands",
      "difficulty": "Medium",
      "url": "https://leetcode.com/problems/number-of-islands/"
    }
  ],
  "trace": {
    "note": "Each frame is one immutable step: data snapshot, pointers, per-index roles, executing code line, and a one-sentence explanation.",
    "length": 18,
    "frames": [
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 1,
        "variables": {
          "items": 8,
          "groups": 8
        },
        "explanation": "Eight things, and nobody is connected to anybody — 8 separate groups. Each one points at itself, so each one is its own boss. The arrows you'll see are parent pointers: they *are* the data structure.",
        "stats": {
          "comparisons": 0,
          "swaps": 0
        },
        "accent": "active"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "0 & 1",
          "rootOfA": 0,
          "rootOfB": 1
        },
        "explanation": "union(0, 1) — first, who's the boss of each? Walk up from 0 to 0, and from 1 to 1.",
        "stats": {
          "comparisons": 1,
          "swaps": 0
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0",
              "role": "active"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 0,
          "groups": 7,
          "merges": 1
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 0 is now the boss of both. One pointer changed, and two entire groups became one. Down to 7 groups.",
        "stats": {
          "comparisons": 1,
          "swaps": 1
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "2 & 3",
          "rootOfA": 2,
          "rootOfB": 3
        },
        "explanation": "union(2, 3) — first, who's the boss of each? Walk up from 2 to 2, and from 3 to 3.",
        "stats": {
          "comparisons": 2,
          "swaps": 1
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2",
              "role": "active"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 2,
          "groups": 6,
          "merges": 2
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 2 is now the boss of both. One pointer changed, and two entire groups became one. Down to 6 groups.",
        "stats": {
          "comparisons": 2,
          "swaps": 2
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "4 & 5",
          "rootOfA": 4,
          "rootOfB": 5
        },
        "explanation": "union(4, 5) — first, who's the boss of each? Walk up from 4 to 4, and from 5 to 5.",
        "stats": {
          "comparisons": 3,
          "swaps": 2
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4",
              "role": "active"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 4,
          "groups": 5,
          "merges": 3
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 4 is now the boss of both. One pointer changed, and two entire groups became one. Down to 5 groups.",
        "stats": {
          "comparisons": 3,
          "swaps": 3
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0",
              "role": "active"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "1 & 2",
          "rootOfA": 0,
          "rootOfB": 2
        },
        "explanation": "union(1, 2) — first, who's the boss of each? Walk up from 1 to 0, and from 2 to 2.",
        "stats": {
          "comparisons": 4,
          "swaps": 3
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0",
              "role": "active"
            },
            {
              "from": "2",
              "to": "0",
              "role": "active"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 0,
          "groups": 4,
          "merges": 4
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 0 is now the boss of both. One pointer changed, and two entire groups became one. Down to 4 groups.",
        "stats": {
          "comparisons": 4,
          "swaps": 4
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "active"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "6 & 7",
          "rootOfA": 6,
          "rootOfB": 7
        },
        "explanation": "union(6, 7) — first, who's the boss of each? Walk up from 6 to 6, and from 7 to 7.",
        "stats": {
          "comparisons": 5,
          "swaps": 4
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "active"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6",
              "role": "active"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 6,
          "groups": 3,
          "merges": 5
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 6 is now the boss of both. One pointer changed, and two entire groups became one. Down to 3 groups.",
        "stats": {
          "comparisons": 5,
          "swaps": 5
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "pivot"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4",
              "role": "active"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "5 & 6",
          "rootOfA": 4,
          "rootOfB": 6
        },
        "explanation": "union(5, 6) — first, who's the boss of each? Walk up from 5 to 4, and from 6 to 6.",
        "stats": {
          "comparisons": 6,
          "swaps": 5
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "pivot"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4",
              "role": "active"
            },
            {
              "from": "6",
              "to": "4",
              "role": "active"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 4,
          "groups": 2,
          "merges": 6
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 4 is now the boss of both. One pointer changed, and two entire groups became one. Down to 2 groups.",
        "stats": {
          "comparisons": 6,
          "swaps": 6
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "pivot"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2",
              "role": "active"
            },
            {
              "from": "5",
              "to": "4"
            },
            {
              "from": "6",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "0 & 3",
          "rootOfA": 0,
          "rootOfB": 0
        },
        "explanation": "union(0, 3) — first, who's the boss of each? Walk up from 0 to 0, and from 3 to 0.",
        "stats": {
          "comparisons": 7,
          "swaps": 6
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "discarded"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "discarded"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "pivot"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2",
              "role": "active"
            },
            {
              "from": "5",
              "to": "4"
            },
            {
              "from": "6",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 15,
        "variables": {
          "alreadyTogether": true,
          "groups": 2
        },
        "explanation": "Both roads lead to 0 — 0 and 3 are *already* in the same group. Nothing to do. Answering that took a couple of hops, not a search of the whole structure.",
        "stats": {
          "comparisons": 7,
          "swaps": 6
        },
        "accent": "discarded"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "pivot"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "pivot"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "pivot"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0",
              "role": "active"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "5",
              "to": "4",
              "role": "active"
            },
            {
              "from": "6",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 14,
        "variables": {
          "union": "1 & 5",
          "rootOfA": 0,
          "rootOfB": 4
        },
        "explanation": "union(1, 5) — first, who's the boss of each? Walk up from 1 to 0, and from 5 to 4.",
        "stats": {
          "comparisons": 8,
          "swaps": 6
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "active"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75,
              "role": "sorted"
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "active"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "sorted"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "sorted"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0",
              "role": "active"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "4",
              "to": "0"
            },
            {
              "from": "5",
              "to": "4",
              "role": "active"
            },
            {
              "from": "6",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 19,
        "variables": {
          "newRoot": 0,
          "groups": 1,
          "merges": 7
        },
        "explanation": "Different groups, so merge them: hang one root under the other and 0 is now the boss of both. One pointer changed, and two entire groups became one. Down to 1 groups.",
        "stats": {
          "comparisons": 8,
          "swaps": 7
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "0",
              "label": "0",
              "x": 0.12,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "1",
              "label": "1",
              "x": 0.35,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "2",
              "label": "2",
              "x": 0.58,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "3",
              "label": "3",
              "x": 0.82,
              "y": 0.25,
              "role": "sorted"
            },
            {
              "id": "4",
              "label": "4",
              "x": 0.12,
              "y": 0.75,
              "role": "sorted"
            },
            {
              "id": "5",
              "label": "5",
              "x": 0.35,
              "y": 0.75,
              "role": "sorted"
            },
            {
              "id": "6",
              "label": "6",
              "x": 0.58,
              "y": 0.75,
              "role": "sorted"
            },
            {
              "id": "7",
              "label": "7",
              "x": 0.82,
              "y": 0.75,
              "role": "sorted"
            }
          ],
          "edges": [
            {
              "from": "1",
              "to": "0"
            },
            {
              "from": "2",
              "to": "0"
            },
            {
              "from": "3",
              "to": "2"
            },
            {
              "from": "4",
              "to": "0"
            },
            {
              "from": "5",
              "to": "4"
            },
            {
              "from": "6",
              "to": "4"
            },
            {
              "from": "7",
              "to": "6"
            }
          ],
          "directed": true,
          "visited": []
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 22,
        "variables": {
          "groups": 1,
          "merges": 7,
          "operations": 8
        },
        "explanation": "Everything is one group. 8 operations, 7 actual merges. Union by rank kept the trees flat, and path compression flattens them further on every lookup — together they make each operation effectively O(1). It's the engine behind Kruskal's minimum spanning tree and every \"are these connected?\" question you'll ever ask.",
        "stats": {
          "comparisons": 8,
          "swaps": 7
        },
        "accent": "sorted"
      }
    ]
  }
}