Jc-alt logo
jc

LeetCode: Graphs I Directed In Degree Out Degree

LeetCode: Graphs I Directed In Degree Out Degree
9 min read
data structures and algorithms

In Degree Out Degree Intro

LeetCode problems with graph based solutions, specifically dealing with the nodes in and out degrees

What is a In Out Degree?

Has to do with the celebrity vs person graph problem. A celebrity may have many in degrees, but few out degrees. While a person may have many out degrees, but few in degrees.

1436. Destination City ::1:: - Easy

Topics: Degree Counting, Array, Hash Table, Graph Theory, Edge List

Intro

You are given the array paths, where paths[i] = [cityAi, cityBi] means there exists a direct path going from cityAi to cityBi. Return the destination city, that is, the city without any path outgoing to another city. It is guaranteed that the graph of paths forms a line without any loop, therefore, there will be exactly one destination city.

Example InputOutput
paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]]"Sao Paulo"
paths = [["B","C"],["D","B"],["C","A"]]"A"
paths = [["A","Z"]]"Z"

Constraints:

1 ≤ paths.length ≤ 100

paths[i].length == 2

1 ≤ cityAi.length, cityBi.length ≤ 10

cityAi != cityBi

All strings consist consist of lowercase and uppercase English letters and the space character.

Abstraction

Find the node that appears as a destination but never appears as a source.

Pseudocode

  text will go here

Solution 1: [Degree Counting] Out-Degree Zero Node Detection - Graph/In Degree Out Degree Counting

    def destCity(self, paths: List[List[str]]) -> str:
        
        # Note:
        # Finding the destination city
        # using a Directed Edge List

        # Directed Edge List:
        # paths = [
        #     ["London", "New York"],
        #     ["New York", "Lima"],
        #     ["Lima", "Sao Paulo"],
        # ]

        # This forms a directed line graph (no branching, no cycles),
        # guaranteed by the problem. Every city except the very last one
        # has exactly one outgoing path to the next city in the chain.
        # The destination city is the one and only city with out-degree
        # 0, it never appears as a source (cityA) in any path.

        # Rather than counting degrees explicitly, we can just collect
        # every source city into a set, then scan for the one
        # destination city (cityB) that never shows up as a source.

        # Track Source Cities:
        # every city that has at least one outgoing path
        # tc: O(E)
        # sc: O(V)
        sources = set()
        for cityA, cityB in paths:
            sources.add(cityA)

        # Find Out-Degree Zero City:
        # the destination is whichever cityB never appears as a source
        # tc: O(E)
        for cityA, cityB in paths:

            if cityB not in sources:
                return cityB

        # overall: tc O(V + E), two passes over paths
        # overall: sc O(V), for the sources set
        return ""
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

997. Find the Town Judge ::1:: - Medium

Topics: Degree Counting, Array, Hash Table, Graph Theory, Edge List

Intro

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] = [ai, bi] representing that the person labeled ai trusts the person labeled bi. If a trust relationship does not exist in 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.
Example InputOutput
n = 2, trust = [[1,2]]2
n = 3, trust = [[1,3],[2,3]]3
n = 3, trust = [[1,3],[2,3],[3,1]]-1

Constraints:

1 ≤ n ≤ 1000

0 ≤ trust.length ≤ 10^4

trust[i].length == 2

All the pairs of trust are unique

ai != bi

1 ≤ ai, bi ≤ n

Abstraction

Given a connected component with in/out degrees, find the node that has n-1 in degrees and 0 out degrees. Which translates to everyone trusts them, but they trust no one.

Pseudocode

  text will go here

