Sort Items by Groups Respecting Dependencies

HardGraphs · Two-Level Topological Sort🧩 Topological SortLeetCode #1203 ↗Updated

Problem

There are n items and m groups. group[i] is the group of item i, or -1 if it belongs to none. beforeItems[i] lists items that must appear before item i. Return any ordering of all items that respects every dependency and keeps the items of each group next to each other; return an empty array if none exists.
Inputn=4, m=1, group=[-1,0,0,-1], before=[[],[0],[1],[2]]Output[0,1,2,3]
Inputn=3, m=1, group=[0,-1,0], before=[[],[0],[1]]Output[] (item 1 would split group 0)
Inputn=3, m=0, group=[-1,-1,-1], before=[[],[],[]]Outputany permutation, e.g. [0,1,2]

1 ≤ m ≤ n ≤ 3·104  ·  -1 ≤ group[i] ≤ m-1  ·  0 ≤ beforeItems[i].length ≤ n-1  ·  no self or duplicate dependencies

Try it yourself

Java
class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Split it into two independent ordering problems: a topological order of the groups (a dependency between items of different groups is an edge between their groups) and a topological order of the items. Give each ungrouped item its own private group, then list groups in order, each with its items in item order.
PatternTopological Sort

Instinct Tasks with prerequisites, "X must come before Y", or an order hidden in comparisons: build a directed graph and peel off nodes with zero in-degree.

Memory hack No prereqs? Off you go. Each finished task frees its followers; if someone is never freed, there is a cycle.

Open the Topological Sort guide →

Approaches

Brute ForceBacktracking With a “Finish Your Group” Rule

Time O(2n·n·(n + m))Space O(2n·(n + m))

Build the order one item at a time: an item is eligible if all its prerequisites are placed. To keep groups contiguous, once we place an item of a group that still has unplaced items, the next item must come from that same group. Dead states (placed set, open group) are memoised.

Java
private int n, totalGroups;
private int[] grp, needMask, remaining, order;
private Set<Long> dead;

public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
    this.n = n;
    grp = group.clone();
    totalGroups = m;
    for (int i = 0; i < n; i++) if (grp[i] == -1) grp[i] = totalGroups++;
    remaining = new int[totalGroups];
    needMask = new int[n]; // bitmask of prerequisites (tiny n only)
    for (int i = 0; i < n; i++) {
        remaining[grp[i]]++;
        for (int p : beforeItems.get(i)) needMask[i] |= 1 << p;
    }
    dead = new HashSet<>();
    order = new int[n];
    return place(0, 0, -1) ? order : new int[0];
}

// open = group that must be continued next, or -1 if any eligible item may follow
private boolean place(int mask, int pos, int open) {
    if (pos == n) return true;
    long key = (long) mask * (totalGroups + 1) + (open + 1);
    if (dead.contains(key)) return false;
    for (int i = 0; i < n; i++) {
        if ((mask >> i & 1) == 1 || (needMask[i] & ~mask) != 0) continue;
        if (open != -1 && grp[i] != open) continue;
        order[pos] = i;
        remaining[grp[i]]--;
        int next = remaining[grp[i]] > 0 ? grp[i] : -1;
        boolean ok = place(mask | 1 << i, pos + 1, next);
        remaining[grp[i]]++;
        if (ok) return true;
    }
    dead.add(key);
    return false;
}

Exponential — only workable for tiny n, but a handy checker for small cases.

At a glance

LevelApproachTimeSpace
Brute ForceBacktracking With a “Finish Your Group” RuleO(2n·n·(n + m))O(2n·(n + m))
OptimalTwo-Level Kahn's AlgorithmO(n + m + E)O(n + m + E)
Common pitfalls
  • Lumping all -1 items into one shared group — they must stay independent.
  • Sorting groups only — dependencies inside a group still need an item order.
  • Missing a group-level cycle that has no item-level cycle (A→B→A across items).
Interview tips
  • Say up front: “two topological sorts — one for groups, one for items”.
  • Explain why the bucketed item order still respects intra-group edges.
  • Factor Kahn's into one helper and call it twice; it keeps the code short and clear.
Pattern
Hierarchical topological sort → ordering with dependencies plus a “keep clusters together” constraint.
My notes saved in this browser