leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 31 】2021-10-10 - 1203. 项目管理 #48

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

1203. 项目管理

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies/

前置知识

公司共有 n 个项目和  m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。

group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。

请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:

同一小组的项目,排序后在列表中彼此相邻。 项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。 如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。

 

示例 1:


![](https://tva1.sinaimg.cn/large/008eGmZEly1gmkv6dy054j305b051mx9.jpg)

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]] 输出:[6,3,4,1,5,2,0,7] 示例 2:

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]] 输出:[] 解释:与示例 1 大致相同,但是在排序后的列表中,4 必须放在 6 的前面。  

提示:

1 <= m <= n <= 3 * 104 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] 不含重复元素

for123s commented 2 years ago

思路

关键点

代码

C++ Code:


class Solution {
public:
    vector<int> topologicalSort(vector<vector<int>> &Adj, vector<int> &Indegree, int n){
        vector<int> res;
        queue<int> q;
        for(int i = 0;i<n;i++){
            if(Indegree[i]==0){
                q.push(i);
            }
        }
        while(!q.empty()){
            int front = q.front();
            q.pop();
            res.push_back(front);
            for(int successor: Adj[front]){
                Indegree[successor]--;
                if(Indegree[successor]==0){
                    q.push(successor);
                }
            }
        }
        if(res.size()==n){return res;}
        return vector<int>();
    }
    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        for(int i=0;i<group.size();i++){
            if(group[i] == -1){
                group[i] = m;
                m++;
            }
        }

        vector<vector<int>> groupAdj(m, vector<int>());
        vector<vector<int>> itemAdj(n, vector<int>());

        vector<int> groupsIndegree(m, 0);
        vector<int> itemIndegree(n, 0);

        int len = group.size();
        for(int i=0;i<len;i++){
            int currentGroup = group[i];
            for(int beforeItem: beforeItems[i]){
                int beforeGroup = group[beforeItem];
                if(beforeGroup!=currentGroup){
                    groupAdj[beforeGroup].push_back(currentGroup);
                    groupsIndegree[currentGroup]++;
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int item: beforeItems[i]){
                itemAdj[item].push_back(i);
                itemIndegree[i]++;
            }
        }

        vector<int> groupList = topologicalSort(groupAdj, groupsIndegree, m);
        if(groupList.size()==0){
            return vector<int> ();
        }
        vector<int> itemList = topologicalSort(itemAdj, itemIndegree, n);
        if(itemList.size()==0){
            return vector<int> ();
        }

        map<int, vector<int>> group2Items;
        for(int item: itemList){
            group2Items[group[item]].push_back(item);
        }

        vector<int> res;
        for(int groupId: groupList){
            vector<int> items = group2Items[groupId];
            for(int item: items){
                res.push_back(item);
            }
        }
        return res;
    } 
};
guangsizhongbin commented 2 years ago
class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
                // 1. 数据预先处理, 给没有分组的项目排到m组以后
       for (int i = 0; i < group.length; i++) {
            if(group[i] == -1){
                group[i] = m;
                m++;
            }
       }

       // 2. 实现组和项目的有向图, 实现邻接表
       List<Integer>[] groupAdj = new ArrayList[m];
       List<Integer>[] itemAdj = new ArrayList[n];
       for (int i = 0; i < m; i++) {
          groupAdj[i] = new ArrayList<>();
       }
       for (int i = 0; i < n; i++) {
          itemAdj[i]  = new ArrayList<>();
       }

       // 3. 建图和统计入度数组
       int[] groupIndegree = new int[m];
       int[] itemsIndegree = new int[n];

       // 组
       int len = group.length;
       for (int i = 0; i < len; i++) {
           // 当前组的编号
           int currentGroup = group[i];
           for (int beforeItem  : beforeItems.get(i)) {
               int beforeGroup = group[beforeItem];
               if(beforeGroup != currentGroup){
                   groupAdj[beforeGroup].add(currentGroup);
                   groupIndegree[currentGroup]++;
               }
           }

       }

       // 项目
       for (int i = 0; i < n; i++) {
           for(Integer item : beforeItems.get(i)){
                // 项目邻接表
                itemAdj[item].add(i);
                // items的入度+1
                itemsIndegree[i]++;
           }
       }

       // 4. 得到组和项目的拓扑排序
       List<Integer> groupList = topologicalSort(groupAdj, groupIndegree,m);
       if(groupList.size() == 0){
           return new int[0];
       }
       List<Integer> itemList = topologicalSort(itemAdj, itemsIndegree, n);
       if(itemList.size() == 0){
           return new int[0];
       }

       // 5. 根据项目的拓扑排序结果,项目到组的多对一关系,建立组到项目的一对多关系
       // key: 组, value: 在同一组的项目项目
       Map<Integer, List<Integer>> groups2Items = new HashMap<>();
       for (Integer item: itemList){
           groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
       }

       // 6. 把组的拓扑排序结果替换成项目的拓扑排序结果
       List<Integer> res = new ArrayList<>();
       for (Integer groupId : groupList) {
           List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
           res.addAll(items);
       }

       // 转换成数组并返回
      return res.stream().mapToInt(Integer::valueOf).toArray();

    }

     private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree, int n) {
        ArrayList<Integer> res = new ArrayList<>();
        LinkedList<Integer> queue = new LinkedList<>();

        // 把入度为零项先加入队列
        for (int i = 0; i < n ; i++) {
            if(inDegree[i] == 0){
                queue.offer(i);
            }
        }

        // 进行广度优先遍历
        while (!queue.isEmpty()){
            Integer front = queue.poll();
            res.add(front);
            for(int successor : adj[front]){
                // 后继结点-1
                inDegree[successor]--;

                // 当入度为0时, 加入后继结点
                if(inDegree[successor] == 0){
                    queue.offer(successor);
                }
            }
        }

        // 不存在环
        if(res.size() == n){
            return res;
        }

        // 存在环
        return new ArrayList<>();
    }

}
mokrs commented 2 years ago
//拓扑排序
vector<int> topSort(vector<vector<int>>& graph, vector<int>& inDegree) {
    vector<int> res;
    queue<int> q;

    //入度为0的结点入q
    for (int i = 0; i < inDegree.size(); ++i) {
        if (inDegree[i] == 0) {
            q.emplace(i);
        }
    }       

    while (!q.empty()) {
        int prerequisite = q.front();
        q.pop();
        res.emplace_back(prerequisite);

        //相关结点入度-1
        for (int n : graph[prerequisite]) {
            //入度为0则加入队列
            if (--inDegree[n] == 0) {
                q.emplace(n);
            }
        }
    }
    //结果数组大小与结点数量一致表示无环
    return res.size() == inDegree.size() ? res : vector<int>{};
}

vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
    // 组号为-1的项目彼此应属于不同组,且组号应与实际存在的组不同
    for (int i = 0; i < group.size(); ++i) {
        if (group[i] == -1) {
            group[i] = m++;             
        }
    }

    vector<vector<int>> groupAdj(m);
    vector<vector<int>> itemAdj(n);

    vector<int> groupsIndegree(m);
    vector<int> itemsIndegree(n);

    //根据项目的约束间接找到组之间的约束,并建立组的邻接矩阵和入度统计数组
    for (int i = 0; i < group.size(); ++i) {
        int currentGroup = group[i];
        for (int beforeItem : beforeItems[i]) {
            //前置项目所在组
            int beforeGroup = group[beforeItem];
            //前置项目所在组与本项目所在组不同,则加入groupAdj中
            if (beforeGroup != currentGroup) {
                groupAdj[beforeGroup].emplace_back(currentGroup);
                ++groupsIndegree[currentGroup];
            }
        }
    }

    //根据项目约束,创建项目的邻接矩阵和入度统计数组
    for (int i = 0; i < n; ++i) {
        for (int item : beforeItems[i]) {
            itemAdj[item].emplace_back(i);
            ++itemsIndegree[i];
        }
    }

    //分别对group和item进行拓扑排序,检查是否存在环
    vector<int> groupsList = topSort(groupAdj, groupsIndegree);
    if (groupsList.size() == 0) {
        return{};
    }
    vector<int> itemsList = topSort(itemAdj, itemsIndegree);
    if (itemsList.size() == 0) {
        return{};
    }

    // key:组号,value:该组的项目
    unordered_map<int, vector<int>> groups2Items;
    for (int item : itemsList) {
        groups2Items[group[item]].emplace_back(item);
    }

    // 根据组的拓扑排序,通过查找组-项目hash表,按拓扑排序结果添加项目
    vector<int> res;
    for (int groupId : groupsList) {
        vector<int> items = groups2Items[groupId];
        res.insert(res.end(), items.begin(),items.end());
    }

    return res;
}

代码是看官方题解写的,当前只能说是理解了为什么这样写,但掌握还不敢说

BlueRui commented 2 years ago

Problem 1203. Sort Items by Groups Respecting Dependencies

Algorithm

Complexity

Code

Credit: Solution on YouTube

Language: Java

public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
    // Assign a group number to items with no group. Assign them "m"
    for (int i = 0; i < group.length; i++) {
        if (group[i] == -1) {
            group[i] = m;
            m++;
        }
    }

    // Instantiate adjacency lists fro groups and items
    List<Integer>[] groupAdj = new ArrayList[m];
    List<Integer>[] itemAdj = new ArrayList[n];
    for (int i = 0; i < m; i++) {
        groupAdj[i] = new ArrayList<>();
    }
    for (int i = 0; i < n; i++) {
        itemAdj[i] = new ArrayList<>();
    }

    // Construct graph
    int[] groupsIndegree = new int[m];
    int[] itemsIndegree = new int[n];

    int len = group.length;
    for (int i = 0; i < len; i++) {
        int currentGroup = group[i];
        for (int beforeItem : beforeItems.get(i)) {
            int beforeGroup = group[beforeItem];
            if (beforeGroup != currentGroup) {
                groupAdj[beforeGroup].add(currentGroup);
                groupsIndegree[currentGroup]++;
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (Integer item : beforeItems.get(i)) {
            itemAdj[item].add(i);
            itemsIndegree[i]++;
        }
    }

    // Topological sort on groups and items
    List<Integer> groupsList = topologicalSort(groupAdj, groupsIndegree);
    if (groupsList.size() == 0) {
        return new int[0];
    }
    List<Integer> itemsList = topologicalSort(itemAdj, itemsIndegree);
    if (itemsList.size() == 0) {
        return new int[0];
    }

    // Create a map on group (key) and items (value) based on previous results
    Map<Integer, List<Integer>> groups2Items = new HashMap<>();
    for (Integer item : itemsList) {
        groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
    }

    // Insert item sort result in group sort result to obtain final result
    List<Integer> res = new ArrayList<>();
    for (Integer groupId : groupsList) {
        List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
        res.addAll(items);
    }
    return res.stream().mapToInt(Integer::valueOf).toArray();
}

private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree) {
    List<Integer> res = new ArrayList<>();
    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < adj.length; i++) {
        if (inDegree[i] == 0) {
            queue.offer(i);
        }
    }

    while (!queue.isEmpty()) {
        Integer front = queue.poll();
        res.add(front);
        for (int successor : adj[front]) {
            inDegree[successor]--;
            if (inDegree[successor] == 0) {
                queue.offer(successor);
            }
        }
    }

    if (res.size() == adj.length) {
        return res;
    }
    return new ArrayList<>();
}
Kashinggo commented 2 years ago
class Solution {
  Map <Integer, List<Integer>> groupGraph;
  Map <Integer, List<Integer>> itemGraph;

