Sort Items by Groups Respecting Dependencies solution and explanation
9 more videos on this problem
Sort Items by Groups Respecting Dependencies | Broken into Simple Steps | Intuition | Leetcode-1203codestorywithMIK · 38:24 · 10K views
LeetCode 1203. Sort Items by Groups Respecting DependenciesHappy Coding · 35:32 · 5K views
Leetcode 1203 Sort Items by Groups Respecting DependenciesJeevan Kumar - Cracckify · 23:23 · 4K views
Leetcode 1203 Sort Items by Groups Respecting DependenciesRen Zhang · 34:56 · 2K views
Leetcode 1203. Sort Items by Groups Respecting DependenciesElite Code · 39:00 · 1K views
1203. Sort Items by Groups Respecting Dependencies (Leetcode Hard)Programming Live with Larry · 41:40 · 952 views
Leetcode 1203 Sort Items by Groups Respecting Dependencies LDD Li · 10:00 · 850 views
LeetCode 1203. Sort Items by Groups Respecting Dependencies Mastering Two-Tier Topological SortingvanAmsen · 11:33 · 810 views
Daily Leetcode #1237: Aug 20, 2023 - 1203. Sort Items by Groups Respecting DependenciesProgramming Live with Larry · 30:39 · 558 views
1203. Sort Items by Groups Respecting Dependencies | Leetcode Hard | C++ | DSA buddy| Easy SolutionDSA Buddy · 21:07 · 482 views
The problem
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:
- The items that belong to the same group are next to each other in the sorted list.
- There are some relations between these items where
beforeItems[i]is a list containing all the items that should come before thei-th item in the sorted array (to the left of thei-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.
Approach overview
Problem Overview: You are given n items where some belong to groups. Certain items depend on others and must appear later in the ordering. The goal is to return a valid ordering where item dependencies are respected and items from the same group appear together.
Approach 1: Topological Sort with Grouped Items (O(n + m) time, O(n + m) space)
This problem is essentially a dependency graph with an extra constraint: items should be ordered within groups, and groups themselves may depend on other groups. The clean solution builds two graphs. First, assign unique group IDs to items that initially have no group. Then construct a group-level dependency graph and an item-level dependency graph. Run topological sort on groups first, and within each group run another topological sort for its items. By separating group ordering from item ordering, you enforce both dependency rules without mixing constraints. This approach relies heavily on topological sort and general graph traversal techniques.
The key insight: item dependencies sometimes imply group dependencies. When item a depends on item b and they belong to different groups, create an edge between those groups. Once group order is resolved, items inside each group can be sorted independently using the same dependency edges.
Approach 2: Using Priority Queues for Topological Sorting (O((n + m) log n) time, O(n + m) space)
This variation still models the problem as two dependency graphs but uses a priority queue while performing topological sorting. Instead of processing nodes in arbitrary order, nodes with zero in-degree are pushed into a min-heap. This guarantees deterministic ordering when multiple valid nodes are available. The technique works with either breadth-first search style Kahn's algorithm or a modified queue-based traversal. The tradeoff is additional log n cost due to heap operations.
The heap-based approach is useful when deterministic output order is preferred or when platforms require lexicographically smaller valid sequences. Otherwise, the standard queue-based topological sort is faster and simpler.
Recommended for interviews: The grouped topological sort solution is what most interviewers expect. It demonstrates strong graph modeling skills: recognizing two dependency layers and solving them separately. Building both graphs and running Kahn's algorithm in O(n + m) shows you understand dependency resolution and scalable graph traversal.
Approach 1: Topological Sort with Grouped Items
This approach involves processing the items and grouping them based on dependency relationships. We'll use topological sorting for individual items within a group. The main steps are:
- Assign groups for items with no valid group using temporary group numbers.
- Build graphs for both items and groups based on dependency constraints.
- Perform topological sorting on both item graph and group graph.
from collections import defaultdict, deque
def topologicalSort(n, adjList, indegree):
order = []
zeroIndegree = deque([i for i in range(n) if indegree[i] == 0])
while zeroIndegree:
node = zeroIndegree.popleft()
order.append(node)
for neighbor in adjList[node]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
zeroIndegree.append(neighbor)
return order if len(order) == n else []
def sortItems(n, m, group, beforeItems):
# Step 1: Assign temporary groups to items with a group of -1.
new_group = m # Start numbering new groups from m
for i in range(n):
if group[i] == -1:
group[i] = new_group
new_group += 1
# Step 2: Item graph and indegree
item_graph = defaultdict(list)
item_indegree = [0] * n
# Step 3: Group graph and indegree
group_graph = defaultdict(list)
group_indegree = [0] * new_group
for i in range(n):
for before in beforeItems[i]:
item_graph[before].append(i)
item_indegree[i] += 1
if group[i] != group[before]:
if group[i] not in group_graph[group[before]]:
group_graph[group[before]].append(group[i])
group_indegree[group[i]] += 1
# Step 4: Topological sort groups and items
group_order = topologicalSort(new_group, group_graph, group_indegree)
if not group_order:
return []
item_order = topologicalSort(n, item_graph, item_indegree)
if not item_order:
return []
# Step 5: Map order back to the result by groups
items_by_group = defaultdict(list)
for item in item_order:
items_by_group[group[item]].append(item)
result = []
for grp in group_order:
result.extend(items_by_group[grp])
return resultimport java.util.*;
public class Solution {
public List<Integer> sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
// Assign unique groups to those without any starting group
int newGroupId = m;
for (int i = 0; i < n; i++) {
if (group[i] == -1) {
group[i] = newGroupId++;
}
}
// Initialize group and item graphs
List<Integer>[] itemGraph = new ArrayList[n];
List<Integer>[] groupGraph = new ArrayList[newGroupId];
int[] itemIndegree = new int[n];
int[] groupIndegree = new int[newGroupId];
for (int i = 0; i < n; i++) {
itemGraph[i] = new ArrayList<>();
}
for (int i = 0; i < newGroupId; i++) {
groupGraph[i] = new ArrayList<>();
}
// Build the graphs for items and groups
for (int i = 0; i < n; i++) {
for (int before : beforeItems.get(i)) {
itemGraph[before].add(i);
itemIndegree[i]++;
if (group[i] != group[before]) {
groupGraph[group[before]].add(group[i]);
groupIndegree[group[i]]++;
}
}
}
// Get sorted orders
List<Integer> groupOrder = topologicalSort(newGroupId, groupGraph, groupIndegree);
if (groupOrder.isEmpty()) return new ArrayList<>();
List<Integer> itemOrder = topologicalSort(n, itemGraph, itemIndegree);
if (itemOrder.isEmpty()) return new ArrayList<>();
// Sort items by group order
List<Integer>[] itemsWithGroupOrder = new ArrayList[newGroupId];
for (int i = 0; i < newGroupId; i++) {
itemsWithGroupOrder[i] = new ArrayList<>();
}
for (int item : itemOrder) {
itemsWithGroupOrder[group[item]].add(item);
}
List<Integer> result = new ArrayList<>();
for (int grp : groupOrder) {
result.addAll(itemsWithGroupOrder[grp]);
}
return result;
}
private List<Integer> topologicalSort(int n, List<Integer>[] graph, int[] indegree) {
Queue<Integer> zeroIndegree = new LinkedList<>();
List<Integer> order = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) {
zeroIndegree.offer(i);
}
}
while (!zeroIndegree.isEmpty()) {
int node = zeroIndegree.poll();
order.add(node);
for (int neighbor : graph[node]) {
indegree[neighbor]--;
if (indegree[neighbor] == 0) {
zeroIndegree.offer(neighbor);
}
}
}
return order.size() == n ? order : new ArrayList<>();
}
}This Python solution implements the approach of topological sorting using graph algorithms. We first assign temporary groups to items without any group. Then, two graphs (one for items and another for groups) are built based on given dependencies. We perform topological sorting on item and group graphs to determine the correct order. The results are gathered by reorganizing items according to their group order and finally returning the sorted list. If any of the topological sort fails to return a complete order, we return an empty list.
The time complexity of this solution is O(n + m) where n is the number of items and m is the number of groups, due to the topological sort operation. The space complexity is also O(n + m) for storing the adjacency list, indegree count, and result list.
Approach 2: Using Priority Queues for Topological Sorting
This approach incorporates priority queues (heaps) to perform topological sorting on items based on their dependencies, akin to a 'priority' based execution. It works by:
- Identifying independent items (i.e., items with no dependencies) and adding them to a min-heap.
- Processing items from the heap to ensure a strict order while satisfying the dependency conditions through the graph.
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
using namespace std;
vector<int> topologicalSortUsingPQ(int n, vector<vector<int>>& graph, vector<int>& indegree) {
priority_queue<int, vector<int>, greater<int>> zeroIndegree;
vector<int> order;
for (int i = 0; i < n; ++i) {
if (indegree[i] == 0) {
zeroIndegree.push(i);
}
}
while (!zeroIndegree.empty()) {
int node = zeroIndegree.top();
zeroIndegree.pop();
order.push_back(node);
for (int neighbor : graph[node]) {
if (--indegree[neighbor] == 0) {
zeroIndegree.push(neighbor);
}
}
}
return order.size() == n ? order : vector<int>();
}
vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
int newGroupId = m;
for (int i = 0; i < n; ++i) {
if (group[i] == -1) {
group[i] = newGroupId++;
}
}
vector<vector<int>> itemGraph(n), groupGraph(newGroupId);
vector<int> itemIndegree(n, 0), groupIndegree(newGroupId, 0);
for (int i = 0; i < n; ++i) {
for (int before : beforeItems[i]) {
itemGraph[before].push_back(i);
++itemIndegree[i];
if (group[i] != group[before]) {
groupGraph[group[before]].push_back(group[i]);
++groupIndegree[group[i]];
}
}
}
vector<int> groupOrder = topologicalSortUsingPQ(newGroupId, groupGraph, groupIndegree);
if (groupOrder.empty()) return {};
vector<int> itemOrder = topologicalSortUsingPQ(n, itemGraph, itemIndegree);
if (itemOrder.empty()) return {};
unordered_map<int, vector<int>> itemsGrouped;
for (int item : itemOrder) {
itemsGrouped[group[item]].push_back(item);
}
vector<int> result;
for (int grp : groupOrder) {
result.insert(result.end(), itemsGrouped[grp].begin(), itemsGrouped[grp].end());
}
return result;
}using System;
using System.Collections.Generic;
public class Solution {
public IList<int> SortItems(int n, int m, int[] group, IList<IList<int>> beforeItems) {
int newGroupId = m;
for (int i = 0; i < n; ++i) {
if (group[i] == -1) {
group[i] = newGroupId++;
}
}
List<int>[] itemGraph = new List<int>[n], groupGraph = new List<int>[newGroupId];
int[] itemIndegree = new int[n], groupIndegree = new int[newGroupId];
for (int i = 0; i < n; i++) {
itemGraph[i] = new List<int>();
}
for (int i = 0; i < newGroupId; i++) {
groupGraph[i] = new List<int>();
}
for (int i = 0; i < n; i++) {
foreach (var before in beforeItems[i]) {
itemGraph[before].Add(i);
itemIndegree[i]++;
if (group[i] != group[before]) {
groupGraph[group[before]].Add(group[i]);
groupIndegree[group[i]]++;
}
}
}
var groupOrder = pqTopologicalSort(newGroupId, groupGraph, groupIndegree);
if (groupOrder.Count == 0) return new List<int>();
var itemOrder = pqTopologicalSort(n, itemGraph, itemIndegree);
if (itemOrder.Count == 0) return new List<int>();
var itemsGrouped = new Dictionary<int, List<int>>();
foreach (var item in itemOrder) {
if (!itemsGrouped.ContainsKey(group[item])) {
itemsGrouped[group[item]] = new List<int>();
}
itemsGrouped[group[item]].Add(item);
}
var result = new List<int>();
foreach (var grp in groupOrder) {
if (itemsGrouped.ContainsKey(grp)) {
result.AddRange(itemsGrouped[grp]);
}
}
return result;
}
private List<int> pqTopologicalSort(int n, List<int>[] graph, int[] indegree) {
var zeroIndegree = new SortedSet<int>();
var order = new List<int>();
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) {
zeroIndegree.Add(i);
}
}
while (zeroIndegree.Count > 0) {
int node = zeroIndegree.Min;
zeroIndegree.Remove(node);
order.Add(node);
foreach (var neighbor in graph[node]) {
if (--indegree[neighbor] == 0) {
zeroIndegree.Add(neighbor);
}
}
}
return order.Count == n ? order : new List<int>();
}
}The C++ solution utilizes a min-priority queue for managing items with zero dependencies. This ensures items are processed in order of their natural indices, which mimics the behavior of prioritizing certain operations. After identifying initial independent items, it iterates through the graph, managing dependencies and constructing the correct order through traditional topological sorting mechanisms. The heap structure adds an extra layer of determinism by processing the lowest index 'ready' item first.
The time complexity remains O(n + m) due to the heap operations and graph traversal. Space complexity is O(n + m) to store necessary elements and dependency structures.
Approach 3: Topological Sorting
First, we traverse the array group. For each project, if it does not belong to any group, we create a new group for it with the ID m, and increment m. This ensures that all projects belong to some group. Then, we use an array groupItems to record the projects contained in each group. The array index is the group ID, and the array value is the list of projects in the group.
Next, we need to build the graph. For each project, we need to build two types of graphs: one for the projects and one for the groups. We traverse the array group. For the current project i, its group is group[i]. We traverse beforeItems[i], and for each project j in it, if group[i] = group[j], it means that i and j belong to the same group. We add an edge j \to i in the project graph. Otherwise, it means that i and j belong to different groups. We add an edge group[j] \to group[i] in the group graph, and update the corresponding in-degree array.
Next, we perform topological sorting on the group graph to get the sorted group list groupOrder. If the length of the sorted list is not equal to the total number of groups, it means that the sorting cannot be completed, so we return an empty list. Otherwise, we traverse groupOrder, and for each group gi, we perform topological sorting on the project list groupItems[gi] to get the sorted project list itemOrder. If the length of the sorted list is not equal to the total number of projects in the group, it means that the sorting cannot be completed, so we return an empty list. Otherwise, we add the projects in itemOrder to the answer array, and finally return this answer array.
The time complexity is O(n + m), and the space complexity is O(n + m). Here, n and m are the total number of projects and groups, respectively.
function sortItems(n: number, m: number, group: number[], beforeItems: number[][]): number[] {
let idx = m;
const groupItems: number[][] = new Array(n + m).fill(0).map(() => []);
const itemDegree: number[] = new Array(n).fill(0);
const gorupDegree: number[] = new Array(n + m).fill(0);
const itemGraph: number[][] = new Array(n).fill(0).map(() => []);
const groupGraph: number[][] = new Array(n + m).fill(0).map(() => []);
for (let i = 0; i < n; ++i) {
if (group[i] === -1) {
group[i] = idx++;
}
groupItems[group[i]].push(i);
}
for (let i = 0; i < n; ++i) {
for (const j of beforeItems[i]) {
if (group[i] === group[j]) {
++itemDegree[i];
itemGraph[j].push(i);
} else {
++gorupDegree[group[i]];
groupGraph[group[j]].push(group[i]);
}
}
}
let items = new Array(n + m).fill(0).map((_, i) => i);
const topoSort = (graph: number[][], degree: number[], items: number[]): number[] => {
const q: number[] = [];
for (const i of items) {
if (degree[i] === 0) {
q.push(i);
}
}
const ans: number[] = [];
while (q.length) {
const i = q.pop()!;
ans.push(i);
for (const j of graph[i]) {
if (--degree[j] === 0) {
q.push(j);
}
}
}
return ans.length === items.length ? ans : [];
};
const groupOrder = topoSort(groupGraph, gorupDegree, items);
if (groupOrder.length === 0) {
return [];
}
const ans: number[] = [];
for (const gi of groupOrder) {
items = groupItems[gi];
const itemOrder = topoSort(itemGraph, itemDegree, items);
if (itemOrder.length !== items.length) {
return [];
}
ans.push(...itemOrder);
}
return ans;
}func sortItems(n int, m int, group []int, beforeItems [][]int) []int {
idx := m
groupItems := make([][]int, n+m)
itemDegree := make([]int, n)
groupDegree := make([]int, n+m)
itemGraph := make([][]int, n)
groupGraph := make([][]int, n+m)
for i, g := range group {
if g == -1 {
group[i] = idx
idx++
}
groupItems[group[i]] = append(groupItems[group[i]], i)
}
for i, gi := range group {
for _, j := range beforeItems[i] {
gj := group[j]
if gi == gj {
itemDegree[i]++
itemGraph[j] = append(itemGraph[j], i)
} else {
groupDegree[gi]++
groupGraph[gj] = append(groupGraph[gj], gi)
}
}
}
items := make([]int, n+m)
for i := range items {
items[i] = i
}
topoSort := func(degree []int, graph [][]int, items []int) []int {
q := []int{}
for _, i := range items {
if degree[i] == 0 {
q = append(q, i)
}
}
ans := []int{}
for len(q) > 0 {
i := q[0]
q = q[1:]
ans = append(ans, i)
for _, j := range graph[i] {
degree[j]--
if degree[j] == 0 {
q = append(q, j)
}
}
}
return ans
}
groupOrder := topoSort(groupDegree, groupGraph, items)
if len(groupOrder) != len(items) {
return nil
}
ans := []int{}
for _, gi := range groupOrder {
items = groupItems[gi]
itemOrder := topoSort(itemDegree, itemGraph, items)
if len(items) != len(itemOrder) {
return nil
}
ans = append(ans, itemOrder...)
}
return ans
}Complexity comparison
| Approach | Complexity |
|---|---|
| Topological Sort with Grouped Items | The time complexity of this solution is O(n + m) where n is the number of items and m is the number of groups, due to the topological sort operation. The space complexity is also O(n + m) for storing the adjacency list, indegree count, and result list. |
| Using Priority Queues for Topological Sorting | The time complexity remains O(n + m) due to the heap operations and graph traversal. Space complexity is O(n + m) to store necessary elements and dependency structures. |
| Topological Sorting | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Topological Sort with Grouped Items | O(n + m) | O(n + m) | General optimal solution when resolving dependencies across grouped items |
| Priority Queue Based Topological Sort | O((n + m) log n) | O(n + m) | When deterministic or lexicographically ordered valid results are preferred |
Frequently asked questions
Is Sort Items by Groups Respecting Dependencies easy or hard?
The problem is classified as Hard because it combines multiple graph concepts. You must model two dependency layers, detect cycles using topological sort, and carefully merge results so group constraints and item dependencies are both satisfied.
Sort Items by Groups Respecting Dependencies Python/Java solution
Python and Java implementations typically build adjacency lists for both group and item graphs and use Kahn's algorithm for topological sorting. Items without groups are assigned new group IDs. After sorting groups and then items within each group, the results are concatenated into the final order.
How to solve Sort Items by Groups Respecting Dependencies in O(n + m)?
Construct two graphs: one representing dependencies between groups and another for dependencies between items. Assign unique group IDs to items without groups. Run topological sort on the group graph, then perform topological sort for items inside each group. Combining the results produces a valid ordering in linear time relative to nodes and edges.
What is the best approach for Sort Items by Groups Respecting Dependencies?
The most efficient approach is a two-level topological sort. Build a graph of groups and another graph of items. First perform topological sorting on the group graph, then sort items within each group using item-level dependencies. This ensures both group ordering and item dependencies are respected in O(n + m) time.
Is Sort Items by Groups Respecting Dependencies asked at Google/Amazon/Meta?
Graph ordering and dependency resolution problems frequently appear in interviews at companies like Google, Amazon, and Meta. Variations of this problem test understanding of topological sorting, graph modeling, and handling layered constraints such as grouping and dependency chains.
What data structure is used in Sort Items by Groups Respecting Dependencies?
The core data structures are adjacency lists for representing graphs, arrays for tracking in-degrees, and queues for Kahn's topological sorting algorithm. Some implementations replace the queue with a priority queue to enforce deterministic ordering among nodes with zero in-degree.
What is the time complexity of Sort Items by Groups Respecting Dependencies?
The optimal solution runs in O(n + m) time where n is the number of items and m is the number of dependency edges. Each node and edge is processed once while building the graph and performing Kahn's topological sort. Space complexity is also O(n + m) for adjacency lists and in-degree arrays.
