There are n items each belonging to zero or one of m groups where group[i] is the group that the i-th item belongs to and it's equal to -1 if the i-th item belongs to no group. The items and the groups are zero indexed. A group can have no item belonging to it.
Return a sorted list of the items such that:
beforeItems[i] is a list containing all the items that should come before the i-th item in the sorted array (to the left of the i-th item).Return any solution if there is more than one solution and return an empty list if there is no solution.
Example 1:

Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]] Output: [6,3,4,1,5,2,0,7]
Example 2:
Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]] Output: [] Explanation: This is the same as example 1 except that 4 needs to be before 6 in the sorted list.
Constraints:
1 <= m <= n <= 3 * 104group.length == beforeItems.length == n-1 <= group[i] <= m - 10 <= beforeItems[i].length <= n - 10 <= beforeItems[i][j] <= n - 1i != beforeItems[i][j]beforeItems[i] does not contain duplicates elements.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach involves trying every single possible arrangement of the items to see if it meets all the requirements. This means we will explore all combinations of item order and group assignments. We then check if each arrangement is valid based on the dependencies between items and groups.
Here's how the algorithm would work step-by-step:
def sort_items_brute_force(num_items, num_groups, group_id, before_items, before_groups):
import itertools
best_arrangement = None
min_violations = float('inf')
for item_permutation in itertools.permutations(range(num_items)):
for group_assignments in itertools.product(range(num_groups + 1), repeat=num_items):
arrangement = list(zip(item_permutation, group_assignments))
item_order_valid = True
for item_index, item in enumerate(item_permutation):
for before_item in before_items[item]:
if item_permutation.index(before_item) > item_index:
item_order_valid = False
break
if not item_order_valid:
break
group_order_valid = True
#Checking if groups order is maintained
group_order = {}
for item_index, item in enumerate(item_permutation):
group_id_for_item = group_assignments[item]
if group_id_for_item not in group_order:
group_order[group_id_for_item] = item_index
for group_index in range(num_groups):
for before_group in before_groups[group_index]:
if group_index in group_order and before_group in group_order:
if group_order[group_index] > group_order[before_group]:
group_order_valid = False
break
if not group_order_valid:
break
if item_order_valid and group_order_valid:
return [item for item, group in arrangement]
# Calculate the number of dependency violations if invalid
num_violations = 0
for item_index, item in enumerate(item_permutation):
for before_item in before_items[item]:
if item_permutation.index(before_item) > item_index:
num_violations += 1
group_order = {}
for item_index, item in enumerate(item_permutation):
group_id_for_item = group_assignments[item]
if group_id_for_item not in group_order:
group_order[group_id_for_item] = item_index
for group_index in range(num_groups):
for before_group in before_groups[group_index]:
if group_index in group_order and before_group in group_order:
if group_order[group_index] > group_order[before_group]:
num_violations += 1
# Track best solution with the least violations
if num_violations < min_violations:
min_violations = num_violations
best_arrangement = [item for item, group in arrangement]
if best_arrangement is not None:
return best_arrangement
else:
return []The problem requires us to sort items that belong to groups, and some items must come before others. We solve it by first ordering the groups and then ordering the items within each group, always respecting the dependencies.
Here's how the algorithm would work step-by-step:
from collections import defaultdict
def sort_items_by_groups_respecting_dependencies(
number_of_items,
group_id_for_item,
before_items
):
# Handle items without a group
for item_index in range(number_of_items):
if group_id_for_item[item_index] == -1:
group_id_for_item[item_index] = number_of_items + item_index
number_of_groups = max(group_id_for_item) + 1
group_graph = defaultdict(list)
group_in_degree = [0] * number_of_groups
item_graph = defaultdict(list)
item_in_degree = [0] * number_of_items
# Build the graph for groups and items
for item_index in range(number_of_items):
for before_item in before_items[item_index]:
if group_id_for_item[item_index] != group_id_for_item[before_item]:
group_index = group_id_for_item[item_index]
before_group_index = group_id_for_item[before_item]
group_graph[before_group_index].append(group_index)
group_in_degree[group_index] += 1
else:
item_graph[before_item].append(item_index)
item_in_degree[item_index] += 1
# Topologically sort the groups.
group_order = topological_sort(number_of_groups, group_graph, group_in_degree)
if len(group_order) != number_of_groups:
return []
# Topologically sort the items within each group.
item_orders = {}
for group_index in range(number_of_groups):
item_orders[group_index] = []
for item_index in range(number_of_items):
group_index = group_id_for_item[item_index]
item_orders[group_index].append(item_index)
sorted_items = []
for group_index in group_order:
items_in_group = item_orders[group_index]
number_of_items_in_group = len(items_in_group)
group_item_graph = defaultdict(list)
group_item_in_degree = [0] * number_of_items
# Filter graph to only consider items within the group
group_item_map = {item: i for i, item in enumerate(items_in_group)}
for item in items_in_group:
for next_item in item_graph[item]:
if next_item in items_in_group:
item_index = group_item_map[item]
next_item_index = group_item_map[next_item]
group_item_graph[item_index].append(next_item_index)
group_item_in_degree[next_item_index] += 1
group_item_order = topological_sort(
number_of_items_in_group,
group_item_graph,
group_item_in_degree
)
# If a topological sort is not possible,
# the graph has a cycle.
if len(group_item_order) != number_of_items_in_group:
return []
# Map back from group item indices to actual item indices
actual_item_order = [items_in_group[i] for i in group_item_order]
sorted_items.extend(actual_item_order)
return sorted_items
def topological_sort(number_of_nodes, graph, in_degree):
# Initialize queue with nodes with zero in-degree.
queue = [
node_index
for node_index in range(number_of_nodes)
if in_degree[node_index] == 0
]
sorted_list = []
# Process each node until queue is empty.
while queue:
node = queue.pop(0)
sorted_list.append(node)
# Update in-degrees of neighboring nodes.
for neighbor in graph[node]:
in_degree[neighbor] -= 1
# Add neighbors with zero in-degree to queue.
if in_degree[neighbor] == 0:
queue.append(neighbor)
# Return an empty array if there is a cycle
return sorted_list| Case | How to Handle |
|---|---|
| Null or empty item list | Return an empty list immediately as there's nothing to sort. |
| Null or empty group list when groups exist | Treat items without a specified group as belonging to a single, independent group and continue the topological sort. |
| Item dependencies forming a cycle (directed graph has a cycle) | Detect cycles during topological sort; return an empty list to indicate no valid ordering exists. |
| Group dependencies forming a cycle (directed graph has a cycle) | Detect cycles during topological sort; return an empty list to indicate no valid ordering exists. |
| Items belong to non-existent groups (group id out of range) | Handle invalid group IDs by either skipping them or throwing an exception (clarify with interviewer). |
| Large number of items and groups leading to potential memory issues | Ensure data structures (adjacency lists, dependency counts) are memory efficient. |
| Maximum integer values used for item and group indices | Verify the indices can be safely used as array indices or keys in hashmaps without overflow. |
| Dependencies list is longer than item list | Ensure the dependency processing handles edge case when there is no item for the dependency. |