  int[] groupsIndegree;
  int[] itemsIndegree;

  private void buildGraphOfGroups(int[] group, List<List<Integer>> beforeItems, int n) {
    for (int i=0;i<group.length;i++) {
      int toGroup = group[i];
      List <Integer> fromItems = beforeItems.get(i);
      for (int fromItem : fromItems) {
        int fromGroup = group[fromItem];
        if(fromGroup != toGroup) {
          groupGraph.computeIfAbsent(fromGroup, x->new ArrayList()).add(toGroup); 
          groupsIndegree[toGroup]++;
        }        
      }
    }
  }

  private void buildGraphOfItems(List<List<Integer>> beforeItems, int n) {
    for (int i=0;i<n;i++) {
      List<Integer> items = beforeItems.get(i);
      for (Integer item : items) {
        itemGraph.computeIfAbsent(item, x->new ArrayList()).add(i);
        itemsIndegree[i]++;
      }
    }
  }

  private List<Integer> topologicalSortUtil(Map <Integer, List<Integer>> graph, int[] indegree, int n) {
    List <Integer> list = new ArrayList<Integer>();
    Queue <Integer> queue = new LinkedList();
    for (int key : graph.keySet()) {
      if(indegree[key] == 0) {
        queue.add(key);
      }
    }

    while(!queue.isEmpty()) {
      int node = queue.poll();
      n--;
      list.add(node);
      for (int neighbor : graph.get(node)) {
        indegree[neighbor] --;        
        if(indegree[neighbor] == 0) {
          queue.offer(neighbor);          
        }
      }
    }    
    return n == 0 ? list : new ArrayList();
  }  

  public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
    groupGraph = new HashMap();
    itemGraph = new HashMap();

    for (int i=0;i<group.length;i++) {
      if(group[i] == -1) group[i] = m++;
    }  

    for (int i=0;i<m;i++) {
      groupGraph.put(i, new ArrayList());      
    }

    for (int i=0;i<n;i++) {
      itemGraph.put(i, new ArrayList());      
    }

    groupsIndegree = new int[m];
    itemsIndegree = new int[n];    

    buildGraphOfGroups(group, beforeItems, n);

    buildGraphOfItems(beforeItems, n);

    List<Integer> groupsList = topologicalSortUtil(groupGraph, groupsIndegree, m);
    List<Integer> itemsList = topologicalSortUtil(itemGraph, itemsIndegree, n);

    if(groupsList.size() == 0 || itemsList.size() == 0) return new int[0];    

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

    for (Integer item : itemsList) {
      groupsToItems.computeIfAbsent(group[item], x->new ArrayList()).add(item);
    }

    int[] ans = new int[n];
    int idx = 0;
    for (Integer grp : groupsList) {
      List <Integer> items = groupsToItems.getOrDefault(grp, new ArrayList());
      for (Integer item : items) {
        ans[idx++] = item;  
      }      
    }

    return ans;
  }
}

图+hard 真的好难

xulli1996 commented 2 years ago

思路

下次一定

代码


public class Solution {

    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;
                m++;
            }
        }

        List<Integer>[] groupAdj = new ArrayList[m];
        List<Integer>[] itemAdj = new ArrayList[n];
        for (int i = 0; i < m; i++) {
            groupAdj[i] = new ArrayList<>();
        }
        for (int i = 0; i < n; i++) {
            itemAdj[i] = new ArrayList<>();
        }

        int[] groupsIndegree = new int[m];
        int[] itemsIndegree = new int[n];

        int len = group.length;
        for (int i = 0; i < len; i++) {
            int currentGroup = group[i];
            for (int beforeItem : beforeItems.get(i)) {
                int beforeGroup = group[beforeItem];
                if (beforeGroup != currentGroup) {
                    groupAdj[beforeGroup].add(currentGroup);
                    groupsIndegree[currentGroup]++;
                }
            }
        }

        for (int i = 0; i < n; i++) {
            for (Integer item : beforeItems.get(i)) {
                itemAdj[item].add(i);
                itemsIndegree[i]++;
            }
        }

        List<Integer> groupsList = topologicalSort(groupAdj, groupsIndegree, m);
        if (groupsList.size() == 0) {
            return new int[0];
        }
        List<Integer> itemsList = topologicalSort(itemAdj, itemsIndegree, n);
        if (itemsList.size() == 0) {
            return new int[0];
        }

        Map<Integer, List<Integer>> groups2Items = new HashMap<>();
        for (Integer item : itemsList) {
            groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
        }

        List<Integer> res = new ArrayList<>();
        for (Integer groupId : groupsList) {
            List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
            res.addAll(items);
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }

    private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree, int n) {
        List<Integer> res = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }

        while (!queue.isEmpty()) {
            Integer front = queue.poll();
            res.add(front);
            for (int successor : adj[front]) {
                inDegree[successor]--;
                if (inDegree[successor] == 0) {
                    queue.offer(successor);
                }
            }
        }

        if (res.size() == n) {
            return res;
        }
        return new ArrayList<>();
    }
}
heyqz commented 2 years ago

先打个卡

class Solution:
    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        def get_top_order(graph, indegree):
            top_order = []
            stack = [node for node in range(len(graph)) if indegree[node] == 0]
            while stack:
                v = stack.pop()
                top_order.append(v)
                for u in graph[v]:
                    indegree[u] -= 1
                    if indegree[u] == 0:
                        stack.append(u)
            return top_order if len(top_order) == len(graph) else []

        for u in range(len(group)):
            if group[u] == -1:
                group[u] = m
                m+=1

        graph_items = [[] for _ in range(n)]
        indegree_items = [0] * n
        graph_groups = [[] for _ in range(m)]
        indegree_groups = [0] * m        
        for u in range(n):
            for v in beforeItems[u]:                
                graph_items[v].append(u)
                indegree_items[u] += 1
                if group[u]!=group[v]:
                    graph_groups[group[v]].append(group[u])
                    indegree_groups[group[u]] += 1                    

        item_order = get_top_order(graph_items, indegree_items)
        group_order = get_top_order(graph_groups, indegree_groups)
        if not item_order or not group_order: return []

        order_within_group = collections.defaultdict(list)
        for v in item_order:
            order_within_group[group[v]].append(v)

        res = []
        for group in group_order:
            res += order_within_group[group]
        return res
joriscai commented 2 years ago

思路

代码

javascript

/*
 * @lc app=leetcode.cn id=1203 lang=javascript
 *
 * [1203] 项目管理
 */

// @lc code=start
/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function(n, m, group, beforeItems) {
  const groupItem = new Array(n + m).fill(0).map(() => [])

  // 组间和组内依赖图
  const groupGraph = new Array(n + m).fill(0).map(() => [])
  const itemGraph = new Array(n).fill(0).map(() => [])

  // 组间和组内入度数组
  const groupDegree = new Array(n + m).fill(0)
  const itemDegree = new Array(n).fill(0)

  const id = new Array(n + m).fill(0).map((v, index) => index)

  let leftId = m
  // 给未分配的 item 分配一个 groupId
  for (let i = 0; i < n; ++i) {
    if (group[i] === -1) {
      group[i] = leftId
      leftId += 1
    }
    groupItem[group[i]].push(i)
  }
  // 依赖关系建图
  for (let i = 0; i < n; ++i) {
    const curGroupId = group[i]
    for (const item of beforeItems[i]) {
      const beforeGroupId = group[item]
      if (beforeGroupId === curGroupId) {
        itemDegree[i] += 1
        itemGraph[item].push(i)
      } else {
        groupDegree[curGroupId] += 1
        groupGraph[beforeGroupId].push(curGroupId)
      }
    }
  }

  // 组间拓扑关系排序
  const groupTopSort = topSort(groupDegree, groupGraph, id)
  if (groupTopSort.length == 0) {
    return []
  }
  const ans = []
  // 组内拓扑关系排序
  for (const curGroupId of groupTopSort) {
    const size = groupItem[curGroupId].length
    if (size == 0) {
      continue
    }
    const res = topSort(itemDegree, itemGraph, groupItem[curGroupId])
    if (res.length === 0) {
      return []
    }
    for (const item of res) {
      ans.push(item)
    }
  }
  return ans
};

const topSort = (deg, graph, items) => {
  const Q = []
  for (const item of items) {
    if (deg[item] === 0) {
      Q.push(item)
    }
  }
  const res = []
  while (Q.length) {
    const u = Q.shift()
    res.push(u)
    for (let i = 0; i < graph[u].length; ++i) {
      const v = graph[u][i]
      if (--deg[v] === 0) {
        Q.push(v)
      }
    }
  }
  return res.length == items.length ? res : []
}
// @lc code=end

复杂度分析

chakochako commented 2 years ago
    def tp_sort(self, items, indegree, neighbors):
        q = collections.deque([])
        ans = []
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            ans.append(cur)

            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)

        return ans

    def sortItems(self, n: int, m: int, group: List[int], pres: List[List[int]]) -> List[int]:
        max_group_id = m
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        project_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        project_neighbors = collections.defaultdict(list)
        group_neighbors = collections.defaultdict(list)
        group_projects = collections.defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)

            for pre in pres[project]:
                if group[pre] != group[project]:
                    # 小组关系图
                    group_indegree[group[project]] += 1
                    group_neighbors[group[pre]].append(group[project])
                else:
                    # 项目关系图
                    project_indegree[project] += 1
                    project_neighbors[pre].append(project)

        ans = []

        group_queue = self.tp_sort([i for i in range(max_group_id)], group_indegree, group_neighbors)

        if len(group_queue) != max_group_id:
            return []

        for group_id in group_queue:

            project_queue = self.tp_sort(group_projects[group_id], project_indegree, project_neighbors)

            if len(project_queue) != len(group_projects[group_id]):
                return []
            ans += project_queue

        return ans
Yufanzh commented 2 years ago

Intuition

From the project, due to the existence of beforeItems list, this is a directed graph. To get its order, we will need topological sort to accomplish it. In the problems, items are split into group 0-- m-1 + ungrouped, so those ungrouped items should be renumbered first inorder to do correct tpSort group 0 ~ m-1 also has to be sorted So the approach is to tp sort group first and then use the order to tpsort items.

Algorithm in Python3

