GraphNode {
int val;
List<GraphNode> neighbors;
}
A topological sort (sometimes abbreviated toposort) or topological ordering of a directed graph is a linear ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes before v in the ordering.
There are a total of n courses you have to take, labeled from 0 to n - 1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example:
[1, 0], possible; [[1,0], [0,1]], impossible.
How would we solve it in real life?
public boolean canFinish(int numCourses, int[][] prerequisites) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<Integer>());
}
int[] preNum = new int[numCourses];
for (int i = 0; i < prerequisites.length; i++) {
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
preNum[prerequisites[i][0]]++;
}
for (int i = 0; i < numCourses; i++) {
boolean availableCourse = false;
for (int j = 0; j < numCourses; j++) {
if (preNum[j] == 0) {
for (int k : graph.get(j)) {
preNum[k]--;
}
availableCourse = true;
preNum[j] = -1;
break;
}
}
if (!availableCourse) {
return false;
}
}
return true;
}
def canFinish(numCourses, prerequisites):
graph = [[] for _ in range(numCourses)]
preNum = [0] * numCourses
for i in range(len(prerequisites)):
graph[prerequisites[i][1]].append(prerequisites[i][0])
preNum[prerequisites[i][0]] += 1
for i in range(numCourses):
availableCourse = False
for j in range(numCourses):
if preNum[j] == 0:
for k in graph[j]:
preNum[k] -= 1
availableCourse = True
preNum[j] = -1
break
if not availableCourse:
return False
return True
clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
Use BFS/DFS to go through all the nodes
Use Hashmap to remember which nodes has been visited
Clone each node with its label and is neighbors
public Node cloneGraph(Node node) {
if (node == null) {
return null;
}
Map<Node, Node> map = new HashMap<>();
return dfs(node, map);
}
private Node dfs(Node node, Map<Node, Node> map) {
if (node == null) {
return null;
}
if (map.containsKey(node)) {
return map.get(node);
}
Node copy = new Node(node.val);
map.put(node, copy);
for (Node neighbor : node.neighbors) {
copy.neighbors.add(dfs(neighbor, map));
}
return copy;
}
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return None
node_map = {}
def dfs(node):
if node in node_map:
return node_map[node]
clone = Node(node.val)
node_map[node] = clone
for neighbor in node.neighbors:
clone.neighbors.append(dfs(neighbor))
return clone
return dfs(node)
There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
For example,
Given the following words in dictionary,
[ "wrt", "wrf", "er", "ett", "rftt"]
The correct order is: "wertf".
What do we need to do?
Use the words to get the priority between two letters
Use topology sort to get the information of the order of these letters
Note:
You may assume all letters are in lowercase.
If the order is invalid, return an empty string.
There may be multiple valid order of letters, return any one of them is fine.
public String alienOrder(String[] words) {
Map<Character, Set<Character>> hm = new HashMap<>();
Set<Character> set = new HashSet<>();
Queue<Character> queue = new LinkedList<>();
StringBuilder result = new StringBuilder();
for (String word: words) {
char[] wordArray = word.toCharArray();
for (char c: wordArray) {
set.add(c);
}
}
for (Character c: set) {
hm.put(c, new HashSet<Character>());
}
for (int i = 0; i < words.length-1; i ++) {
int minLen = Math.min(words[i].length(), words[i+1].length());
int j = 0;
for(j = 0; j < minLen; j ++) {
if (words[i].charAt(j) != words[i+1].charAt(j)) {
hm.get(words[i+1].charAt(j)).add(words[i].charAt(j));
break;
}
}
if (j == minLen && words[i].length() > words[i+1].length()) return "";
}
for(Map.Entry<Character, Set<Character>> entry: hm.entrySet()) {
if (entry.getValue().isEmpty()) {
queue.add(entry.getKey());
result.append(entry.getKey());
}
}
while(!queue.isEmpty()) {
Character c = queue.poll();
for(Map.Entry<Character, Set<Character>> entry: hm.entrySet()) {
if (entry.getValue().contains(c)) {
entry.getValue().remove(c);
if (entry.getValue().isEmpty()) {
queue.add(entry.getKey());
result.append(entry.getKey());
}
}
}
}
String finalResult = result.toString();
if (finalResult.length() == set.size()) {
return finalResult;
} else {
return "";
}
}
def alienOrder(words):
hm = {}
set = set()
queue = []
result = ""
for word in words:
wordArray = list(word)
for c in wordArray:
set.add(c)
for c in set:
hm[c] = set()
for i in range(len(words)-1):
minLen = min(len(words[i]), len(words[i+1]))
j = 0
for j in range(minLen):
if words[i][j] != words[i+1][j]:
hm[words[i+1][j]].add(words[i][j])
break
if j == minLen and len(words[i]) > len(words[i+1]):
return ""
for c, values in hm.items():
if len(values) == 0:
queue.append(c)
result += c
while len(queue) > 0:
c = queue.pop(0)
for key, values in hm.items():
if c in values:
values.remove(c)
if len(values) == 0:
queue.append(key)
result += key
if len(result) == len(set):
return result
else:
return ""
For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.
Examples
Given n = 4, edges = [[1, 0], [1, 2], [1, 3]], return [1].
Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]], return [3, 4]
0 | 1 / \ 2 3
0 1 2 \ | / 3 | 4 | 5
How many Minimum Height Trees can a tree have at most?
0 | 1 / \ 2 3
0 | 1
0 | 1 | 2
At most 2!
How many Minimum Height Trees can a tree have at most?
We can use a proof by Contradiction
If there are more than 2 nodes that are the roots.
We just choose 3 of them. Then these 3 must become a circle somehow. It contradicts with the definition of tree
How many Minimum Height Trees can a tree have at most?
We can use a proof by Contradiction
m
h-m
h-m
n
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> result = new ArrayList<>();
if (n < 1) return result;
ArrayList<HashSet<Integer>> graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
graph.add(new HashSet<>());
}
for (int i = 0; i < edges.length; i++) {
graph.get(edges[i][0]).add(edges[i][1]);
graph.get(edges[i][1]).add(edges[i][0]);
}
Queue<Integer> leaves = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (graph.get(i).size() <= 1) {
leaves.add(i);
}
}
while (n > 2) {
n -= leaves.size();
Queue<Integer> newLeaves = new LinkedList<>();
while (!leaves.isEmpty()) {
Integer leaf = leaves.poll();
Integer neighbor = graph.get(leaf).iterator().next();
graph.get(neighbor).remove(leaf);
if (graph.get(neighbor).size() == 1) {
newLeaves.add(neighbor);
}
}
leaves = newLeaves;
}
return new ArrayList<>(leaves);
}
def findMinHeightTrees(n, edges):
if n < 1:
return []
graph = [[] for _ in range(n)]
for edge in edges:
graph[edge[0]].append(edge[1])
graph[edge[1]].append(edge[0])
leaves = []
for i in range(n):
if len(graph[i]) <= 1:
leaves.append(i)
while n > 2:
n -= len(leaves)
newLeaves = []
for leaf in leaves:
neighbor = graph[leaf][0]
graph[neighbor].remove(leaf)
if len(graph[neighbor]) == 1:
newLeaves.append(neighbor)
leaves = newLeaves
return leaves