Skip to main content

Sort Items by Groups Respecting Dependencies


Sort Items by Groups Respecting Dependencies: 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 the i-th item in the sorted array (to the left of the i-th item).

Return any solution if there is more than one solution and return an empty list if there is no solution.

Example 1:

image

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 * 10^4
  • group.length == beforeItems.length == n
  • -1 <= group[i] <= m - 1
  • 0 <= beforeItems[i].length <= n - 1
  • 0 <= beforeItems[i][j] <= n - 1
  • i != beforeItems[i][j]
  • beforeItems[i] does not contain duplicates elements.

Try this Problem on your own or check similar problems:

  1. Course Schedule
  2. Course Schedule II
Solution:
class Solution {
Map <Integer, List<Integer>> groupDependencyGraph = new HashMap<>();
Map <Integer, List<Integer>> itemDependencyGraph = new HashMap<>();

int[] groupDependencyDegree;
int[] itemDependencyDegree;

private void buildGroupsGraph(int[] group, List<List<Integer>> beforeItems, int n) {
for (int i=0; i < group.length; ++i) {
int to = group[i];
List <Integer> items = beforeItems.get(i);
for (int item : items) {
int from = group[item];
if(from != to) {
if(groupDependencyGraph.get(from) == null){
groupDependencyGraph.put(from, new ArrayList<>());
}
groupDependencyGraph.get(from).add(to);
++groupDependencyDegree[to];
}
}
}
}

private void buildItemsGraph(List<List<Integer>> beforeItems, int n) {
for (int i=0; i < n; ++i) {
List<Integer> items = beforeItems.get(i);
for (Integer item : items) {
if(itemDependencyGraph.get(item) == null){
itemDependencyGraph.put(item, new ArrayList<>());
}
itemDependencyGraph.get(item).add(i);
++itemDependencyDegree[i];
}
}
}

private List<Integer> topologicalSort(Map<Integer, List<Integer>> graph, int[] degree, int n) {
List<Integer> result = new ArrayList<Integer>();
Queue<Integer> q = new LinkedList();

for (int key : graph.keySet()) {
if(degree[key] == 0) {
q.add(key);
}
}

while(!q.isEmpty()) {
int node = q.poll();
result.add(node);
for (int neighbor : graph.get(node)) {
if(--degree[neighbor] == 0) {
q.add(neighbor);
}
}
}
return result.size() == n ? result : new ArrayList();
}

public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
for (int i=0; i < group.length; ++i) {
if(group[i] == -1) group[i] = m++;
}

IntStream.range(0, m)
.forEach(i -> groupDependencyGraph.put(i, new ArrayList<>()));

IntStream.range(0, n)
.forEach(i -> itemDependencyGraph.put(i, new ArrayList<>()));

groupDependencyDegree = new int[m];
itemDependencyDegree = new int[n];

buildGroupsGraph(group, beforeItems, n);
buildItemsGraph(beforeItems, n);


List<Integer> sortedGroups = topologicalSort(groupDependencyGraph, groupDependencyDegree, m);
List<Integer> sortedItems = topologicalSort(itemDependencyGraph, itemDependencyDegree, n);

if(sortedGroups.size() == 0 || sortedItems.size() == 0) return new int[0];

Map<Integer, List<Integer>> groupsToItemsMapping = new HashMap();

for (Integer item : sortedItems) {
if(groupsToItemsMapping.get(group[item]) == null){
groupsToItemsMapping.put(group[item], new ArrayList<>());
}
groupsToItemsMapping.get(group[item]).add(item);
}

int[] result = new int[n];
int writer = 0;
for (Integer g : sortedGroups) {
List<Integer> items = groupsToItemsMapping.getOrDefault(g, new ArrayList());
for (Integer item : items) {
result[writer++] = item;
}
}

return result;
}
}

Time/Space Complexity:
  • Time Complexity: O((E1+V1) + (E2+V2)) where E1 and V1 are edges and vertices of groups dependency graph, while E2 and V2 are edges and vertices of items dependency graph
  • Space Complexity: O((E1+V1) + (E2+V2))

Explanation:

If we can create a dependency graph between groups and items we can utilize the two-level topological sort, by sorting first the groups and then the items. If we can get a valid topological sort of both that means, there is no cycle in either of graphs. We build the graphs (adjacency list approach) using the beforeItems information and we also track degree for each node in group and items dependency graphs which are later used in topological sort which we reuse from the solution Course Schedule II. If there is a valid topological order, we build the map that does the mapping from group to items and we build the end result by traversing the sorted order of groups each time taking all the items (in sorted topological order) from each of the groups. The time and space complexity reflect both building the graphs and their respective topological sort.