class Solution:
    # tp sort for groups, then use the order to sort items
    # tp sort -- BFS can work, use a helper function to do tpsort
    def tpSort(self, items, indegree, neighbors):
        q = collections.deque([])
        ans = []
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            ans.append(cur)

            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)
        return ans

    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        """step 1. re-numbering those items that are not in the group
            since we have m group, the items not in group can be renumbering starting from m
        """
        max_group_id = m
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        # create dictionary to store neighbors and indegree for tp sort purpose
        project_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        project_neighbors = collections.defaultdict(list)
        group_neighbors = collections.defaultdict(list)
        group_projects = collections.defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)

            for pre in beforeItems[project]:
                # if items in two different groups
                # we will create the group dependencies 
                if group[pre] != group[project]:
                    group_indegree[group[project]] += 1
                    group_neighbors[group[pre]].append(group[project])
                else:
                    # if items in the same group
                    # we create item dependencies
                    project_indegree[project] += 1
                    project_neighbors[pre].append(project)

        ans = []

        # tp sort group + ungrouped items and then tp sort items
        group_queue = self.tpSort([i for i in range(max_group_id)], group_indegree, group_neighbors)

        if len(group_queue) != max_group_id:
            return []

        for group_id in group_queue:#!!
            project_queue = self.tpSort(group_projects[group_id], project_indegree, project_neighbors)
            if len(project_queue) != len(group_projects[group_id]):
                return []
            ans += project_queue

        return ans

Complexity analysis:

taojin1992 commented 2 years ago

Plan:

one indegree array/map for project
one indegree array/map for group

m groups: 0-(m-1)
n items: 0-(n-1)

when a group == -1, start from m, increment its group id

5
3
[0,0,2,1,0]
[[3],[],[],[],[1,3,2]]

[3,2,0,1,4]

5
3
[0,0,2,1,0]
[[3],[],[],[],[1,3,2]]
-> [3,2,0,1,4]

do not double count duplicate edges for in-degree

Evaluate:

Time: O(2*n) + O(maxGroupID) + O(n + number of pres) + O(maxGroupID) + O(number of groups + number of group edges) + O(number of item + number of item edge)

Space: 
output: O(n)
Map<Integer, Set<Integer>> groupProjectsMapping: O(n)
Map<Integer, Set<Integer>> projectIndegree: O(n + edges between item)
Map<Integer, Set<Integer>> groupIndegree: O(group num + edge between group)
Map<Integer, Set<Integer>> groupsGraph: O(group num + edge between group), edge between group bounded by number of pres
Map<Integer, Set<Integer>> projectsGraph: O(n + edges between item)
Set<Integer> groupNodes: O(maxGroupID)
Set<Integer> targetNodes: O(n)

Code:

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        int maxGroupID = m;
        // reassign the group id given -1, -1 means an item stands on its own
        for (int i = 0; i < n; i++) {
            if (group[i] == -1) {
                group[i] = maxGroupID;
                maxGroupID++;
            }
        }

        // assign each project to each group
        // group -> set of projects
        //  & populate: 
        // one indegree array for project
        // one indegree array for group
        // & build 2 graphs: projects (pre) / groups -> neighbors 小组关系图 + 项目关系图

        Map<Integer, Set<Integer>> groupProjectsMapping = new HashMap<>();

        // project - indegree mapping
        // do not double count duplicate edges for in-degree, that's why i don't use int[] here
        Map<Integer, Set<Integer>> projectIndegree = new HashMap<>(); // max n size
        for (int i = 0; i < n; i++) {
            projectIndegree.put(i, new HashSet<>());
        }
        Map<Integer, Set<Integer>> groupIndegree = new HashMap<>(); // maxGroupID size
        for (int i = 0; i < maxGroupID; i++) {
            groupIndegree.put(i, new HashSet<>());
        }

        Map<Integer, Set<Integer>> groupsGraph = new HashMap<>();
        Map<Integer, Set<Integer>> projectsGraph = new HashMap<>();

        for (int p = 0; p < n; p++) {
            int groupID = group[p];
            if (!groupProjectsMapping.containsKey(groupID)) {
                groupProjectsMapping.put(groupID, new HashSet<Integer>());
            }
            groupProjectsMapping.get(groupID).add(p);

            // we can check the pre for each project
            List<Integer> presOfP = beforeItems.get(p);
            for (int preOfP : presOfP) {
                int groupOfPre = group[preOfP];
                int groupOfP = group[p];

                // pre and p are not in the same group
                if (groupOfPre != groupOfP) { // self-loop excluded
                    // pre's group goes before p's group, pre's group -> p's group

                    groupIndegree.get(groupOfP).add(groupOfPre);
                    if (!groupsGraph.containsKey(groupOfPre)) {
                        groupsGraph.put(groupOfPre, new HashSet<Integer>());
                    }
                    groupsGraph.get(groupOfPre).add(groupOfP);
                } 
                // pre and p are in the same group
                // 因为属于不同group的project,indegree不影响,所以如果pre和project在不同group,不需要更新project indegree(我之前问的else地方想明白了,# 项目关系图处)。
                else { //最后按group,分别sort,然后append结果。所以project之间关系仅针对同一group更新即可
                    projectIndegree.get(p).add(preOfP);
                    if (!projectsGraph.containsKey(preOfP)) {
                        projectsGraph.put(preOfP, new HashSet<Integer>());
                    }
                    projectsGraph.get(preOfP).add(p);// inside projectsGraph, of course, neighbors are from the same group
                }
            }
        }

        //System.out.println(groupIndegree);

        // after extracting all the info, call topological sorting on group
        Set<Integer> groupNodes = new HashSet<>();
        for (int i = 0; i < maxGroupID; i++) {
            groupNodes.add(i);
        }

        List<Integer> sortGroupRes = topologicalSort(groupNodes, groupIndegree, groupsGraph);

       // System.out.println(sortGroupRes);

        if (sortGroupRes.size() != maxGroupID) {
            return new int[0];
        }

        int[] sortRes = new int[n];
        int index = 0;

        // topological sort every single group
        for (int groupID : sortGroupRes) {
            Set<Integer> targetNodes = groupProjectsMapping.get(groupID);
            if (targetNodes == null) continue; //A group can have no item belonging to it.
            List<Integer> sortProjects = topologicalSort(targetNodes, projectIndegree, projectsGraph);

            if (sortProjects.size() != targetNodes.size()) {
                return new int[0];
            }
            for (int each : sortProjects) {
                sortRes[index++] = each;
            }
        }
        return sortRes;
    }

    // topological sort
    private List<Integer> topologicalSort(Set<Integer> selectedNodes,  Map<Integer, Set<Integer>> indegree, Map<Integer, Set<Integer>> graph) {
        List<Integer> sortRes = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for (int id : selectedNodes) {
            if (indegree.get(id).size() == 0) {
                queue.offer(id);
            }
        }
        while (!queue.isEmpty()) {
            int curNode = queue.poll();
            sortRes.add(curNode);
            Set<Integer> neighbors = graph.get(curNode); 
            if (neighbors != null) {
                for (int neighbor : neighbors) {

                    indegree.get(neighbor).remove(curNode);
                    if (indegree.get(neighbor).size() == 0) {
                        queue.offer(neighbor);
                    }

                }
            }
        }
        return sortRes;
    }

}
Auto-SK commented 2 years ago

思路

拓扑排序。

程序

class Solution:
    def tp_sort(self, items, indegree, neighbors):
        q = collections.deque([])
        ans = []
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            ans.append(cur)

            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)

        return ans

    def sortItems(self, n: int, m: int, group: List[int], pres: List[List[int]]) -> List[int]:
        max_group_id = m
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        project_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        project_neighbors = collections.defaultdict(list)
        group_neighbors = collections.defaultdict(list)
        group_projects = collections.defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)

            for pre in pres[project]:
                if group[pre] != group[project]:
                    # 小组关系图
                    group_indegree[group[project]] += 1
                    group_neighbors[group[pre]].append(group[project])
                else:
                    # 项目关系图
                    project_indegree[project] += 1
                    project_neighbors[pre].append(project)

        ans = []

        group_queue = self.tp_sort([i for i in range(max_group_id)], group_indegree, group_neighbors)

        if len(group_queue) != max_group_id:
            return []

        for group_id in group_queue:

            project_queue = self.tp_sort(group_projects[group_id], project_indegree, project_neighbors)

            if len(project_queue) != len(group_projects[group_id]):
                return []
            ans += project_queue

        return ans

复杂度

joeytor commented 2 years ago

思路

根据题意思路是 两重拓扑排序, 对组之间和组内 根据 before 数组都进行拓扑排序

先给 group 标号为 -1 的标记为一个新的只有这个数的组, 方便下面的排序

然后构建 组内, 组间的 next 数组, 即 before 的相反方向的链接, 并添加 1 到 被指向的 node 的 indegree

先 topological sort 组, 如果返回 [] 代表不可能那么返回 []

再遍历每一个组, topological sort 每个组内的 elements, 如果返回 [] 代表不可能那么返回 []

最后将两次 topological sort 的结果拼接, 先遍历组 topological sort 的结果,再将这个组 topological sort 好的 elements 添加到 res 数组中

topological bfs algorithm:

​ 先添加所有 indegree 为 0 的点到 queue 中

​ 如果 queue 不是empty, 那么将 queue 左端元素 pop, 添加到 res 数组中, 然后 将 这个元素指向的元素 (即要求这个元素最为 before 的元素) 的 indegree -1

​ 如果 -1 后这个元素的 indegree 是 0, 将这个元素 添加到 queue 中

​ 最后如果 res 数组的长度 跟 element 数组的长度不同, 说明无法完成 topological sort, 那么返回 [], 否则返回 res 数组

代码

import collections
from collections import deque

class Solution:
    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:

        def topological_sort(graph_next, graph_indegree, graph_elements):
            # bfs topological sort
            queue = deque()
            res = []
            for i in graph_elements:
                if graph_indegree[i] == 0:
                    queue.append(i)

            while len(queue) > 0:
                i = queue.popleft()
                res.append(i)
                for j in graph_next[i]:
                    graph_indegree[j] -= 1
                    if graph_indegree[j] == 0:
                        queue.append(j)

            if len(res) != len(graph_elements):
                return []
            else:
                return res

        # for the ones in group -1, give a unique group id
        for i in range(len(group)):
            if group[i] == -1:
                group[i] = m
                m += 1

        groupitem = collections.defaultdict(list)
        # build the within group and between group graph and indegree
        groupnext = collections.defaultdict(set)
        itemnext = collections.defaultdict(set)
        groupindegree = collections.defaultdict(int)
        itemindegree = collections.defaultdict(int)

        for i, before in enumerate(beforeItems):
            gi = group[i]
            groupitem[gi].append(i)
            for j in before:
                gj = group[j]
                # if in same group, then build within in group topological sort
                if group[i] == group[j]:
                    itemnext[j].add(i)
                    itemindegree[i] += 1
                else:
                    # remove duplicate links in graph pointing from 1 part to another
                    if gi not in groupnext[gj]:
                        groupnext[gj].add(gi)
                        groupindegree[gi] += 1

        # group topological sort
        group_res = topological_sort(groupnext, groupindegree, list(range(m)))
        if group_res == []:
            return []

        # item topological sort for each group
        item_res = []
        for i in range(m):
            groupelements = groupitem[i]
            i_res = topological_sort(itemnext, itemindegree, groupelements)
            if len(groupelements) > 0 and i_res == []:
                return []
            item_res.append(i_res)

        # put two topological result together into the order
        res = []
        for i in group_res:
            res.extend(item_res[i])

        return res

复杂度

时间复杂度: O(m+n) 拓扑排序的时间复杂度

空间复杂度: O(m+n) 存储组间依赖和入读的复杂度, 是 O(m+n)

JianXinyu commented 2 years ago

