Find the town judge
Here is an interesting problem that I enjoyed solving using UMPIRE method.
Problem statement
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:
- The town judge trusts nobody.
- Everybody (except for the town judge) trusts the town judge.
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.
Return the label of the town judge if the town judge exists and can be identified, or return -1 otherwise.
For Example:
- For n = 2 and trust = [[1,2]] return 2, because 1 trusts 2, 2 trusts no one
Understand
Happy cases:
Input: n = 3, trust = [[1,2],[3,2]]
Output: 2
Input: n = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
Output: 3
Input: n = 4, trust = [[1,2],[2,4],[3,4],[1,4]]
Output: 4
Input: n = 3, trust = [[1,2],[2,3]]
Output: -1
Input: n = 3, trust = [[1,3],[2,3],[3,1]]
Output: -1
Input: n = 2, trust = [[1,2],[2,1]]
Output: -1
Input: n = 5, trust = [[1,3],[1,2],[1,4],[1,5]]
Output: -1
Match
Lists:
// iterate _trust_ to create a list of list,
list[i] = list of people i trusts,
// find a member common to all the lists (town judge)
// if no common member found:
return -1
- complexity:
- time: O(n), where n = len(trust)
- time to traverse trust array
- space: O(m2), where m = n
- for example, if n = 5, trust = [[1,2],[1,3],[1,4],[1,5],[2,1],[2,3],[2,4],[2,5],[3,1],[3,2],[3,4],[3,5],[4,1],[4,2],[4,3],[4,5],[5,1],[5,2],[5,3],[5,4]]
- after iterating through trust we will have a list = [[2,3,4,5],[1,3,4,5],[1,2,4,5],[1,2,3,5],[1,2,3,4]]
- space = O(m*m-1) ≈ O(m2), where m = n
- time: O(n), where n = len(trust)
Linked List
- linked list will not optimize the m2 complexity
Stack and Queue
- stacks and Queue data structure help us in maintaining LIFO or FIFO order
- the problem does not require us to look or maintain LIFO or FIFO order
- so it’s better to avoid stack or queue
Heap
- Since we are not required to sort, heap will not be useful to solve this problem
Hashmap
- Oh, yes hashmaps!!
- we could have n keys whose values can be the people a particular person trusts
- since every person is unique, the keys are unique
- also, there is no need to maintain the order of people
- plus hashmaps have constant time look up, which will make our solution faster
- so hashmap it is!
Plan
- build a hashmap, where keys are the people ranging from 1-n and the values are can the appended looking up the trust array
- in other words we are building a graph
- the relation between the nodes is given in the trust array
- the first property, the town judge trusts nobody, makes town judge a sink node, and
- the second property, everybody (except for the town judge) trusts the town judge, essentially tells that all the nodes in the graph are connected to the sink node
- this is clearly a dependency graph problem
- steps involved:
// construct a graph // if sink node not found: return -1 // if sink node present: if all nodes are not connected to sink node: return -1 // return sink node
Implement
def findJudge(n: int, trust: List[List[int]]) -> int:
graph = dict()
in_degree = dict() # to find sink node
judge = -1
for i in range(1,n+1): # O(n)
graph[i] = set()
in_degree[i] = 0
for edge in trust: # O(t)
graph[edge[0]].add(edge[1])
in_degree[edge[0]] += 1
for key in in_degree.keys(): # O(n)
if in_degree[key]==0: # only one sink node, only one judge per town
judge = key
for key in graph.keys():# O(n)
if judge not in graph[key] and key!=judge: # in order to exclude the judge himself
return -1
return judge
Review
Input: n = 2, trust = [[1,2]]
- for this problem,
- graph = {1: {2}, 2: set()}
- in_degree = {1: 1, 2: 0}
- judge = 2 (in_degree[2]==0)
Output: 2
Evaluate
- time: n + t + n + n ~ O(3n + t) ~ O(n + t)
- if t « n ~ O(n)
- where n = n(number of people) and t = len(trust)
- space: O(n)
- graph and in_degree