🧠
DFS Postorder Topological Sort
💡 This approach uses DFS to perform a postorder traversal to generate a topological order, which is a classic method for topological sorting and helps detect cycles naturally.
Intuition
Build a graph from the inferred character order, then run DFS on each node, adding nodes to the order after visiting all descendants. If a cycle is detected during DFS, return empty string.
Algorithm
- Build a graph and initialize visitation states for each character.
- For each character, if unvisited, run DFS to visit all descendants.
- During DFS, mark nodes as visiting; if a visiting node is encountered again, a cycle exists.
- Add nodes to a stack or list after visiting descendants (postorder).
- Reverse the collected order to get the correct character order.
💡 The key difficulty is understanding how DFS postorder naturally produces a valid topological order and how cycle detection is integrated.
from collections import defaultdict
def alienOrder(words):
graph = defaultdict(set)
for i in range(len(words) - 1):
w1, w2 = words[i], words[i + 1]
min_len = min(len(w1), len(w2))
if len(w1) > len(w2) and w1[:min_len] == w2[:min_len]:
return ""
for j in range(min_len):
if w1[j] != w2[j]:
graph[w1[j]].add(w2[j])
break
visited = {} # char: 0=unvisited, 1=visiting, 2=visited
order = []
def dfs(c):
if c in visited:
return visited[c] == 2
visited[c] = 1
for nei in graph[c]:
if not dfs(nei):
return False
visited[c] = 2
order.append(c)
return True
all_chars = set(''.join(words))
for c in all_chars:
if c not in visited:
if not dfs(c):
return ""
return ''.join(order[::-1])
# Driver code
if __name__ == "__main__":
words = ["wrt","wrf","er","ett","rftt"]
print(alienOrder(words))
Line Notes
graph = defaultdict(set)Build adjacency list for character order
if len(w1) > len(w2) and w1[:min_len] == w2[:min_len]Detect invalid prefix ordering
visited = {}Track visitation state for cycle detection
def dfs(c):DFS function returns False if cycle detected
visited[c] = 1Mark node as visiting to detect cycles
for nei in graph[c]:Visit all neighbors recursively
visited[c] = 2Mark node as fully visited after descendants
order.append(c)Add node postorder for topological sort
for c in all_chars:Run DFS on all characters to cover disconnected graph
import java.util.*;
public class AlienDictionaryDFS {
Map<Character, Set<Character>> graph = new HashMap<>();
Map<Character, Integer> visited = new HashMap<>(); // 0=unvisited,1=visiting,2=visited
StringBuilder order = new StringBuilder();
public String alienOrder(String[] words) {
for (int i = 0; i < words.length - 1; i++) {
String w1 = words[i], w2 = words[i + 1];
int minLen = Math.min(w1.length(), w2.length());
if (w1.length() > w2.length() && w1.startsWith(w2)) return "";
for (int j = 0; j < minLen; j++) {
if (w1.charAt(j) != w2.charAt(j)) {
graph.computeIfAbsent(w1.charAt(j), k -> new HashSet<>()).add(w2.charAt(j));
break;
}
}
}
Set<Character> allChars = new HashSet<>();
for (String w : words) {
for (char c : w.toCharArray()) {
allChars.add(c);
}
}
for (char c : allChars) {
if (!visited.containsKey(c)) {
if (!dfs(c)) return "";
}
}
return order.reverse().toString();
}
private boolean dfs(char c) {
visited.put(c, 1);
for (char nei : graph.getOrDefault(c, Collections.emptySet())) {
if (!visited.containsKey(nei)) {
if (!dfs(nei)) return false;
} else if (visited.get(nei) == 1) {
return false;
}
}
visited.put(c, 2);
order.append(c);
return true;
}
public static void main(String[] args) {
AlienDictionaryDFS sol = new AlienDictionaryDFS();
String[] words = {"wrt","wrf","er","ett","rftt"};
System.out.println(sol.alienOrder(words));
}
}
Line Notes
Map<Character, Set<Character>> graph = new HashMap<>()Adjacency list for character graph
if (w1.length() > w2.length() && w1.startsWith(w2)) return "";Detect invalid prefix ordering
visited = new HashMap<>()Track visitation state for cycle detection
if (!visited.containsKey(c))Start DFS only on unvisited nodes
visited.put(c, 1)Mark node as visiting to detect cycles
for (char nei : graph.getOrDefault(c, Collections.emptySet()))Visit neighbors recursively
if (visited.get(nei) == 1)Cycle detected if neighbor is visiting
visited.put(c, 2)Mark node as visited after descendants
order.append(c)Add node postorder for topological order
return order.reverse().toString()Reverse postorder to get correct order
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <string>
using namespace std;
class Solution {
unordered_map<char, unordered_set<char>> graph;
unordered_map<char, int> visited; // 0=unvisited,1=visiting,2=visited
string order;
bool dfs(char c) {
visited[c] = 1;
for (char nei : graph[c]) {
if (visited[nei] == 0) {
if (!dfs(nei)) return false;
} else if (visited[nei] == 1) {
return false;
}
}
visited[c] = 2;
order.push_back(c);
return true;
}
public:
string alienOrder(vector<string>& words) {
for (int i = 0; i < (int)words.size() - 1; i++) {
string& w1 = words[i], &w2 = words[i + 1];
int minLen = min(w1.size(), w2.size());
if (w1.size() > w2.size() && w1.substr(0, minLen) == w2.substr(0, minLen))
return "";
for (int j = 0; j < minLen; j++) {
if (w1[j] != w2[j]) {
graph[w1[j]].insert(w2[j]);
break;
}
}
}
unordered_set<char> allChars;
for (auto& w : words) {
for (char c : w) allChars.insert(c);
}
for (char c : allChars) {
if (visited[c] == 0) {
if (!dfs(c)) return "";
}
}
reverse(order.begin(), order.end());
return order;
}
};
int main() {
vector<string> words = {"wrt","wrf","er","ett","rftt"};
Solution sol;
cout << sol.alienOrder(words) << endl;
return 0;
}
Line Notes
unordered_map<char, unordered_set<char>> graph;Adjacency list for character graph
if (w1.size() > w2.size() && w1.substr(0, minLen) == w2.substr(0, minLen))Detect invalid prefix ordering
unordered_map<char, int> visited;Track visitation state for cycle detection
visited[c] = 1;Mark node as visiting to detect cycles
for (char nei : graph[c])Visit neighbors recursively
if (visited[nei] == 1)Cycle detected if neighbor is visiting
visited[c] = 2;Mark node as visited after descendants
order.push_back(c);Add node postorder for topological order
reverse(order.begin(), order.end());Reverse postorder to get correct order
var alienOrder = function(words) {
const graph = new Map();
for (let i = 0; i < words.length - 1; i++) {
const w1 = words[i], w2 = words[i + 1];
const minLen = Math.min(w1.length, w2.length);
if (w1.length > w2.length && w1.startsWith(w2)) return "";
for (let j = 0; j < minLen; j++) {
if (w1[j] !== w2[j]) {
if (!graph.has(w1[j])) graph.set(w1[j], new Set());
graph.get(w1[j]).add(w2[j]);
break;
}
}
}
const visited = new Map(); // 0=unvisited,1=visiting,2=visited
const order = [];
function dfs(c) {
if (visited.get(c) === 1) return false;
if (visited.get(c) === 2) return true;
visited.set(c, 1);
for (const nei of (graph.get(c) || [])) {
if (!dfs(nei)) return false;
}
visited.set(c, 2);
order.push(c);
return true;
}
const allChars = new Set(words.join(''));
for (const c of allChars) {
if (!visited.has(c)) {
if (!dfs(c)) return "";
}
}
return order.reverse().join("");
};
// Driver code
const words = ["wrt","wrf","er","ett","rftt"];
console.log(alienOrder(words));
Line Notes
const graph = new Map();Adjacency list for character graph
if (w1.length > w2.length && w1.startsWith(w2)) return "";Detect invalid prefix ordering
const visited = new Map();Track visitation state for cycle detection
if (visited.get(c) === 1) return false;Cycle detected if node is visiting
visited.set(c, 1);Mark node as visiting
for (const nei of (graph.get(c) || []))Visit neighbors recursively
visited.set(c, 2);Mark node as visited after descendants
order.push(c);Add node postorder for topological order
return order.reverse().join("");Reverse postorder to get correct order
TimeO(C + U + E) where C is total chars, U unique chars, E edges
SpaceO(U + E) for graph and recursion stack
Build graph by comparing adjacent words, then DFS each node once. Cycle detection is integrated in DFS.
💡 For 1 million characters, DFS visits each node and edge once, making it efficient and scalable.
Interview Verdict: Accepted
This is the optimal and most common approach for this problem in interviews, demonstrating mastery of DFS and cycle detection.