Back to all solutions

#1203 - Sort Items by Groups Respecting Dependencies

Problem Description

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.

Solution

/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function(n, m, group, beforeItems) {
  const groupAdj = Array.from({ length: n + m }, () => []);
  const itemAdj = Array.from({ length: n }, () => []);
  const groupInDegree = new Array(n + m).fill(0);
  const itemInDegree = new Array(n).fill(0);
  let nextGroupId = m;

  assignGroupIds();
  buildGraphs();

  const groupOrder = topologicalSort(groupAdj, groupInDegree, nextGroupId);
  if (!groupOrder.length) return [];

  const itemOrder = topologicalSort(itemAdj, itemInDegree, n);
  if (!itemOrder.length) return [];

  const groupToItems = new Map();
  itemOrder.forEach(item => {
    if (!groupToItems.has(group[item])) {
      groupToItems.set(group[item], []);
    }
    groupToItems.get(group[item]).push(item);
  });

  const result = [];
  groupOrder.forEach(groupId => {
    if (groupToItems.has(groupId)) {
      result.push(...groupToItems.get(groupId));
    }
  });

  return result;

  function assignGroupIds() {
    for (let i = 0; i < n; i++) {
      if (group[i] === -1) {
        group[i] = nextGroupId++;
      }
    }
  }

  function buildGraphs() {
    for (let i = 0; i < n; i++) {
      for (const before of beforeItems[i]) {
        if (group[before] !== group[i]) {
          groupAdj[group[before]].push(group[i]);
          groupInDegree[group[i]]++;
        } else {
          itemAdj[before].push(i);
          itemInDegree[i]++;
        }
      }
    }
  }

  function topologicalSort(adjList, inDegree, size) {
    const queue = [];
    const order = [];

    for (let i = 0; i < size; i++) {
      if (inDegree[i] === 0) queue.push(i);
    }

    while (queue.length) {
      const current = queue.shift();
      order.push(current);
      for (const next of adjList[current]) {
        inDegree[next]--;
        if (inDegree[next] === 0) queue.push(next);
      }
    }

    return order.length === size ? order : [];
  }
};