https://www.cnblogs.com/grandyang/p/15187461.html

class Solution {
public:
    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        vector<int> t, res(n), state(n + 2 * m);
        vector<vector<int>> g(n + 2 * m);
        for (int i = 0; i < n; ++i) {
            if (group[i] != -1) {
                g[n + group[i]].push_back(i);
                g[i].push_back(n + m + group[i]);
            }
            for (int j : beforeItems[i]) {
                if (group[i] != -1 && group[i] == group[j]) {
                    g[j].push_back(i);
                } else {
                    int p = group[i] == -1 ? i : n + group[i];
                    int q = group[j] == -1 ? j : n + m + group[j];
                    g[q].push_back(p);
                }
            }
        }
        for (int i = (int)g.size() - 1; i >= 0; --i) {
            if (!helper(g, i, state, t)) return {};
        }
        reverse(t.begin(), t.end());
        copy_if(t.begin(), t.end(), res.begin(), [&](int i) {return i < n;});
        return res;
    }
    bool helper(vector<vector<int>>& g, int i, vector<int>& state, vector<int>& res) {
        if (state[i] != 0) return state[i] == 2;
        state[i] = 1;
        for (int next : g[i]) {
            if (!helper(g, next, state, res)) return false;
        }
        state[i] = 2;
        res.push_back(i);
        return true;
    }
};
SunStrongChina commented 2 years ago

1203. 项目管理

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies/

前置知识

  • 图论
  • 拓扑排序
  • BFS & DFS

题目描述


公司共有 n 个项目和  m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。

group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。

请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:

同一小组的项目,排序后在列表中彼此相邻。
项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。

 

示例 1:

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
输出:[6,3,4,1,5,2,0,7]
示例 2:

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]]
输出:[]
解释:与示例 1 大致相同,但是在排序后的列表中,4 必须放在 6 的前面。
 

提示:

1 <= m <= n <= 3 * 104
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] 不含重复元素

双层拓扑排序,先对小组排序,再对组内项目排序,同一个小组的项目连续完成。 代码虽长 但重复多 小组排序和项目排序几乎一样 变量命名时注意一致 避免迷糊


def sortItems2(n, m, group, beforeItems)
# 为不属于任何小组的项目设置一个虚拟组名
j=m
for i in range(n):
if group[i]==-1: 
group[i]=j
j+=1
# 小组拓扑:in={小组:需要排在前面的小组} out={小组:需要排在后面的小组}
groups=list(set(group))
# 注意:使用set避免重复
group_in, group_out = defaultdict(set), defaultdict(set) 
for i in range(n):
for pre_i in beforeItems[i]:
if group[pre_i]!=group[i]: # 注意: 不等时才加入
group_in[group[i]].add(group[pre_i])
group_out[group[pre_i]].add(group[i])
indegree={g:len(group_in[g]) for g in groups}
que=[g for g in groups if indegree[g]==0]
order_group=[]
while que:
g = que.pop()
order_group.append(g)
for g_post in group_out[g]:
indegree[g_post]-=1
if indegree[g_post]==0: que.append(g_post)
if len(order_group)<len(groups): return []
# 建立字典 {小组: 组内项目}
group_item=defaultdict(list)
for i in range(len(group)):
group_item[group[i]].append(i)
# 组内项目拓扑排序 遍历小组按顺序执行
execute=[]
for g in order_group:
item_in, item_out = defaultdict(list), defaultdict(list)
for i in group_item[g]:
for pre_i in beforeItems[i]:
if pre_i in group_item[g]: # 注意: 同一个组才加入
item_in[i].append(pre_i)
item_out[pre_i].append(i)
indegree={i:len(item_in[i]) for i in group_item[g]}
que=[i for i in group_item[g] if indegree[i]==0]
temp=[]
while que:
i = que.pop()
temp.append(i)
for i_post in item_out[i]:
indegree[i_post]-=1
if indegree[i_post]==0: que.append(i_post)
if len(temp)<len(group_item[g]): return []
execute+=temp
return execute

时间复杂度: O(m+n) 

空间复杂度: O(m+n) 
laurallalala commented 2 years ago

代码

class Solution(object):
    def sortItems(self, n, m, group, beforeItems):
        """
        :type n: int
        :type m: int
        :type group: List[int]
        :type beforeItems: List[List[int]]
        :rtype: List[int]
        """
        max_group_id = m
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        project_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        project_neighbors = collections.defaultdict(list)
        group_neighbors = collections.defaultdict(list)
        group_projects = collections.defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)
            for pre in beforeItems[project]:
                if group[pre] != group[project]:
                    group_indegree[group[project]] += 1
                    group_neighbors[group[pre]].append(group[project])
                else:
                    project_indegree[project] += 1
                    project_neighbors[pre].append(project)
        ans = []

        group_queue = self.tp_sort([i for i in range(max_group_id)], group_indegree, group_neighbors)

        if len(group_queue) != max_group_id:
            return []

        for group_id in group_queue:
            project_queue = self.tp_sort(group_projects[group_id], project_indegree, project_neighbors)
            if len(project_queue) != len(group_projects[group_id]):
                return []
            ans += project_queue
        return ans

    def tp_sort(self, items, indegree, neighbors):
        q = collections.deque([])
        ans = []
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            ans.append(cur)
            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)

        return ans

复杂度

q815101630 commented 2 years ago

思路

学习了拓扑排序

class Solution:
    def tp_sort(self, items, indegree, neighbors):
        q = collections.deque([])
        ans = []
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            ans.append(cur)

            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)

        return ans

    def sortItems(self, n: int, m: int, group: List[int], pres: List[List[int]]) -> List[int]:
        max_group_id = m
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        project_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        project_neighbors = collections.defaultdict(list)
        group_neighbors = collections.defaultdict(list)
        group_projects = collections.defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)

            for pre in pres[project]:
                if group[pre] != group[project]:
                    # 小组关系图
                    group_indegree[group[project]] += 1
                    group_neighbors[group[pre]].append(group[project])
                else:
                    # 项目关系图
                    project_indegree[project] += 1
                    project_neighbors[pre].append(project)

        ans = []

        group_queue = self.tp_sort([i for i in range(max_group_id)], group_indegree, group_neighbors)

        if len(group_queue) != max_group_id:
            return []

        for group_id in group_queue:

            project_queue = self.tp_sort(group_projects[group_id], project_indegree, project_neighbors)

            if len(project_queue) != len(group_projects[group_id]):
                return []
            ans += project_queue

        return and

时间 O(m+n)

taoyr722 commented 2 years ago

思路

记录节点的依赖数,依赖数降到0时入队的方式。我们需要做到以下几点:

thisjustsoso commented 2 years ago

public class Solution {

public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
    // 第 1 步:数据预处理,给没有归属于一个组的项目编上组号
    for (int i = 0; i < group.length; i++) {
        if (group[i] == -1) {
            group[i] = m;
            m++;
        }
    }

    // 第 2 步:实例化组和项目的邻接表
    List<Integer>[] groupAdj = new ArrayList[m];
    List<Integer>[] itemAdj = new ArrayList[n];
    for (int i = 0; i < m; i++) {
        groupAdj[i] = new ArrayList<>();
    }
    for (int i = 0; i < n; i++) {
        itemAdj[i] = new ArrayList<>();
    }

    // 第 3 步:建图和统计入度数组
    int[] groupsIndegree = new int[m];
    int[] itemsIndegree = new int[n];

    int len = group.length;
    for (int i = 0; i < len; i++) {
        int currentGroup = group[i];
        for (int beforeItem : beforeItems.get(i)) {
            int beforeGroup = group[beforeItem];
            if (beforeGroup != currentGroup) {
                groupAdj[beforeGroup].add(currentGroup);
                groupsIndegree[currentGroup]++;
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (Integer item : beforeItems.get(i)) {
            itemAdj[item].add(i);
            itemsIndegree[i]++;
        }
    }

    // 第 4 步:得到组和项目的拓扑排序结果
    List<Integer> groupsList = topologicalSort(groupAdj, groupsIndegree, m);
    if (groupsList.size() == 0) {
        return new int[0];
    }
    List<Integer> itemsList = topologicalSort(itemAdj, itemsIndegree, n);
    if (itemsList.size() == 0) {
        return new int[0];
    }

    // 第 5 步:根据项目的拓扑排序结果,项目到组的多对一关系,建立组到项目的一对多关系
    // key:组,value:在同一组的项目列表
    Map<Integer, List<Integer>> groups2Items = new HashMap<>();
    for (Integer item : itemsList) {
        groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
    }

    // 第 6 步:把组的拓扑排序结果替换成为项目的拓扑排序结果
    List<Integer> res = new ArrayList<>();
    for (Integer groupId : groupsList) {
        List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
        res.addAll(items);
    }
    return res.stream().mapToInt(Integer::valueOf).toArray();
}

private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree, int n) {
    List<Integer> res = new ArrayList<>();
    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        if (inDegree[i] == 0) {
            queue.offer(i);
        }
    }

    while (!queue.isEmpty()) {
        Integer front = queue.poll();
        res.add(front);
        for (int successor : adj[front]) {
            inDegree[successor]--;
            if (inDegree[successor] == 0) {
                queue.offer(successor);
            }
        }
    }

    if (res.size() == n) {
        return res;
    }
    return new ArrayList<>();
}

}

winterdogdog commented 2 years ago
const topSort = (deg, graph, items) => {
    const Q = [];
    for (const item of items) {
        if (deg[item] === 0) {
            Q.push(item);
        }
    }
    const res = [];
    while (Q.length) {
        const u = Q.shift(); 
        res.push(u);
        for (let i = 0; i < graph[u].length; ++i) {
            const v = graph[u][i];
            if (--deg[v] === 0) {
                Q.push(v);
            }
        }
    }
    return res.length == items.length ? res : [];
}

var sortItems = function(n, m, group, beforeItems) {
    const groupItem = new Array(n + m).fill(0).map(() => []);

    // 组间和组内依赖图
    const groupGraph = new Array(n + m).fill(0).map(() => []);
    const itemGraph = new Array(n).fill(0).map(() => []);

    // 组间和组内入度数组
    const groupDegree = new Array(n + m).fill(0);
    const itemDegree = new Array(n).fill(0);

    const id = new Array(n + m).fill(0).map((v, index) => index);

    let leftId = m;
    // 给未分配的 item 分配一个 groupId
    for (let i = 0; i < n; ++i) {
        if (group[i] === -1) {
            group[i] = leftId;
            leftId += 1;
        }
        groupItem[group[i]].push(i);
    }
    // 依赖关系建图
    for (let i = 0; i < n; ++i) {
        const curGroupId = group[i];
        for (const item of beforeItems[i]) {
            const beforeGroupId = group[item];
            if (beforeGroupId === curGroupId) {
                itemDegree[i] += 1;
                itemGraph[item].push(i);   
            } else {
                groupDegree[curGroupId] += 1;
                groupGraph[beforeGroupId].push(curGroupId);
            }
        }
    }

    // 组间拓扑关系排序
    const groupTopSort = topSort(groupDegree, groupGraph, id); 
    if (groupTopSort.length == 0) {
        return [];
    } 
    const ans = [];
    // 组内拓扑关系排序
    for (const curGroupId of groupTopSort) {
        const size = groupItem[curGroupId].length;
        if (size == 0) {
            continue;
        }
        const res = topSort(itemDegree, itemGraph, groupItem[curGroupId]);
        if (res.length === 0) {
            return [];
        }
        for (const item of res) {
            ans.push(item);
        }
    }
    return ans;
};
leungogogo commented 2 years ago

