AlgoMaster Logo

Sort Items by Groups Respecting Dependencies

hardFrequency6 min readUpdated June 23, 2026

Understanding the Problem

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.

A useful analogy is scheduling tasks for different teams in a company. Each task may depend on other tasks finishing first, and each team's tasks should sit together in the schedule so the team works in one focused block. We need a single ordering that respects both the dependencies and the grouping requirement.

This is two topological sort problems stacked on top of each other. We need to figure out two things: what order the group blocks appear in, and within each group block, what order the items appear in. If any item A in group X must come before item B in group Y, then 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.

Key Constraints:

  • 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.

Approach 1: Single-Level Topological Sort (Naive)

Intuition

One starting point is to ignore the grouping constraint and run a regular topological sort on all items using the beforeItems dependencies. Topological sort produces a valid ordering where every dependency is respected, so that constraint is handled.

This ordering has no reason to keep items from the same group together. For example, 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 placed between 2 and 5. That violates the grouping constraint.

Rearranging the result afterward to pull group members next to each other 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 itself comes after 3, then moving 2 next to 5 might place 2 before 3 and violate a dependency.

A single-level topological sort has no mechanism to enforce grouping.

Algorithm

  1. Build a directed graph from beforeItems dependencies.
  2. Run topological sort (BFS or DFS) on all n items.
  3. If a cycle is detected, return an empty list.
  4. Try to rearrange the result so group members are adjacent.
  5. If rearrangement breaks any dependency, return an empty list.

Step 4 is the obstacle: rearranging while preserving dependencies is the original problem again, not a simpler subproblem. A standard topological sort orders items by dependencies alone, with no way to express "keep these items together." The fix is to sort at two levels, first ordering the groups relative to each other, then ordering the items within each group.

Approach 2: Two-Level Topological Sort (Kahn's BFS)

Intuition

Decompose the problem into two separate topological sorts. The final output is a sequence of group blocks, where each block contains all items from one group in some order. Two decisions remain: what order the group blocks appear in, and within each block, what order the items appear in.

Both orderings come from 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 cross-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), assign each one its own unique virtual group of size 1. A group with one item satisfies the adjacency requirement automatically, which removes the special case.

Algorithm

  1. Assign unique group IDs to ungrouped items. For each item where group[i] == -1, assign group[i] = nextGroupId++ starting from m. Track the total number of groups.
  2. Build two graphs: For each item 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).
  3. Map items to groups. Create a list for each group containing its member items.
  4. Topological sort on the group graph. Use Kahn's BFS. If not all groups are processed, a cycle exists. Return an empty list.
  5. Topological sort within each group. For each group in the order from step 4, compute local in-degrees (counting only predecessors within the same group), run BFS, and append sorted items to the result. If not all members are processed, return an empty list.
  6. Return the concatenated result.

Example Walkthrough

1Assign virtual groups: item 0→G2, item 1→G3, item 7→G4. Build group graph: edge G0→G3
G0 (3,4,6)G3 (1)G1 (2,5)G2 (0)G4 (7)
1/7

Code