The row indices of the numbers must be a permutation of row indices
. Say we only have
rowConditions — no conditions on the columns. We can build a directed graph with vertex set and edge set
. Then, from a topological ordering of
, we can find a valid permuation of row indices
. Note,
is a transitive relation but it is neither reflexive nor symmetric. Consequently, any cycle, including self-loop, signals absence of a solution.
Likewise, with only colConditions, from a similar directed graph with the same , but with
, we can first find a topological ordering of
and from that we can find a valid permutation of column indices
.
Insight: Row placement is independent from column placement.
Now, in the matrix, we put in the cell
.
DFS
Time and space complexity comes from building the graph. Vertex set, , has size
and edge set,
, has size
. Time and space complexity:
.
class Solution:
def build_graph(self, k, edges) -> Dict[int, Set[int]]:
# V: {1, 2, ..., k}
# E: {(u, v) : u is-strictly-above-of v}
# E: {(u, v) : u is-strictly-left-of v}
adj_list = { u : set() for u in range(1, k+1) }
for u, v in edges:
adj_list[u].add(v)
return adj_list
def explore(self, u, adj_list, visited, onstack, post) -> bool:
visited.add(u)
onstack.add(u)
for v in adj_list[u]:
if (v in visited) and (v in onstack):
# Cycle
return True
if (v in visited):
continue
if self.explore(v, adj_list, visited, onstack, post):
return True
post.append(u)
onstack.remove(u)
return False
def dfs(self, adj_list) -> Tuple[bool, List[int]]:
visited, onstack = set(), set()
post = []
for u in adj_list:
if u in visited:
continue
if self.explore(u, adj_list, visited, onstack, post):
return True, []
return False, post[::-1]
def sorted_topo(self, adj_list) -> Dict[int, int]:
has_cycle, topo = self.dfs( adj_list )
if has_cycle:
return {}
return {x : coord for coord, x in enumerate(topo)}
def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]:
row_adj_list = self.build_graph(k, rowConditions)
row_sorted = self.sorted_topo(row_adj_list)
if not row_sorted:
return []
col_adj_list = self.build_graph(k, colConditions)
col_sorted = self.sorted_topo(col_adj_list)
if not col_sorted:
return []
matrix = [ [0]*k for _ in range(k) ]
for x in range(1, k+1):
r, c = row_sorted[x], col_sorted[x]
matrix[r][c] = x
return matrix
In-degree
class Solution:
def build_graph(self, k, edges) -> Dict[int, Set[int]]:
# V: {1, 2, ..., k}
# E: {(u, v) : u is-strictly-above-of v}
# E: {(u, v) : u is-strictly-left-of v}
adj_list = { u : set() for u in range(1, k+1) }
for u, v in edges:
adj_list[u].add(v)
return adj_list
def build_indegree_map(self, adj_list) -> Dict[int, int]:
inmap = {u : 0 for u in adj_list}
for u in adj_list:
for v in adj_list[u]:
inmap[v] += 1
return inmap
def sorted_topo(self, adj_list) -> Dict[int, int]:
inmap = self.build_indegree_map( adj_list )
curr_sources = [u for u, d in inmap.items() if d == 0]
topo = []
while curr_sources:
topo.extend(curr_sources)
new_sources = []
for u in curr_sources:
for v in adj_list[u]:
inmap[v] -= 1
if inmap[v] == 0:
new_sources.append(v)
curr_sources = new_sources
if len(topo) != len(adj_list):
# cycle
return {}
return {x : coord for coord, x in enumerate(topo)}
def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]:
row_adj_list = self.build_graph(k, rowConditions)
row_sorted = self.sorted_topo(row_adj_list)
if not row_sorted:
return []
col_adj_list = self.build_graph(k, colConditions)
col_sorted = self.sorted_topo(col_adj_list)
if not col_sorted:
return []
matrix = [ [0]*k for _ in range(k) ]
for x in range(1, k+1):
r, c = row_sorted[x], col_sorted[x]
matrix[r][c] = x
return matrix
Leave a comment