You are given a positive integer k. You are also given:
2D integer array rowConditions of size n where rowConditions[i] = [above[i], below[i]], and2D integer array colConditions of size m where colConditions[i] = [left[i], right[i]].The two arrays contain integers from 1 to k.
You have to build a k x k matrix that contains each of the numbers from 1 to k exactly once. The remaining cells should have the value 0.
The matrix should also satisfy the following conditions:
The number above[i] should appear in a row that is strictly above the row at which the number below[i] appears for all i from 0 to n - 1.
The number left[i] should appear in a column that is strictly left of the column at which the number right[i] appears for all i from 0 to m - 1.
Return any matrix that satisfies the conditions. If no answer exists, return an empty matrix.
Example 1:
Input: k = 3, rowConditions = [[2,1],[1,3]], colConditions = [[3,1],[2,3]]
Output: [[2,0,0],[0,0,1],[0,3,0]]Example 2:
Input: k = 3, rowConditions = [[1,2],[2,3],[3,1],[2,3]], colConditions = [[2,1]]
Output: []Constraints:
2 <= k <= 4001 <= rowConditions.length, colConditions.length <= 10,000rowConditions[i].length == colConditions[i].length == 21 <= above[i], below[i], left[i], right[i] <= kabove[i] != below[i]left[i] != right[i]Before attempting this problem, you should be comfortable with:
The problem asks us to place numbers 1 to k in a k x k matrix such that certain numbers appear above others (row conditions) and certain numbers appear to the left of others (column conditions). This is essentially two independent ordering problems: one for rows and one for columns.
Each set of conditions forms a directed graph where an edge from A to B means A must come before B. Finding a valid ordering that satisfies all constraints is exactly what topological sort does. If there's a cycle in either graph, no valid ordering exists and we return an empty matrix.
rowConditions and colConditions.rowConditions using DFS:colConditions similarly.1 to k at the position determined by these mappings.class Solution:
def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]:
def dfs(src, adj, visit, path, order):
if src in path:
return False
if src in visit:
return True
visit.add(src)
path.add(src)
for nei in adj[src]:
if not dfs(nei, adj, visit, path, order):
return False
path.remove(src)
order.append(src)
return True
def topo_sort(edges):
adj = defaultdict(list)
for src, dst in edges:
adj[src].append(dst)
visit, path = set(), set()
order = []
for src in range(1, k + 1):
if src not in visit:
if not dfs(src, adj, visit, path, order):
return []
return order[::-1]
row_order = topo_sort(rowConditions)
if not row_order: return []
col_order = topo_sort(colConditions)
if not col_order: return []
val_to_row = {num: i for i, num in enumerate(row_order)}
val_to_col = {num: i for i, num in enumerate(col_order)}
res = [[0] * k for _ in range(k)]
for num in range(1, k + 1):
r, c = val_to_row[num], val_to_col[num]
res[r][c] = num
return resWhere is the size of the array , is the size of the array , and is the size of the output matrix.
This approach solves the same problem using Kahn's algorithm (BFS-based topological sort) instead of DFS. The key insight remains the same: we need valid orderings for both rows and columns. Kahn's algorithm processes nodes with zero incoming edges first, which naturally produces a valid topological order when no cycle exists.
If we process fewer than k nodes, it means there's a cycle in the graph and no valid ordering is possible.
k nodes, a cycle exists; return empty matrix.class Solution:
def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]:
def topo_sort(edges):
indegree = [0] * (k + 1)
adj = [[] for _ in range(k + 1)]
for u, v in edges:
adj[u].append(v)
indegree[v] += 1
order = []
q = deque()
for i in range(1, k + 1):
if not indegree[i]:
q.append(i)
while q:
node = q.popleft()
order.append(node)
for nei in adj[node]:
indegree[nei] -= 1
if not indegree[nei]:
q.append(nei)
return order
row_order = topo_sort(rowConditions)
if len(row_order) != k: return []
col_order = topo_sort(colConditions)
if len(col_order) != k: return []
res = [[0] * k for _ in range(k)]
colIndex = [0] * (k + 1)
for i in range(k):
colIndex[col_order[i]] = i
for i in range(k):
res[i][colIndex[row_order[i]]] = row_order[i]
return resWhere is the size of the array , is the size of the array , and is the size of the output matrix.
Row conditions and column conditions must be processed as two separate, independent graphs. Combining them into a single graph leads to incorrect cycle detection and wrong orderings since row ordering has no relation to column ordering.
When using DFS for topological sort, you need both a visited set (permanently processed nodes) and a path set (nodes in current recursion stack). Using only one set fails to detect back edges that indicate cycles.
# Wrong: single visited set misses cycles
if src in visited:
return True # Should check path set too
# Correct: check both sets
if src in path:
return False # Cycle detected
if src in visited:
return True # Already processedNumbers from 1 to k that don't appear in any condition still need positions in the matrix. The topological sort must iterate over all values 1 to k, not just those present in the edge list.
The values are 1 to k (1-indexed) but arrays are typically 0-indexed. When building adjacency lists or index mappings, mixing these up causes index out of bounds errors or incorrect placements.