This is a challenging problem involving topological sorting, grouping, and dependency ordering.
You are given:
nitems labeled from0ton-1- Each item belongs to a group (group ID)
- Some items have prerequisites (
beforeItems) - You must return a valid ordering of all items such that:
- All dependency rules are satisfied
- Items from the same group appear together
If no valid ordering exists, return an empty list.
Example:
Input: n = 8 m = 2 group = [-1,-1,1,0,0,1,0,-1] beforeItems = [ [],[6],[5],[6],[3,6],[],[],[] ] Output: [6,3,4,5,2,0,7,1]
Problem Understanding
Key details:
- Items may depend on other items
- Items are partitioned into groups
- Items with dependencies in other groups affect group ordering
- Items with -1 group must be treated as unique new groups
So ordering constraints exist at two levels:
1. Between groups
If any item in group A depends on an item in group B,
then group B must come before group A
2. Within each group
Items must follow dependency order inside their group
This means two topological sorts are needed:
✅ one for groups
✅ one for items
Logic Explained in Simple English
To solve this problem, think in these steps:
Step A: Normalize group assignments
- Items with group = -1 must be treated as belonging to their own new group
Why?
Because they must not mix with other groups.
Step B: Build two dependency graphs:
1. Item graph
- Standard dependency graph
- Edges: prerequisite → dependent item
2. Group graph
- If item X depends on item Y and they have different groups:
- Add edge from group(Y) → group(X)
Step C: Topologically sort the groups
- If impossible → return empty result
Step D: Topologically sort items within their groups
- But maintain group ordering from Step C
Step E: Assemble final result
- Output items grouped in sorted group order
- Preserve item order inside each group
This ensures:
- All dependencies respected
- Groups stay in contiguous blocks
Step-by-Step Solution Approach
- Assign new group IDs to ungrouped items
- Create:
groupGraphand group in-degreesitemGraphand item in-degrees
- For each dependency:
- Always update item graph
- If groups differ, update group graph
- Topo sort the group graph
- Topo sort the item graph
- Bucket items into their groups based on item topo order
- Output items following sorted group order
Java Implementation
class Solution {
public List<Integer> sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
// Step 1: assign new group IDs to ungrouped items
int newGroupId = m;
for (int i = 0; i < n; i++) {
if (group[i] == -1) {
group[i] = newGroupId++;
}
}
int totalGroups = newGroupId;
// Step 2: build graphs
List<List<Integer>> groupGraph = new ArrayList<>();
List<List<Integer>> itemGraph = new ArrayList<>();
for (int i = 0; i < totalGroups; i++) {
groupGraph.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
itemGraph.add(new ArrayList<>());
}
int[] groupIndegree = new int[totalGroups];
int[] itemIndegree = new int[n];
// Step 3: build edges
for (int i = 0; i < n; i++) {
for (int before : beforeItems.get(i)) {
itemGraph.get(before).add(i);
itemIndegree[i]++;
if (group[before] != group[i]) {
groupGraph.get(group[before]).add(group[i]);
groupIndegree[group[i]]++;
}
}
}
// Step 4: topological sort for groups
List<Integer> groupOrder = topoSort(groupGraph, groupIndegree, totalGroups);
if (groupOrder.isEmpty()) return new ArrayList<>();
// Step 5: topological sort for items
List<Integer> itemOrder = topoSort(itemGraph, itemIndegree, n);
if (itemOrder.isEmpty()) return new ArrayList<>();
// Step 6: bucket items by group
Map<Integer, List<Integer>> groupedItems = new HashMap<>();
for (int g : groupOrder) {
groupedItems.put(g, new ArrayList<>());
}
for (int item : itemOrder) {
groupedItems.get(group[item]).add(item);
}
// Step 7: assemble final result
List<Integer> result = new ArrayList<>();
for (int g : groupOrder) {
result.addAll(groupedItems.get(g));
}
return result;
}
private List<Integer> topoSort(List<List<Integer>> graph, int[] indegree, int size) {
Queue<Integer> queue = new LinkedList<>();
List<Integer> order = new ArrayList<>();
for (int i = 0; i < size; i++) {
if (indegree[i] == 0) queue.add(i);
}
while (!queue.isEmpty()) {
int node = queue.poll();
order.add(node);
for (int neighbor : graph.get(node)) {
indegree[neighbor]--;
if (indegree[neighbor] == 0) {
queue.add(neighbor);
}
}
}
return order.size() == size ? order : new ArrayList<>();
}
}
Dry Run Example (High Level)
Input:
n = 4, m = 1 group = [0,0,1,1] beforeItems: 0: [] 1: [0] 2: [1] 3: []
Step 1 — No -1 groups, skip assigning
Step 2 — Build graphs
Item dependencies:
0 → 1 1 → 2
Groups:
group(0)=0, group(1)=0 → same group group(1)=0, group(2)=1 → group edge 0 → 1
Step 3 — Toposort groups
Order:
[0, 1]
Step 4 — Toposort items
Order:
[0,1,2,3]
Step 5 — Bucket items:
Group 0: [0,1]
Group 1: [2,3]
Final Output:
[0,1,2,3]
Time and Space Complexity
Time Complexity
O(n + totalDependencies)
Because:
- Building graphs takes linear time
- Two topo sorts take linear time
Space Complexity
O(n + totalDependencies)
Graph storage and output
Common Mistakes and How to Avoid Them
Mistake 1: Only sorting items
Group dependencies matter.
Mistake 2: Not creating separate groups for -1 items
Those items must not mix arbitrarily.
Mistake 3: Forgetting two-level topo sorting
One level alone cannot satisfy constraints.
Mistake 4: Returning result too early
Must check cycles in both graphs.
Mistake 5: Assuming dependencies imply item-level only
Group ordering is required too.
