269. Alien Dictionary
Date: 2026-01-31 Difficulty: Hard Topics: Topological Sort, BFS, Graph, Kahn’s Algorithm Link: https://leetcode.com/problems/alien-dictionary/
Final Solution
class Solution:
def alienOrder(self, words: List[str]) -> str:
edges = []
nodes = set()
result = []
# Collect all unique characters
for word in words:
for char in word:
nodes.add(char)
# Extract ordering relationships from adjacent words
for i in range(len(words) - 1):
currWord = words[i]
nextWord = words[i+1]
minLen = min(len(currWord), len(nextWord))
foundDiff = False
for j in range(minLen):
currLetter = currWord[j]
nextLetter = nextWord[j]
if currLetter != nextLetter:
edges.append([currLetter, nextLetter])
foundDiff = True
break
if not foundDiff and len(currWord) > len(nextWord):
return ""
# Build graph and run topological sort
queue = deque()
adj_list = {node: [] for node in nodes}
in_edges = {node: 0 for node in nodes}
for letter, nextLetter in edges:
adj_list[letter].append(nextLetter)
in_edges[nextLetter] += 1
for letter in in_edges:
if in_edges[letter] == 0:
queue.append(letter)
while queue:
letter = queue.popleft()
result.append(letter)
dependencies = adj_list[letter]
for dep in dependencies:
in_edges[dep] -= 1
if in_edges[dep] == 0:
queue.append(dep)
return "".join(result) if len(result) == len(nodes) else ""Initial Issues
-
Edge extraction loop bug: Used
for j in range(currWord)instead offor j in range(len(currWord)) -
Missing break: Didn’t break after finding first difference - only the first differing character tells you ordering
-
Invalid prefix case: Didn’t handle case where
currWordis longer andnextWordis a prefix (e.g., [“abc”, “ab”] is invalid). Need to check this only when no difference was found (for...elseor flag) -
Node collection bug: Initially tried to collect nodes during edge comparison loop, but this misses characters that appear after the first difference
-
Adjacency list direction wrong: Built
adj_list[nextLetter].append(letter)(backwards) instead ofadj_list[letter].append(nextLetter)(forward). When processing a node, need to decrement in_degree of nodes that come AFTER it, not before.
Key Learnings
-
Core insight: Comparing adjacent words reveals character ordering. First differing character tells you
currWord[j]comes beforenextWord[j] -
Two-phase approach:
- Extract edges from word comparisons
- Run topological sort on the character graph
-
Invalid input cases:
- Prefix case: [“abc”, “ab”] - longer word can’t come before its prefix
- Cycle: detected by
len(result) != len(nodes)
-
Adjacency list direction matters: When node A → B (A before B):
- Store B in A’s adjacency list
- When A is processed, decrement B’s in_degree
Nuances to Remember
- Collect ALL unique characters first, not during edge extraction
- Only first differing character between adjacent words gives ordering info
for...elsepattern: else runs only if loop completes without break- Characters with no ordering constraints still need to appear in output (in_degree 0 from start)
Edge Extraction Pattern
for j in range(min(len(word1), len(word2))):
if word1[j] != word2[j]:
# word1[j] comes before word2[j]
edges.append([word1[j], word2[j]])
break
else:
# No difference found - check prefix case
if len(word1) > len(word2):
return "" # Invalid