LC1203. Sort Items by Groups Respecting Dependencies

Method. Topological Sort

Main Idea

Code

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {

        Map<Integer, Set<Integer>> groupGraph = new HashMap<>();
        Map<Integer, Integer> indegreeGroup = new HashMap<>();

        int increment = 0;
        for(int i = 0 ; i < group.length; i++){
            if(group[i]==-1){
                group[i] = group[i] + increment;
                increment = increment-2;
            }
            groupGraph.put(group[i], new HashSet<Integer>());
        }

        Map<Integer, Set<Integer>> itemGraph = new HashMap<>();
        Map<Integer, Integer> indegreeItem = new HashMap<>();

        for(int i = 0; i < n ; i++)
            itemGraph.put(i, new HashSet<Integer>());

        for(int i = 0 ; i < beforeItems.size(); i++){
            List<Integer> l = beforeItems.get(i);
            for(int item : l){
                itemGraph.get(item).add(i);
                indegreeItem.put(i, indegreeItem.getOrDefault(i,0)+1);

                int group1 = group[item];
                int group2 = group[i];

                if(group1!=group2 && groupGraph.get(group1).add(group2)){
                    indegreeGroup.put(group2, indegreeGroup.getOrDefault(group2,0)+1);
                }
            }
        }

        List<Integer> itemOrdering = topoSort(itemGraph, indegreeItem, n);
        List<Integer> groupOrdering = topoSort(groupGraph, indegreeGroup, groupGraph.size());

        if(itemOrdering.size()==0 || groupOrdering.size()==0) return new int[0];

        Map<Integer, List<Integer>> map = new HashMap<>();
        for(int item : itemOrdering){
            int grp = group[item];
            map.putIfAbsent(grp, new ArrayList<>());
            map.get(grp).add(item);
        }

        int[] res = new int[n];
        int i=0;
        for(int grp : groupOrdering){
            List<Integer> l = map.get(grp);
            for(int item : l){
                res[i] = item;
                i++;
            }
        }
        return res;
    }

    private List<Integer> topoSort(Map<Integer, Set<Integer>> itemGraph, 
    Map<Integer, Integer> indegreeItem, int countOfItems) {

        Queue<Integer> q = new LinkedList<>();
        List<Integer> res = new ArrayList<>();

        for(int i: itemGraph.keySet()){
            if(indegreeItem.getOrDefault(i,0)==0)
                q.add(i);
        }

        while(!q.isEmpty()){
            int pop = q.poll();

            countOfItems--;
            res.add(pop);

            for(int nextItem : itemGraph.get(pop)){
                int indegreeOfNextItem = indegreeItem.get(nextItem)-1;
                indegreeItem.put(nextItem, indegreeOfNextItem);
                if(indegreeOfNextItem==0)
                    q.add(nextItem);
            }
        }

        if(countOfItems==0) return res;
        else return new ArrayList<>();

    }
}

Complexity Analysis

ff1234-debug commented 2 years ago

思路:

方法: 拓扑排序

代码

实现语言: C++

class Solution {
public:
    vector<int> topSort(vector<int>& deg, vector<vector<int>>& graph, vector<int>& items) {
        queue<int> Q;
        for (auto& item: items) {
            if (deg[item] == 0) {
                Q.push(item);
            }
        }
        vector<int> res;
        while (!Q.empty()) {
            int u = Q.front(); 
            Q.pop();
            res.emplace_back(u);
            for (auto& v: graph[u]) {
                if (--deg[v] == 0) {
                    Q.push(v);
                }
            }
        }
        return res.size() == items.size() ? res : vector<int>{};
    }

    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        vector<vector<int>> groupItem(n + m);

        // 组间和组内依赖图
        vector<vector<int>> groupGraph(n + m);
        vector<vector<int>> itemGraph(n);

        // 组间和组内入度数组
        vector<int> groupDegree(n + m, 0);
        vector<int> itemDegree(n, 0);

        vector<int> id;
        for (int i = 0; i < n + m; ++i) {
            id.emplace_back(i);
        }

        int leftId = m;
        // 给未分配的 item 分配一个 groupId
        for (int i = 0; i < n; ++i) {
            if (group[i] == -1) {
                group[i] = leftId;
                leftId += 1;
            }
            groupItem[group[i]].emplace_back(i);
        }
        // 依赖关系建图
        for (int i = 0; i < n; ++i) {
            int curGroupId = group[i];
            for (auto& item: beforeItems[i]) {
                int beforeGroupId = group[item];
                if (beforeGroupId == curGroupId) {
                    itemDegree[i] += 1;
                    itemGraph[item].emplace_back(i);   
                } else {
                    groupDegree[curGroupId] += 1;
                    groupGraph[beforeGroupId].emplace_back(curGroupId);
                }
            }
        }

        // 组间拓扑关系排序
        vector<int> groupTopSort = topSort(groupDegree, groupGraph, id); 
        if (groupTopSort.size() == 0) {
            return vector<int>{};
        } 
        vector<int> ans;
        // 组内拓扑关系排序
        for (auto& curGroupId: groupTopSort) {
            int size = groupItem[curGroupId].size();
            if (size == 0) {
                continue;
            }
            vector<int> res = topSort(itemDegree, itemGraph, groupItem[curGroupId]);
            if (res.size() == 0) {
                return vector<int>{};
            }
            for (auto& item: res) {
                ans.emplace_back(item);
            }
        }
        return ans;
    }
};

复杂度分析:

ZETAVI commented 2 years ago

思路

做两次拓扑排序

目前还没理解,先来打卡~~

语言

java

代码

 public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        List<List<Integer>> groupItem = new ArrayList<>();//项目分组
        for(int i = 0;i < n + m;i++){//初始化小组
            groupItem.add(new ArrayList<>());
        }
        int gId = m;//新的组号从m开始
        for(int i = 0;i < group.length;i++){
            if(group[i] == -1)group[i] = gId++;//没有id的加上组id
            groupItem.get(group[i]).add(i);//同一组的放在一起
        }
        List<List<Integer>> graphInGroup = new ArrayList<>();//组内拓扑关系
        List<List<Integer>> graphOutGroup = new ArrayList<>();//组间拓扑关系
        for(int i = 0;i < n + m;i++){//初始化拓扑关系
            graphOutGroup.add(new ArrayList<>());
            if(i >= n)continue;
            graphInGroup.add(new ArrayList<>());
        }
        List<Integer> groupId = new ArrayList<>();//所有组id
        for(int i = 0;i < n + m;i++){
            groupId.add(i);
        }
        // 需要拓扑排序 所以结点的入度必不可少 两个数组分别维护不同结点的入度
        int[] degInGroup = new int[n];//组内 结点入度 (组内项目入度)
        int[] degOutGroup = new int[n + m];//组间 结点入度(小组入度)

        for(int i = 0;i < beforeItems.size();i++){//遍历关系
            int curGroupId = group[i];//当前项目i所属的小组id
            List<Integer> beforeItem = beforeItems.get(i);
            for(Integer item : beforeItem){
                if(group[item] == curGroupId){//同一组 修改组内拓扑
                    degInGroup[i]++;// 组内结点的入度+1
                    graphInGroup.get(item).add(i);//item 在 i之前
                }else{
                    degOutGroup[curGroupId]++;// 小组间的结点入度 + 1
                    graphOutGroup.get(group[item]).add(curGroupId);// group[item] 小组 在 curGroupId 之前
                }
            }
        }
        //组间拓扑排序,也就是小组之间的拓扑排序,需要的参数 小组结点的入度degOutGroup,所有的小组groupId,组间的拓扑关系图graphOutGroup
        List<Integer> outGroupTopSort = topSort(degOutGroup,groupId,graphOutGroup);
        if(outGroupTopSort.size() == 0)return new int[0];//无法拓扑排序 返回

        int[] res = new int[n];
        int index = 0;
        for(Integer gid : outGroupTopSort){//遍历排序后的小组id
            List<Integer> items = groupItem.get(gid);//根据小组id 拿到这一小组中的所有成员
            if(items.size() == 0)continue;
            //组内拓扑排序,需要的参数 组内结点的入度degInGroup,组内的所有的结点groupItem.get(gid),组内的拓扑关系图graphInGroup
            List<Integer> inGourpTopSort = topSort(degInGroup,groupItem.get(gid),graphInGroup);
            if(inGourpTopSort.size() == 0)return new int[0];//无法拓扑排序 返回
            for(int item : inGourpTopSort){//排序后,依次的放入答案集合当中
                res[index++] = item;
            }
        }
        return res;
    }

    public List<Integer> topSort(int[] deg, List<Integer> items, List<List<Integer>> graph){
        Queue<Integer> queue = new LinkedList<>();
        for(Integer item:items){
            if(deg[item] == 0)queue.offer(item);
        }
        List<Integer> res = new ArrayList<>();
        while(!queue.isEmpty()){
            int cur = queue.poll();
            res.add(cur);
            for(int neighbor: graph.get(cur)){
                if(--deg[neighbor] == 0){
                    queue.offer(neighbor);
                }
            }
        }
        return res.size() == items.size() ? res : new ArrayList<>();
    }

复杂度分析

Richard-LYF commented 2 years ago

class Solution: def tp_sort(self, items, indegree, neighbors): q = collections.deque([]) ans = [] for item in items: if not indegree[item]: q.append(item) while q: cur = q.popleft() ans.append(cur)

        for neighbor in neighbors[cur]:
            indegree[neighbor] -= 1
            if not indegree[neighbor]:
                q.append(neighbor)

    return ans

def sortItems(self, n: int, m: int, group: List[int], pres: List[List[int]]) -> List[int]:
    max_group_id = m
    for project in range(n):
        if group[project] == -1:
            group[project] = max_group_id
            max_group_id += 1

    project_indegree = collections.defaultdict(int)
    group_indegree = collections.defaultdict(int)
    project_neighbors = collections.defaultdict(list)
    group_neighbors = collections.defaultdict(list)
    group_projects = collections.defaultdict(list)

    for project in range(n):
        group_projects[group[project]].append(project)

        for pre in pres[project]:
            if group[pre] != group[project]:
                # 小组关系图
                group_indegree[group[project]] += 1
                group_neighbors[group[pre]].append(group[project])
            else:
                # 项目关系图
                project_indegree[project] += 1
                project_neighbors[pre].append(project)

    ans = []

    group_queue = self.tp_sort([i for i in range(max_group_id)], group_indegree, group_neighbors)

    if len(group_queue) != max_group_id:
        return []

    for group_id in group_queue:

        project_queue = self.tp_sort(group_projects[group_id], project_indegree, project_neighbors)

        if len(project_queue) != len(group_projects[group_id]):
            return []
        ans += project_queue

    return ans
Zhang6260 commented 2 years ago

JAVA版本

