title: Find the Town Judge
slug: find-the-town-judge
difficulty: easy
leetcode_id: 997
leetcode_url: https://leetcode.com/problems/find-the-town-judge/
categories:
  - arrays
  - graphs
  - hash-tables
patterns:
  - greedy

function_signature: "def find_judge(n: int, trust: list[list[int]]) -> int:"

test_cases:
  visible:
    - input: { n: 2, trust: [[1, 2]] }
      expected: 2
    - input: { n: 3, trust: [[1, 3], [2, 3]] }
      expected: 3
    - input: { n: 3, trust: [[1, 3], [2, 3], [3, 1]] }
      expected: -1
  hidden:
    - input: { n: 1, trust: [] }
      expected: 1
    - input: { n: 2, trust: [] }
      expected: -1
    - input: { n: 4, trust: [[1, 3], [1, 4], [2, 3], [2, 4], [4, 3]] }
      expected: 3
    - input: { n: 3, trust: [[1, 2], [2, 3]] }
      expected: -1
    - input: { n: 4, trust: [[1, 2], [1, 3], [2, 1], [2, 3], [1, 4], [4, 3], [4, 1]] }
      expected: 3

description: |
  In a town, there are `n` people labeled from `1` to `n`. There is a rumor that one of these people is secretly the town judge.

  If the town judge exists, then:

  1. The town judge trusts nobody.
  2. Everybody (except for the town judge) trusts the town judge.
  3. There is exactly one person that satisfies properties **1** and **2**.

  You are given an array `trust` where `trust[i] = [a_i, b_i]` representing that the person labeled `a_i` trusts the person labeled `b_i`. If a trust relationship does not exist in the `trust` array, then such a trust relationship does not exist.

  Return *the label of the town judge if the town judge exists and can be identified, or return* `-1` *otherwise*.

constraints: |
  - `1 <= n <= 1000`
  - `0 <= trust.length <= 10^4`
  - `trust[i].length == 2`
  - All the pairs of `trust` are **unique**
  - `a_i != b_i`
  - `1 <= a_i, b_i <= n`

examples:
  - input: "n = 2, trust = [[1,2]]"
    output: "2"
    explanation: "Person 1 trusts person 2, but person 2 trusts no one. Since person 2 is trusted by everyone else (just person 1) and trusts nobody, person 2 is the town judge."
  - input: "n = 3, trust = [[1,3],[2,3]]"
    output: "3"
    explanation: "Both person 1 and person 2 trust person 3, while person 3 trusts nobody. Person 3 satisfies both conditions."
  - input: "n = 3, trust = [[1,3],[2,3],[3,1]]"
    output: "-1"
    explanation: "Person 3 is trusted by everyone else, but person 3 also trusts person 1. Since the town judge must trust nobody, there is no valid town judge."

