Last Updated: March 28, 2026
This problem asks us to sort items while satisfying two constraints at the same time. First, there are explicit ordering dependencies: some items must appear before others, as given by beforeItems. Second, there is a grouping constraint: all items belonging to the same group must be adjacent in the final ordering.
Think of it like scheduling tasks for different teams in a company. Each task may depend on other tasks being completed first, and you want each team's tasks grouped together in the schedule so the team can work in a focused block. You need a single ordering that respects both the dependencies and the grouping requirement.
The key observation is that this is really two topological sort problems stacked on top of each other. We need to figure out (1) what order do the group blocks appear in, and (2) within each group block, what order do the items appear in. If any item A in group X must come before item B in group Y, that also tells us group X's block must appear before group Y's block. Both levels of ordering can be computed with topological sort, and if either level has a cycle, no valid answer exists.
1 <= m <= n <= 3 * 10^4 -> With up to 30,000 items, we need an approach that runs in roughly O(n + E) time, where E is the total number of dependency edges. Anything quadratic is risky.0 <= beforeItems[i].length <= n - 1 -> Each item can depend on up to n-1 others. The total number of edges across all items could be O(n^2) in the worst case, though typical inputs are much sparser.group[i] == -1 means the item belongs to no group -> These ungrouped items need special handling. Since they do not need to be adjacent to anything, they can be placed freely as long as dependencies are satisfied.The most natural first attempt is to ignore the grouping constraint entirely and just do a regular topological sort on all items using the beforeItems dependencies. Topological sort gives us a valid ordering where every dependency is respected, so that part is handled.
The problem? This ordering has no reason to keep items from the same group together. For instance, if items 2 and 5 are in group 1, the topological sort might produce something like [..., 2, ..., 3, 4, ..., 5, ...] with items from other groups sandwiched between 2 and 5. That violates the grouping constraint.
You might think: "Okay, after the topological sort, just rearrange items so that group members are adjacent." But rearranging can break the dependency ordering. If item 3 (group 0) must come before item 5 (group 1), and item 2 (group 1) must come after some item that comes after 3, moving 2 next to 5 might place 2 before 3, violating a dependency.
So a single-level topological sort is fundamentally insufficient. We need to think about this differently.
beforeItems dependencies.This approach cannot be implemented cleanly because step 4 (rearranging while preserving dependencies) is essentially solving the original problem all over again.
A single topological sort has no mechanism to enforce grouping. It sorts items by dependencies alone, and there is no way to inject the "keep groups together" constraint into a standard topological sort. What if we sorted at two levels: first determining the order of groups relative to each other, then the order of items within each group?
The breakthrough insight is to decompose this into two separate topological sort problems that work together harmoniously.
Picture the final output as a sequence of "group blocks," where each block contains all items from one group in some order. We need to decide two things: (1) what order do the group blocks appear in, and (2) within each block, what order do the items appear in.
Both of these orderings are constrained by the beforeItems dependencies. If item A (in group X) must come before item B (in group Y, where X != Y), then group X's block must come before group Y's block entirely. If A and B are both in the same group, then A must come before B within that group's block.
So we build two graphs: a group-level graph where edges represent "group X must come before group Y" relationships derived from inter-group item dependencies, and an item-level graph where edges represent the original beforeItems dependencies (used for within-group sorting).
For ungrouped items (where group[i] == -1), we assign each one its own unique "virtual group" of size 1. A group with one item trivially satisfies the adjacency requirement. This eliminates the special case entirely.
The two-level topological sort works because it cleanly separates the two constraints into independent subproblems. The group-level sort ensures that if any item in group X must come before any item in group Y, then the entire group X block appears before group Y's block. The item-level sort within each group ensures the correct internal ordering. Since we output each group's items as a contiguous block, the grouping constraint is automatically satisfied.
The trick of assigning unique group IDs to ungrouped items is what makes this approach so clean. An ungrouped item in its own virtual group of size 1 trivially satisfies the "group members must be adjacent" constraint.
group[i] == -1, assign group[i] = nextGroupId++ starting from m. Track the total number of groups.i and each predecessor p in beforeItems[i], add edge p -> i to the item graph. If group[p] != group[i], also add edge group[p] -> group[i] to the group graph (with deduplication).