思路:使用拓扑排序(还是很是很懂的,明天再看看吧)。

class Solution {
   public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
       List<List<Integer>> groupItem = new ArrayList<List<Integer>>();
       for (int i = 0; i < n + m; ++i) {
           groupItem.add(new ArrayList<Integer>());
       }

       // 组间和组内依赖图
       List<List<Integer>> groupGraph = new ArrayList<List<Integer>>();
       for (int i = 0; i < n + m; ++i) {
           groupGraph.add(new ArrayList<Integer>());
       }
       List<List<Integer>> itemGraph = new ArrayList<List<Integer>>();
       for (int i = 0; i < n; ++i) {
           itemGraph.add(new ArrayList<Integer>());
       }

       // 组间和组内入度数组
       int[] groupDegree = new int[n + m];
       int[] itemDegree = new int[n];

       List<Integer> id = new ArrayList<Integer>();
       for (int i = 0; i < n + m; ++i) {
           id.add(i);
       }

       int leftId = m;
       // 给未分配的 item 分配一个 groupId
       for (int i = 0; i < n; ++i) {
           if (group[i] == -1) {
               group[i] = leftId;
               leftId += 1;
           }
           groupItem.get(group[i]).add(i);
       }
       // 依赖关系建图
       for (int i = 0; i < n; ++i) {
           int curGroupId = group[i];
           for (int item : beforeItems.get(i)) {
               int beforeGroupId = group[item];
               if (beforeGroupId == curGroupId) {
                   itemDegree[i] += 1;
                   itemGraph.get(item).add(i);   
               } else {
                   groupDegree[curGroupId] += 1;
                   groupGraph.get(beforeGroupId).add(curGroupId);
               }
           }
       }

       // 组间拓扑关系排序
       List<Integer> groupTopSort = topSort(groupDegree, groupGraph, id); 
       if (groupTopSort.size() == 0) {
           return new int[0];
       }
       int[] ans = new int[n];
       int index = 0;
       // 组内拓扑关系排序
       for (int curGroupId : groupTopSort) {
           int size = groupItem.get(curGroupId).size();
           if (size == 0) {
               continue;
           }
           List<Integer> res = topSort(itemDegree, itemGraph, groupItem.get(curGroupId));
           if (res.size() == 0) {
               return new int[0];
           }
           for (int item : res) {
               ans[index++] = item;
           }
       }
       return ans;
   }

   public List<Integer> topSort(int[] deg, List<List<Integer>> graph, List<Integer> items) {
       Queue<Integer> queue = new LinkedList<Integer>();
       for (int item : items) {
           if (deg[item] == 0) {
               queue.offer(item);
           }
       }
       List<Integer> res = new ArrayList<Integer>();
       while (!queue.isEmpty()) {
           int u = queue.poll(); 
           res.add(u);
           for (int v : graph.get(u)) {
               if (--deg[v] == 0) {
                   queue.offer(v);
               }
           }
       }
       return res.size() == items.size() ? res : new ArrayList<Integer>();
   }
}

时间复杂度:O(n+m)

空间复杂度:O(n+m)

mixtureve commented 2 years ago

思路

拓扑排序

代码

Java Code:


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

        int[] countsGroup = new int[m];
        List<List<Integer>> graphGroups = buildGraphGroups(group, beforeItems, countsGroup, m, n);

        int[] countsItem = new int[n];
        List<List<Integer>> graphItems = buildGraphItems(beforeItems, countsItem, n);

        // to acquire the topologically sorted groups and items
        List<Integer> groupsSorted = topologicalSort(graphGroups, countsGroup);
        List<Integer> itemsSorted = topologicalSort(graphItems, countsItem);
        // to detect any cycle
        if(groupsSorted.isEmpty() || itemsSorted.isEmpty()) return new int[0];

        // to build up the relationship between sorted groups and sorted items
        List<List<Integer>> groupsToItems = new ArrayList<List<Integer>>(m);
        for(int i = 0; i < m; i++){
            groupsToItems.add(new ArrayList<Integer>());
        }
        for(int item : itemsSorted){
            groupsToItems.get(group[item]).add(item);
        }

        // to generate the answer array
        int[] ans = new int[n];
        int idx = 0;
        // to first pick, in front groups, front elements/items 
        for(int curGroup : groupsSorted){
            for(int item : groupsToItems.get(curGroup)) {
                ans[idx++] = item;
            }
        }

        return ans;
    }

    private List<Integer> topologicalSort(List<List<Integer>> graph, int[] counts){
        final int N = counts.length;
        List<Integer> res = new ArrayList<Integer>();
        Queue<Integer> queue = new LinkedList<Integer>();
        for(int i = 0; i < N; i++){
            if(counts[i] == 0){
                queue.add(i);
            }
        }

        int count = 0;
        while(!queue.isEmpty()){
            int node = queue.poll();
            res.add(node); 
            count++;
            for(int neighbor : graph.get(node)){
                if(--counts[neighbor] == 0){
                    queue.offer(neighbor);
                }
            }
        }

        return count == N ? res : new ArrayList<Integer>();
    }

    private List<List<Integer>> buildGraphItems(List<List<Integer>> beforeItems, 
                                                int[] counts, 
                                                int n){
        List<List<Integer>> graph = new ArrayList<List<Integer>>();
        for(int i = 0; i < n; i++){
            graph.add(new ArrayList<Integer>());
        }

        for(int i = 0; i < n; i++){
            List<Integer> items = beforeItems.get(i);
            for(int item : items){
                graph.get(item).add(i);
                ++counts[i];
            }
        }

        return graph;
    }

    private List<List<Integer>> buildGraphGroups(int[] group, 
                                                 List<List<Integer>> beforeItems, 
                                                 int[] counts, 
                                                 int m,
                                                 int n){
        List<List<Integer>> graph = new ArrayList<List<Integer>>(m);
        for(int i = 0; i < m; i++){
            graph.add(new ArrayList<Integer>());
        }

        for(int i = 0; i < n; i++){
            int toGroup = group[i];
            List<Integer> fromItems = beforeItems.get(i);
            for(int fromItem : fromItems){
                int fromGroup = group[fromItem];
                if(fromGroup != toGroup){
                    graph.get(fromGroup).add(toGroup);
                    ++counts[toGroup];
                }
            }
        }

        return graph;
    }
}

复杂度分析

ChenJingjing85 commented 2 years ago

代码

 public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        List<List<Integer>> groupItem = new ArrayList<>();//项目分组
        for(int i = 0;i < n + m;i++){//初始化小组
            groupItem.add(new ArrayList<>());
        }
        int gId = m;//新的组号从m开始
        for(int i = 0;i < group.length;i++){
            if(group[i] == -1)group[i] = gId++;//没有id的加上组id
            groupItem.get(group[i]).add(i);//同一组的放在一起
        }
        List<List<Integer>> graphInGroup = new ArrayList<>();//组内拓扑关系
        List<List<Integer>> graphOutGroup = new ArrayList<>();//组间拓扑关系
        for(int i = 0;i < n + m;i++){//初始化拓扑关系
            graphOutGroup.add(new ArrayList<>());
            if(i >= n)continue;
            graphInGroup.add(new ArrayList<>());
        }
        List<Integer> groupId = new ArrayList<>();//所有组id
        for(int i = 0;i < n + m;i++){
            groupId.add(i);
        }
        // 需要拓扑排序 所以结点的入度必不可少 两个数组分别维护不同结点的入度
        int[] degInGroup = new int[n];//组内 结点入度 (组内项目入度)
        int[] degOutGroup = new int[n + m];//组间 结点入度(小组入度)

        for(int i = 0;i < beforeItems.size();i++){//遍历关系
            int curGroupId = group[i];//当前项目i所属的小组id
            List<Integer> beforeItem = beforeItems.get(i);
            for(Integer item : beforeItem){
                if(group[item] == curGroupId){//同一组 修改组内拓扑
                    degInGroup[i]++;// 组内结点的入度+1
                    graphInGroup.get(item).add(i);//item 在 i之前
                }else{
                    degOutGroup[curGroupId]++;// 小组间的结点入度 + 1
                    graphOutGroup.get(group[item]).add(curGroupId);// group[item] 小组 在 curGroupId 之前
                }
            }
        }
        //组间拓扑排序,也就是小组之间的拓扑排序,需要的参数 小组结点的入度degOutGroup,所有的小组groupId,组间的拓扑关系图graphOutGroup
        List<Integer> outGroupTopSort = topSort(degOutGroup,groupId,graphOutGroup);
        if(outGroupTopSort.size() == 0)return new int[0];//无法拓扑排序 返回

        int[] res = new int[n];
        int index = 0;
        for(Integer gid : outGroupTopSort){//遍历排序后的小组id
            List<Integer> items = groupItem.get(gid);//根据小组id 拿到这一小组中的所有成员
            if(items.size() == 0)continue;
            //组内拓扑排序,需要的参数 组内结点的入度degInGroup,组内的所有的结点groupItem.get(gid),组内的拓扑关系图graphInGroup
            List<Integer> inGourpTopSort = topSort(degInGroup,groupItem.get(gid),graphInGroup);
            if(inGourpTopSort.size() == 0)return new int[0];//无法拓扑排序 返回
            for(int item : inGourpTopSort){//排序后,依次的放入答案集合当中
                res[index++] = item;
            }
        }
        return res;
    }

    public List<Integer> topSort(int[] deg, List<Integer> items, List<List<Integer>> graph){
        Queue<Integer> queue = new LinkedList<>();
        for(Integer item:items){
            if(deg[item] == 0)queue.offer(item);
        }
        List<Integer> res = new ArrayList<>();
        while(!queue.isEmpty()){
            int cur = queue.poll();
            res.add(cur);
            for(int neighbor: graph.get(cur)){
                if(--deg[neighbor] == 0){
                    queue.offer(neighbor);
                }
            }
        }
        return res.size() == items.size() ? res : new ArrayList<>();
    }
vaqua commented 2 years ago

思路

组与组的依赖关系,组抽象成点,依赖关系抽象成边,看是否有拓扑
有部位未理解,以后改

/*
 * @lc app=leetcode.cn id=1203 lang=javascript
 *
 * [1203] 项目管理
 */

// @lc code=start
/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function(n, m, group, beforeItems) {
  const groupItem = new Array(n + m).fill(0).map(() => [])

  // 组间和组内依赖图
  const groupGraph = new Array(n + m).fill(0).map(() => [])
  const itemGraph = new Array(n).fill(0).map(() => [])

  // 组间和组内入度数组
  const groupDegree = new Array(n + m).fill(0)
  const itemDegree = new Array(n).fill(0)

  const id = new Array(n + m).fill(0).map((v, index) => index)

  let leftId = m
  // 给未分配的 item 分配一个 groupId
  for (let i = 0; i < n; ++i) {
    if (group[i] === -1) {
      group[i] = leftId
      leftId += 1
    }
    groupItem[group[i]].push(i)
  }
  // 依赖关系建图
  for (let i = 0; i < n; ++i) {
    const curGroupId = group[i]
    for (const item of beforeItems[i]) {
      const beforeGroupId = group[item]
      if (beforeGroupId === curGroupId) {
        itemDegree[i] += 1
        itemGraph[item].push(i)
      } else {
        groupDegree[curGroupId] += 1
        groupGraph[beforeGroupId].push(curGroupId)
      }
    }
  }

  // 组间拓扑关系排序
  const groupTopSort = topSort(groupDegree, groupGraph, id)
  if (groupTopSort.length == 0) {
    return []
  }
  const ans = []
  // 组内拓扑关系排序
  for (const curGroupId of groupTopSort) {
    const size = groupItem[curGroupId].length
    if (size == 0) {
      continue
    }
    const res = topSort(itemDegree, itemGraph, groupItem[curGroupId])
    if (res.length === 0) {
      return []
    }
    for (const item of res) {
      ans.push(item)
    }
  }
  return ans
};

const topSort = (deg, graph, items) => {
  const Q = []
  for (const item of items) {
    if (deg[item] === 0) {
      Q.push(item)
    }
  }
  const res = []
  while (Q.length) {
    const u = Q.shift()
    res.push(u)
    for (let i = 0; i < graph[u].length; ++i) {
      const v = graph[u][i]
      if (--deg[v] === 0) {
        Q.push(v)
      }
    }
  }
  return res.length == items.length ? res : []
}
// @lc code=end

复杂度分析:

空间复杂度:O(n+m)
时间复杂度:O(n+m)

lihuiwen commented 2 years ago

代码

const topSort = (deg, graph, items) => {
    const Q = [];
    for (const item of items) {
        if (deg[item] === 0) {
            Q.push(item);
        }
    }
    const res = [];
    while (Q.length) {
        const u = Q.shift(); 
        res.push(u);
        for (let i = 0; i < graph[u].length; ++i) {
            const v = graph[u][i];
            if (--deg[v] === 0) {
                Q.push(v);
            }
        }
    }
    return res.length == items.length ? res : [];
}

var sortItems = function(n, m, group, beforeItems) {
    const groupItem = new Array(n + m).fill(0).map(() => []);

    // 组间和组内依赖图
    const groupGraph = new Array(n + m).fill(0).map(() => []);
    const itemGraph = new Array(n).fill(0).map(() => []);

    // 组间和组内入度数组
    const groupDegree = new Array(n + m).fill(0);
    const itemDegree = new Array(n).fill(0);

    const id = new Array(n + m).fill(0).map((v, index) => index);

    let leftId = m;
    // 给未分配的 item 分配一个 groupId
    for (let i = 0; i < n; ++i) {
        if (group[i] === -1) {
            group[i] = leftId;
            leftId += 1;
        }
        groupItem[group[i]].push(i);
    }
    // 依赖关系建图
    for (let i = 0; i < n; ++i) {
        const curGroupId = group[i];
        for (const item of beforeItems[i]) {
            const beforeGroupId = group[item];
            if (beforeGroupId === curGroupId) {
                itemDegree[i] += 1;
                itemGraph[item].push(i);   
            } else {
                groupDegree[curGroupId] += 1;
                groupGraph[beforeGroupId].push(curGroupId);
            }
        }
    }

    // 组间拓扑关系排序
    const groupTopSort = topSort(groupDegree, groupGraph, id); 
    if (groupTopSort.length == 0) {
        return [];
    } 
    const ans = [];
    // 组内拓扑关系排序
    for (const curGroupId of groupTopSort) {
        const size = groupItem[curGroupId].length;
        if (size == 0) {
            continue;
        }
        const res = topSort(itemDegree, itemGraph, groupItem[curGroupId]);
        if (res.length === 0) {
            return [];
        }
        for (const item of res) {
            ans.push(item);
        }
    }
    return ans;
};
jaysonss commented 2 years ago

思路

代码实现

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer,List<Integer>> group_projects = new HashMap<>();
        Map<Integer,Integer> groupDegres = new HashMap<>();
        Map<Integer,List<Integer>> groupNext = new HashMap<>();
        Map<Integer,Integer> projectDegree = new HashMap<>();
        Map<Integer,List<Integer>> projectNext = new HashMap<>();
        int nonGroupIdx = m;

        for(int i=0;i<n;i++){
            if(group[i] == -1){
                //给没有组的元素设置group
                group_projects.put(nonGroupIdx,Collections.singletonList(i));
                group[i] = nonGroupIdx;
                nonGroupIdx++;
            }else {
                fillMapList(group_projects, group[i], i);
            }
        }

        //创建图
        for(int project=0;project<n;project++){
            List<Integer> preList = beforeItems.get(project);
            for(int pre : preList){
                int preGroup = group[pre];
                int projectGroup = group[project];
                if(preGroup!=projectGroup){
                    addMapValue(groupDegres, projectGroup);
                    fillMapList(groupNext, preGroup, projectGroup);
                }else {
                    addMapValue(projectDegree, project);
                    fillMapList(projectNext, pre, project);
                }
            }
        }

        //获取结果
        List<Integer> gIdxList = new ArrayList<>();
        for(int i=0;i<nonGroupIdx;i++){
            gIdxList.add(i);
        }
        List<Integer> sortedGroup = topSort(gIdxList,groupDegres,groupNext);
        if(sortedGroup.size()!=nonGroupIdx)return new int[0];
        int[] ans = new int[n];
        int idx = 0;

        for(int sgroup : sortedGroup){
            List<Integer> projectList = group_projects.get(sgroup);
            if(projectList == null)continue;
            List<Integer> sortedProjectList = topSort(projectList,projectDegree,projectNext);

            if(sortedProjectList.size()!=projectList.size())return new int[0];

            for(int project : sortedProjectList){
                ans[idx++] = project;
            }
        }
        return ans;
    }

    List<Integer> topSort(List<Integer> valueList, Map<Integer,Integer> degrees,Map<Integer,List<Integer>> next){
        LinkedList<Integer> queue = new LinkedList<>();
        for(int v : valueList){
            if(degrees.get(v) == null){
                queue.offerLast(v);
            }
        }

        List<Integer> ans = new ArrayList<>();
        while(!queue.isEmpty()){
            int group = queue.pollFirst();
            ans.add(group);

            List<Integer> nextList = next.get(group);
            if(nextList != null){
                for(int nextRet : nextList){
                    int latest = decMapValue(degrees, nextRet);
                    if(latest == 0){
                        queue.offerLast(nextRet);
                    }
                }
            }
        }
        return ans;
    }

    void addMapValue(Map<Integer,Integer> map, int key){
        Integer v = map.get(key);
        if(v == null){
            map.put(key,1);
        }else {
            map.put(key,v + 1);
        }
    }

    int decMapValue(Map<Integer,Integer> map, int key){
        Integer v = map.get(key);
        if(v != null){
            map.put(key,v - 1);
        }
        return v - 1;
    }

    void fillMapList(Map<Integer,List<Integer>> map, int key,int value){
        List<Integer> v = map.get(key);
        if(v == null){
            v = new ArrayList<>();
            map.put(key,v);
        }
        v.add(value);
    }
}

复杂度分析

时间和空间都是O(m+n^2)

skinnyh commented 2 years ago

Note

Solution

class Solution:
    # Return the topological sorted result of items
    def topological_sort(self, items, indegree, adjacency_list):
        q = collections.deque()
        res = []
        for i in items:
            if indegree[i] == 0:
                q.append(i)
        while(q):
            i = q.popleft()
            res.append(i)
            for adj in adjacency_list[i]:
                indegree[adj] -= 1
                if indegree[adj] == 0:
                    q.append(adj)
        return res

    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        item_indegree = collections.defaultdict(int)
        group_indegree = collections.defaultdict(int)
        item_adjacencies = collections.defaultdict(list)
        group_adjacencies = collections.defaultdict(list)
        group_items = collections.defaultdict(list)

        # dummy groups for unassigned items
        max_group = m
        for i in range(n):
            if group[i] == -1:
                group[i] = max_group
                max_group += 1

        # Build adjacency lists for groups and items
        for i in range(n):
            group_items[group[i]].append(i)
            for pre in beforeItems[i]:
                if group[pre] == group[i]:
                    item_indegree[i] += 1
                    item_adjacencies[pre].append(i)
                else:
                    group_indegree[group[i]] += 1
                    group_adjacencies[group[pre]].append(group[i])

        # Topological sort for groups
        sorted_groups = self.topological_sort([i for i in range(max_group)], group_indegree, group_adjacencies)
        if len(sorted_groups) != max_group:
            return []
        res = []
        for g in sorted_groups:
            # Topological sort for items in each group
            sorted_items = self.topological_sort(group_items[g], item_indegree, item_adjacencies)
            if len(sorted_items) != len(group_items[g]):
                return []
            res += sorted_items
        return res

Time complexity: O(V + E) V is the number of vertices in and E is the number of edges.

Space complexity: O(V + E)

shuichen17 commented 2 years ago
var sortItems = function(n, m, group, beforeItems) {
    for (let i = 0; i < group.length; i++) {
        if (group[i] === -1) {
            group[i] = m;
            m++;
        }
    }

    let groupAdj = new Array(m).fill(0).map(() => []);
    let itemAdj = new Array(n).fill(0).map(() => []);

    let groupsIndegree = new Array(m).fill(0);
    let itemsIndegree = new Array(n).fill(0);
    let len = group.length;
    for (let i = 0; i < len; i++) {
        let currentGroup = group[i];
        for (let beforeItem of beforeItems[i]) {
            let beforeGroup = group[beforeItem];
            if (beforeGroup !== currentGroup) {
                groupAdj[beforeGroup].push(currentGroup);
                groupsIndegree[currentGroup]++;
            }
        }
    }
    for (let i = 0; i < n; i++) {
        for (let item of beforeItems[i]) {
            itemAdj[item].push(i);
            itemsIndegree[i]++;
        }
    }
    let groupList = topologicalSort(groupAdj, groupsIndegree, m);

    if (groupList.length === 0) {
        return [];
    }

    let itemList = topologicalSort(itemAdj, itemsIndegree, n);

    if (itemList.length === 0) {
        return [];
    }
    let map = new Map();
    for (let item of itemList) {
        let groupId = group[item];
        if (!map.has(groupId)) {
            map.set(groupId, []);
        }
        map.get(groupId).push(item);
    }

    let ans = [];
    for (let g of groupList) {
        let items = map.get(g);
        if (items !== undefined) {
           ans.push(...items); 
        }

    }
    return ans;
};

var topologicalSort = function (adj, inDegree, n) {
    let queue = [];
    let res = [];
    for (let i = 0; i < inDegree.length; i++) {
        if (inDegree[i] === 0) {
            queue.push(i);
        }
    }
    while (queue.length !== 0) {
        let node = queue.shift();
        res.push(node);
        for (let successor of adj[node]) {
            inDegree[successor]--;
            if (inDegree[successor] === 0) {
                queue.push(successor);
            }
        }
    }
   return res.length === n ? res : [];
}
hewenyi666 commented 2 years ago

题目名称

problem name

题目链接

https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies/

题目思路

code for Python3

class Solution:
    def topological_sort(self,items,indegree,neighbors):
        # 建立队列和访问顺序
        queue = collections.deque()
        res = []

        # 初始化队列
        for item in items:
            if not indegree[item]:
                queue.append(item)

        if not queue: return []

        # BFS
        while queue:
            cur = queue.popleft()
            res.append(cur)

            # 遍历邻居节点
            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    queue.append(neighbor)

        return res

    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        max_group_id = m
        for task in range(n):
            if group[task] == -1:

复杂度分析

Huangxuang commented 2 years ago

题目:1203. Sort Items by Groups Respecting Dependencies

思路

代码

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

public class Solution {

    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        // 第 1 步:数据预处理,给没有归属于一个组的项目编上组号
        for (int i = 0; i < group.length; i++) {
            if (group[i] == -1) {
                group[i] = m;
                m++;
            }
        }

        // 第 2 步:实例化组和项目的邻接表
        List<Integer>[] groupAdj = new ArrayList[m];
        List<Integer>[] itemAdj = new ArrayList[n];
        for (int i = 0; i < m; i++) {
            groupAdj[i] = new ArrayList<>();
        }
        for (int i = 0; i < n; i++) {
            itemAdj[i] = new ArrayList<>();
        }

        // 第 3 步:建图和统计入度数组
        int[] groupsIndegree = new int[m];
        int[] itemsIndegree = new int[n];

        int len = group.length;
        for (int i = 0; i < len; i++) {
            int currentGroup = group[i];
            for (int beforeItem : beforeItems.get(i)) {
                int beforeGroup = group[beforeItem];
                if (beforeGroup != currentGroup) {
                    groupAdj[beforeGroup].add(currentGroup);
                    groupsIndegree[currentGroup]++;
                }
            }
        }

        for (int i = 0; i < n; i++) {
            for (Integer item : beforeItems.get(i)) {
                itemAdj[item].add(i);
                itemsIndegree[i]++;
            }
        }

        // 第 4 步:得到组和项目的拓扑排序结果
        List<Integer> groupsList = topologicalSort(groupAdj, groupsIndegree, m);
        if (groupsList.size() == 0) {
            return new int[0];
        }
        List<Integer> itemsList = topologicalSort(itemAdj, itemsIndegree, n);
        if (itemsList.size() == 0) {
            return new int[0];
        }

        // 第 5 步:根据项目的拓扑排序结果,项目到组的多对一关系,建立组到项目的一对多关系
        // key:组,value:在同一组的项目列表
        Map<Integer, List<Integer>> groups2Items = new HashMap<>();
        for (Integer item : itemsList) {
            groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
        }

        // 第 6 步:把组的拓扑排序结果替换成为项目的拓扑排序结果
        List<Integer> res = new ArrayList<>();
        for (Integer groupId : groupsList) {
            List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
            res.addAll(items);
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }

    private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree, int n) {
        List<Integer> res = new ArrayList<>();
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }

        while (!queue.isEmpty()) {
            Integer front = queue.poll();
            res.add(front);
            for (int successor : adj[front]) {
                inDegree[successor]--;
                if (inDegree[successor] == 0) {
                    queue.offer(successor);
                }
            }
        }

        if (res.size() == n) {
            return res;
        }
        return new ArrayList<>();
    }
}
jerry9926 commented 2 years ago

思路

(mark)

代码

const sortItems = function (n, m, group, beforeItems) {
  const vertexs = new Map();
  const groupVertexs = new Map();
  let groupNo = m;
  for (let i = 0; i < n; i++) {
    vertexs.set(i, {
      neighbors: new Set(),
      indegree: 0,
    });
    if (group[i] === -1) {
      group[i] = groupNo++;
    }
    if (!groupVertexs.has(group[i])) {
      groupVertexs.set(group[i], {
        v: new Set(),
        neighbors: new Set(),
        indegree: 0,
      });
    }
    groupVertexs.get(group[i]).v.add(i);
  }

  for (let i = 0; i < n; i++) {
    for (const before of beforeItems[i]) {
      if (!vertexs.get(before).neighbors.has(i)) {
        vertexs.get(i).indegree += 1;
      }
      vertexs.get(before).neighbors.add(i);

      const groupOfBefore = group[before];
      if (groupOfBefore === group[i]) continue;
      if (!groupVertexs.get(groupOfBefore).neighbors.has(group[i])) {
        groupVertexs.get(group[i]).indegree += 1;
      }
      groupVertexs.get(groupOfBefore).neighbors.add(group[i]);
    }
  }

  const zeroGroup = [];
  for (const group of groupVertexs) {
    if (group[1].indegree === 0) {
      zeroGroup.push(group[0]);
    }
  }
  const result = [];
  let cntGroup = 0;
  let cntV = 0;
  const groupTotal = groupVertexs.size;

  while (zeroGroup.length) {
    const top = zeroGroup.pop();
    cntGroup += 1;
    const v = groupVertexs.get(top).v;
    const total = v.size;
    const zero = [];

    for (const i of v) {
      if (vertexs.get(i).indegree === 0) {
        zero.push(i);
      }
    }
    while (zero.length) {
      const it = zero.pop();
      result.push(it);
      for (const n of vertexs.get(it).neighbors) {
        vertexs.get(n).indegree -= 1;
        if (v.has(n) && vertexs.get(n).indegree === 0) {
          zero.push(n);
        }
      }
    }
    if (result.length - cntV !== total) {
      return [];
    }
    cntV = result.length;

    for (const groupneigbor of groupVertexs.get(top).neighbors) {
      groupVertexs.get(groupneigbor).indegree -= 1;
      if (groupVertexs.get(groupneigbor).indegree === 0) {
        zeroGroup.push(groupneigbor);
      }
    }
  }
  return cntGroup === groupTotal ? result : [];
};

复杂度分析

ZJP1483469269 commented 2 years ago

1203. 项目管理

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies/

前置知识

  • 图论
  • 拓扑排序
  • BFS & DFS

题目描述


公司共有 n 个项目和  m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。

group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。

请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:

同一小组的项目,排序后在列表中彼此相邻。
项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。

 

示例 1:

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
输出:[6,3,4,1,5,2,0,7]
示例 2:

输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]]
输出:[]
解释:与示例 1 大致相同,但是在排序后的列表中,4 必须放在 6 的前面。
 

提示:

1 <= m <= n <= 3 * 104
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] 不含重复元素

import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue;

public class Solution {

public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
    // 第 1 步:数据预处理,给没有归属于一个组的项目编上组号
    for (int i = 0; i < group.length; i++) {
        if (group[i] == -1) {
            group[i] = m;
            m++;
        }
    }

    // 第 2 步:实例化组和项目的邻接表
    List<Integer>[] groupAdj = new ArrayList[m];
    List<Integer>[] itemAdj = new ArrayList[n];
    for (int i = 0; i < m; i++) {
        groupAdj[i] = new ArrayList<>();
    }
    for (int i = 0; i < n; i++) {
        itemAdj[i] = new ArrayList<>();
    }

    // 第 3 步:建图和统计入度数组
    int[] groupsIndegree = new int[m];
    int[] itemsIndegree = new int[n];

    int len = group.length;
    for (int i = 0; i < len; i++) {
        int currentGroup = group[i];
        for (int beforeItem : beforeItems.get(i)) {
            int beforeGroup = group[beforeItem];
            if (beforeGroup != currentGroup) {
                groupAdj[beforeGroup].add(currentGroup);
                groupsIndegree[currentGroup]++;
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (Integer item : beforeItems.get(i)) {
            itemAdj[item].add(i);
            itemsIndegree[i]++;
        }
    }

    // 第 4 步:得到组和项目的拓扑排序结果
    List<Integer> groupsList = topologicalSort(groupAdj, groupsIndegree, m);
    if (groupsList.size() == 0) {
        return new int[0];
    }
    List<Integer> itemsList = topologicalSort(itemAdj, itemsIndegree, n);
    if (itemsList.size() == 0) {
        return new int[0];
    }

    // 第 5 步:根据项目的拓扑排序结果,项目到组的多对一关系,建立组到项目的一对多关系
    // key:组,value:在同一组的项目列表
    Map<Integer, List<Integer>> groups2Items = new HashMap<>();
    for (Integer item : itemsList) {
        groups2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
    }

    // 第 6 步:把组的拓扑排序结果替换成为项目的拓扑排序结果
    List<Integer> res = new ArrayList<>();
    for (Integer groupId : groupsList) {
        List<Integer> items = groups2Items.getOrDefault(groupId, new ArrayList<>());
        res.addAll(items);
    }
    return res.stream().mapToInt(Integer::valueOf).toArray();
}

private List<Integer> topologicalSort(List<Integer>[] adj, int[] inDegree, int n) {
    List<Integer> res = new ArrayList<>();
    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        if (inDegree[i] == 0) {
            queue.offer(i);
        }
    }

    while (!queue.isEmpty()) {
        Integer front = queue.poll();
        res.add(front);
        for (int successor : adj[front]) {
            inDegree[successor]--;
            if (inDegree[successor] == 0) {
                queue.offer(successor);
            }
        }
    }

    if (res.size() == n) {
        return res;
    }
    return new ArrayList<>();
}

}

hellowxwworld commented 2 years ago

思路

拓扑排序

代码

class Solution {
    vector<int> tpSort(vector<int>& nodes, unordered_map<int,unordered_set<int>> next, vector<int> inDegree) {
        vector<int> res;
        queue<int> q;
        for (auto& n : nodes) {
            if (inDegree[n] == 0)
                q.push(n);
        }
        while (!q.empty()) {
            int cur = q.front();
            q.pop();
            for (auto& v : next[cur]) {
                --inDegree[v];
                if (inDegree[v] == 0)
                    q.push(v);
            }
        }
        if (res.size() == nodes.size())
            return res;
        return {};
    }

    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        vector<int> res;
        unordered_map<int,unordered_set<int>> groupItems;
        int nextGroupId = m;
        for (int i = 0; i < n; ++i) {
            if (group[i] == -1) {
                group[i] = nextGroupId;
                nextGroupId += 1;
            }
        }
        unordered_map<int,unordered_set<int>> next;
        unordered_map<int, int> inDegree;
        for (int i = 0; i < n; ++i) {
            for (auto& j : beforeItems[i]) {
                if (group[i] != group[j])
                    continue;
                if (next[j].count(i) == 0) {
                    next[j].insert(i);
                    ++inDegree[i];
                }
            }
        }
        unordered_map<int, vector<int>> groupItemsOrdered;
        for (auto& x : groupItems) {
            int groupId = x.first;
            groupItemsOrdered[groupId] = tpSort(groupItems[groupId], next, inDegree);
            if (groupItemsOrdered[groupId].size() != groupItems[groupId].size())
                return {};
        }
        next.clear();
        inDegree.clear();
        for (int i = 0; i < n; ++i) {
            for (int j : beforeItems[i]) {
                if (group[i] == group[j])
                    continue;
                if (next[group[j]].count(group[i]) == 0) {
                    next[group[j]].insert(group[i]);
                    ++inDegree[group[i]];
                }
            }
        }
        unordered_set<int> groups;
        for (int i = 0; i < n; ++i) {
            groups.insert(group[i]);
        }
        vector<int> groupOrdered = tpSort(groups, next, inDegree);
        for (int groupId : groupOrdered) {
            for (auto node : groupItemsOrdered(groupId)) {
                res.push_back(node);
            }
        }
        return res;
    }
};