# 269\. Alien Dictionary -- topological sort topological sort, aka, straightening a graph to a line ![](https://hackmd.io/_uploads/HJ75-L51i.png) ![](https://hackmd.io/_uploads/HyVlpB5yo.png) [picture source](https://www.techiedelight.com/kahn-topological-sort-algorithm/) - method 1: BFS aka Kahn's algorithm - method 2: DFS - color with grey then black - kind of backtracking with early return ```python= """ BFS """ class Solution: def alienOrder(self, words: List[str]) -> str: G=collections.defaultdict(set) # Graph in_degree={c:0 for word in words for c in word } for former_word,latter_word in zip(words,words[1:]): for c,d in zip(former_word,latter_word): if c!=d: if d not in G[c]: G[c].add(d) # c < d in_degree[d]+=1 break else: # e.g., if len(former_word)>len(latter_word): return "" ans=[] q=collections.deque([c for c in in_degree if in_degree[c]==0]) while q: c=q.popleft() ans.append(c) for d in G[c]: # c < d in_degree[d]-=1 if in_degree[d]==0: q.append(d) if len(ans) < len(in_degree): return "" return ''.join(ans) """ DFS """ class Solution: def alienOrder(self, words: List[str]) -> str: reversed_adj_list = {c : [] for word in words for c in word} for first_word, second_word in zip(words,words[1::]): for c,d in zip(first_word,second_word): if c!=d: reversed_adj_list[d].append(c) # c < d break else: if len(first_word)>len(second_word): return '' seen={} # once and twice ans=[] def dfs(node): if node in seen: if seen[node]=="grey": return False # we detect a loop else: # black return True seen[node]="grey" for next_node in reversed_adj_list[node]: # all next_node < node if dfs(next_node)==False: # we detect a loop return False seen[node]="black" ans.append(node) return True if not all(dfs(node) for node in reversed_adj_list): return '' return ''.join(ans) """ DFS """ class Solution: def alienOrder(self, words: List[str]) -> str: adj_list = {c : [] for word in words for c in word} for first_word, second_word in zip(words,words[1::]): for c,d in zip(first_word,second_word): if c!=d: adj_list[c].append(d) # c < d break else: if len(first_word)>len(second_word): return '' seen={} # once:grey and twice:black ans=[] def dfs(node): if node in seen: if seen[node]=="grey": return False # we detect a loop else: # black return True seen[node]="grey" for next_node in adj_list[node]: # all next_node < node if dfs(next_node)==False: # we detect a loop return False seen[node]="black" ans.append(node) return True if not all(dfs(node) for node in adj_list): return '' return ''.join(reversed(ans)) # or you can use # return ''.join(ans)[::-1] ``` ## topological sort Leetcode https://leetcode.com/problems/alien-dictionary/solution/ Leetcode CN https://leetcode.cn/problems/alien-dictionary/solution/huo-xing-ci-dian-by-leetcode-solution-nr0l/ ### Kahn's Algorithm -- BFS -- indegree ![](https://hackmd.io/_uploads/H1xy6Hq1s.png) ![](https://hackmd.io/_uploads/SyK5lIcJs.png) [picture source](https://www.interviewkickstart.com/learn/kahns-algorithm-topological-sorting) For indegree==0, a node good to be a global source/start. Collect it, detach it from the graph, and remove the edges. ### DFS -- outdegree For outdegree==0, a node to be a global destination/end. So, the leaf node when doing DFS is safe to be a global destination/end. When firstly arriving, mark it as GREY. When bouncing back, mark it as BLACK. ![](https://hackmd.io/_uploads/BJ3jyLcyj.gif) ```python= def dfs(node): if node in seen: if seen[node] == "GREY": return False # We detect a loop else: # BLACK # It is already fully explored. return True seen[node] = "GREY" for next_node in adj_list[node]: if dfs(next_node) == False: # We detect a loop. return False # Report this to the caller. seen[node] = "BLACK" ans.append(node) # Note that the order is reversed. return True ```