explanation:
  intuition: |
    Think of the trust relationships as a directed graph where each person is a node, and an edge from `a` to `b` means "person `a` trusts person `b`."

    The town judge has a very specific signature in this graph:
    - **Zero outgoing edges**: They trust nobody
    - **Exactly `n-1` incoming edges**: Everyone else trusts them

    Imagine you're counting votes at a town meeting. Each trust relationship is like a vote of confidence. The judge receives votes from everyone but casts no votes themselves. If we track the "net trust score" for each person (votes received minus votes cast), the judge would have a score of exactly `n-1`.

    This insight transforms a graph problem into a simple counting problem: instead of building complex data structures, we just need to track a single number for each person.

  approach: |
    We solve this using a **Trust Score Approach**:

    **Step 1: Initialise a trust score array**

    - Create an array `trust_score` of size `n+1` (using 1-based indexing to match person labels)
    - Each position starts at `0`, representing the net trust balance for that person

    &nbsp;

    **Step 2: Process each trust relationship**

    - For each `[a, b]` pair in the `trust` array:
      - Decrement `trust_score[a]` by 1 (person `a` trusts someone, so they lose a point)
      - Increment `trust_score[b]` by 1 (person `b` is trusted, so they gain a point)

    &nbsp;

    **Step 3: Find the town judge**

    - Iterate through people `1` to `n`
    - The town judge is the person with `trust_score[i] == n - 1`
    - This means they received `n-1` trust votes and cast 0 votes themselves

    &nbsp;

    **Step 4: Return the result**

    - If found, return the judge's label
    - If no one has a trust score of `n-1`, return `-1`

    &nbsp;

    This approach works because the trust score naturally captures both conditions: trusting nobody (no deductions) and being trusted by everyone else (exactly `n-1` additions).

  common_pitfalls:
    - title: Using Two Separate Arrays
      description: |
        A common approach is to maintain two arrays: one for "trusts count" (outgoing edges) and one for "trusted by count" (incoming edges). Then checking if `trusted_by[i] == n-1` and `trusts[i] == 0`.

        While correct, this uses unnecessary space. The single trust score approach combines both conditions into one value, halving space usage and simplifying the logic.
      wrong_approach: "Two arrays tracking incoming and outgoing separately"
      correct_approach: "Single array with net trust score (incoming - outgoing)"

    - title: Forgetting the Single Person Case
      description: |
        When `n = 1` and `trust = []`, the single person is the town judge by definition. They trust nobody (vacuously true since there's no one to trust) and are trusted by everyone else (vacuously true since there's no one else).

        The trust score approach handles this naturally: person 1 has a score of `0`, and we need `n - 1 = 0`, so they qualify as the judge.
      wrong_approach: "Special-casing n=1 with extra conditionals"
      correct_approach: "Let the algorithm handle it naturally"

    - title: Using 0-Based Indexing Incorrectly
      description: |
        People are labeled from `1` to `n`, not `0` to `n-1`. Using a size-`n` array with 0-based indexing requires translating indices, which is error-prone.

        Using a size `n+1` array and ignoring index 0 keeps the code simple and matches the problem's labeling directly.
      wrong_approach: "Size-n array with index translation"
      correct_approach: "Size-(n+1) array with 1-based indexing"

  key_takeaways:
    - "**Graph degree insight**: In directed graphs, problems about nodes with specific in-degree and out-degree can often be solved by tracking net degree (in - out)"
    - "**Space optimisation**: When tracking two related quantities (trusts vs trusted-by), consider if a single combined metric suffices"
    - "**Constraint-driven design**: The judge's unique property (trusted by `n-1`, trusts `0`) translates directly to a net score of `n-1`"
    - "**Foundation for graph problems**: This in-degree/out-degree counting technique appears in problems like finding celebrities, detecting cycles, and topological sorting"

  time_complexity: "O(n + t) where `t` is the length of the trust array. We initialise an array of size `n` and iterate through all `t` trust relationships, then check `n` people."
  space_complexity: "O(n). We use a single array of size `n+1` to store the trust score for each person."

solutions:
  - approach_name: Trust Score
    is_optimal: true
    code: |
      def find_judge(n: int, trust: list[list[int]]) -> int:
          # Use n+1 size for 1-based indexing (people labeled 1 to n)
          trust_score = [0] * (n + 1)

          # Process each trust relationship
          for a, b in trust:
              # Person a trusts someone, so they can't be the judge
              trust_score[a] -= 1
              # Person b is trusted, gaining one vote
              trust_score[b] += 1

          # Find the person with trust score of n-1
          # This means: trusted by n-1 people, trusts nobody
          for i in range(1, n + 1):
              if trust_score[i] == n - 1:
                  return i

          # No valid town judge found
          return -1
    explanation: |
      **Time Complexity:** O(n + t) where t is the number of trust relationships. We process each relationship once and scan through n people.

      **Space Complexity:** O(n) for the trust score array.

      The key insight is that a net trust score of `n-1` uniquely identifies the judge: they received votes from all `n-1` other people (contributing +n-1) and cast no votes themselves (contributing 0).

  - approach_name: Two Arrays (In-degree and Out-degree)
    is_optimal: false
    code: |
      def find_judge(n: int, trust: list[list[int]]) -> int:
          # Track how many people each person trusts (out-degree)
          trusts_count = [0] * (n + 1)
          # Track how many people trust each person (in-degree)
          trusted_by_count = [0] * (n + 1)

          for a, b in trust:
              trusts_count[a] += 1
              trusted_by_count[b] += 1

          # Judge trusts nobody (out-degree = 0) and is trusted by all others (in-degree = n-1)
          for i in range(1, n + 1):
              if trusts_count[i] == 0 and trusted_by_count[i] == n - 1:
                  return i

          return -1
    explanation: |
      **Time Complexity:** O(n + t) where t is the number of trust relationships.

      **Space Complexity:** O(n) but uses two arrays instead of one.

      This approach explicitly tracks in-degree and out-degree separately, making the logic clearer but using twice the space. The optimal solution combines these into a single net score.