Solution 1: [Degree Counting] Net Trust Score Single Array - Graph/In Degree Out Degree Counting

    def findJudge(self, n: int, trust: List[List[int]]) -> int:
        
        # Note:
        #   We represent trust as a graph where an edge a -> b means a trusts b.
        #   The town judge is the only node with In Degree of (n-1), meaning 
        #   everyone trusts them, and an Out Degree of 0, meaning they trust nobody.
        #   We can track this with a single net score per person instead of using
        #   separate in and out degree counters.
        #   for (a -> b) in trust:
        #       +1 to b's score for incoming trust edges
        #       -1 to a's score for outgoing trust edges
        #   The judge if they exist will have a score of (n-1)

        # Check:
        # Single person, no trust relationships possible or needed,
        # they will always be the judge
        if n == 1 and not trust:
            return 1
        
        # Trust array to track net trust score for each person
        # sc: O(n)
        trustScore = [0] * (n + 1)
        
        # Check every trust relationship and update the trust score accordingly
        #   +1 for every person who trusts i
        #   -1 for every person i trusts
        # tc: O(m)
        for a, b in trust:
            trustScore[a] -= 1
            trustScore[b] += 1
        
        # Check every final trust score to see if any person has a score of (n-1)
        # tc: O(n)
        for person in range(1, n + 1):
            if trustScore[person] == n - 1:
                return person
        
        # overall: tc O(n + m)
        # overall: sc O(n)
        return -1
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

277. Find the Celebrity ::1:: - Medium

Topics: Degree Counting, Graph Theory

Intro

Suppose you are at a party with n people labeled from 0 to n - 1 and among them, there may exist one celebrity. The definition of a celebrity is that all the other n - 1 people know the celebrity, but the celebrity does not know any of them. Now you want to find out who the celebrity is or verify that there is not one. You are only allowed to ask questions in this form: "Hi, A. Do you know B?" to get the answer of whether A knows B. You need to find out the celebrity (or verify there is not one) by asking as few questions as possible (in the asymptotic sense). You are given a helper function bool knows(a, b) which tells you whether a knows b. Implement a function int findCelebrity(n). There will be exactly one celebrity if they are at the party. Return the celebrity's label if there is a celebrity at the party, or return -1 if there is no celebrity.

Example InputOutput
n = 2, graph = [[1,1],[0,1]]1
n = 3, graph = [[1,0,1],[0,1,0],[1,1,1]]-1

Constraints:

2 ≤ n ≤ 100

1 ≤ graph.length == graph[i].length == n

graph[i][j] is 0 or 1

graph[i][i] == 1

Abstraction

The celebrity will hsave in-degree (n-1) and out-degree 0, just like the judge from 997. The differences is we have an oracle function instead of an edge list, and we want to do it in the fewest possible calls.

Pseudocode

  text will go here

Solution 1: [Degree Counting] Elimination Then Verification - Graph/In Degree Out Degree Counting

    def findCelebrity(self, n: int) -> int:

        # Node:
        #   The celebrity, if one exists, is known by everyone but knows no one,
        #   and have in-degree (n-1) and out-degree 0 in a directed graph.
        #   For any pair (a, b), at most one of them can be the celebrity:
        #       - if a knows b, a cannot be the celebrity
        #       - if a does not know b, b cannot be the celebrity
        #   The allows a single pass to eliminate n-1 candidates, leaving 1 to verify.

        # Start with any candidate, and eliminate one person for each comparison
        # until we are left with one candidate
        a = 0

        for b in range(1, n):

            # Check:
            # Person A knows person B, so Person A cannot be the celebrity,
            # we now test Person B
            if knows(a, b):
                a = b

            # Else:
            # Person A did not know Person B, so Person B cannot be the celebrity,
            # we continue to test Person A

        # Person A is the last remaining candidate,
        # for each person other than A, we validate:
        #   - A does not know B
        #   - B knows A
        for b in range(n):

            # Skip A
            if b == a:
                continue

            # If A knows B => A is not the candidate
            # If B does not know A => A is not the candidate
            if knows(a, b) or not knows(b, a):
                return -1

        # A is validated to be the candidate

        # overall: tc O(n)
        # overall: sc O(1)
        return candidate
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks