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] 不含重复元素

chen445 commented 2 years ago

思路

不会做,太难了

代码

class Solution:
    def topological_sort(self, graph, node, seen):
        result = []

        def recursive_helper(node):
            for neighbor in graph[node]:
                if neighbor not in seen:
                    seen.add(neighbor)
                    recursive_helper(neighbor)
            result.insert(0, node)

        recursive_helper(node)
        return result

    def sortItems(self, n: int, m: int, group: List[int],
                  beforeItems: List[List[int]]) -> List[int]:
        group_items_map = defaultdict(list)
        for i in range(n):
            if group[i] == -1:
                group[i] = m
                m += 1
            group_items_map[group[i]].append(i)

        graph_group = defaultdict(set)
        graph_item = {i: defaultdict(list) for i in range(m)}
        for item_after, before_items in enumerate(beforeItems):
            for item_before in before_items:
                group_before = group[item_before]
                group_after = group[item_after]

                if group_before != group_after:
                    graph_group[group_before].add(group_after)
                else:
                    graph_item[group_before][item_before].append(item_after)
        group_order = []
        seen = set()
        for group_id in range(m):
            if group_id not in seen:
                seen.add(group_id)
                group_order += self.topological_sort(graph_group, group_id, seen)
        final_result = []
        for group_id in group_order:
            seen = set()
            for item_id in group_items_map[group_id]:
                if item_id not in seen:
                    seen.add(item_id)
                    final_result += self.topological_sort(graph_item[group_id], item_id, seen)          
        return final_result

复杂度

Time: O(V+E)

Space: O(V+E)

kennyxcao commented 2 years ago

1203. Sort Items by Groups Respecting Dependencies

Intuition

Code

/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
const sortItems = function(n, m, group, beforeItems) {
  const groupToItems = new Map();
  let groupSize = m;
  for (let i = 0; i < n; i++) {
    if (group[i] === -1) group[i] = groupSize++; // assign new group to each ungrouped item
    if (!groupToItems.has(group[i])) groupToItems.set(group[i], []);
    groupToItems.get(group[i]).push(i);
  }

  const groupIn = Array(groupSize).fill(0); // in degree for each group
  const groupAdj = Array.from({length: groupSize}, () => new Set()); // adj list for each group
  const itemIn = Array(n).fill(0); // in degree for each item within same group
  const itemAdj = Array.from({length: n}, () => new Set()); // adj list for each item within same group

  for (let toIdx = 0; toIdx < beforeItems.length; toIdx++) {
    const toGroup = group[toIdx];
    for (const fromIdx of beforeItems[toIdx]) {
      const fromGroup = group[fromIdx];
      if (toGroup === fromGroup) {
        itemAdj[fromIdx].add(toIdx);
        itemIn[toIdx] += 1;
      } else if (!groupAdj[fromGroup].has(toGroup)) {
        groupAdj[fromGroup].add(toGroup);
        groupIn[toGroup] += 1;
      }
    }
  }

  // Verify group order
  const queue = [];
  const sortedGroup = [];
  for (let i = 0; i < groupSize; i++) {
    if (groupIn[i] === 0) {
      queue.push(i);
      sortedGroup.push(i);
    }
  }
  while (queue.length > 0) {
    const curr = queue.shift();
    for (const next of groupAdj[curr]) {
      if (--groupIn[next] === 0) {
        queue.push(next);
        sortedGroup.push(next);
      }
    }
  }
  if (sortedGroup.length !== groupSize) return []; // invalid

  // Verify item order
  const ans = [];
  for (const groupId of sortedGroup) {
    const items = groupToItems.get(groupId) || [];
    const queue = [];
    let count = 0;
    for (const item of items) {
      if (itemIn[item] === 0) {
        queue.push(item);
        ans.push(item);
        count += 1;
      }
    }
    while (queue.length > 0) {
      const curr = queue.shift();
      for (const next of itemAdj[curr]) {
        if (--itemIn[next] === 0) {
          queue.push(next);
          ans.push(next);
          count += 1;
        }
      }
    }
    if (count !== items.length) return []; // invalid
  }
  return ans;
};

Complexity Analysis

AruSeito 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;
};
RonghuanYou commented 2 years ago

思路

参考答案

class Solution:
    def tp_sort(self, items, indegree, neighbors):
        q = []
        order = []
        for item in items:
            if not indegree[item]:
                q.append(item)

        while q:
            cur = q.pop(0)
            order.append(cur)

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

    def sortItems(self, n: int, m: int, group: List[int], pres: List[List[int]]) -> List[int]:
        for project in range(n):
            if group[project] == -1:
                group[project] = m
                m += 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)

        group_queue = self.tp_sort([i for i in range(m)], group_indegree, group_neighbors)
        if len(group_queue) != m:
            return []

        ans = []
        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            
Venchyluo commented 2 years ago

贴上大神做法: https://www.youtube.com/watch?v=I4Vwkq7e2xE 之所以需要两次拓扑排序是因为 3 -> 5, 3->2 单次拓扑排序, 5,2 的顺序是可以swap的,但是这道题规定了也必须按组sort, 如果2,3 在group1, 5在group 2,那必须先完成3,2 再完成5.
大神新思路可以添加'dummy node',插入每一个group之间,这样保证3->5 这一行会变成3->dummy tail -> 5,这样只需要单次topology sort 就可以了。

class Solution:
    def topsort(self,queue,out_edges,in_degrees):
        ordering = []
        while queue:
            node = queue.popleft()
            ordering.append(node)
            for next_ in out_edges[node]:
                in_degrees[next_] -= 1
                if not in_degrees[next_]: 
                    queue.append(next_)
        return ordering

    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:     
        # create dummy head, dummy tail
        # group: -1 可以随便摆,不用按组顺序摆
        heads = [n + 2 * i for i in range(m)]
        tails = [n + 2 * i + 1 for i in range(m)]
        nodes = list(range(n + 2 * m)) # node + dummy.head, dummy.tail 
        out_edges = collections.defaultdict(list)
        in_degrees = collections.defaultdict(int)

        for item, (g,b) in enumerate(zip(group, beforeItems)):
            if g != -1:
                head,tail = heads[g],tails[g]
                out_edges[head].append(item)
                in_degrees[item] += 1
                out_edges[item].append(tail)
                in_degrees[tail] += 1
                # 这一步把每个group 中单独的item 连上head -> item -> tail

            for before in b:
                # from which group, to which group, 连接group
                from_,to_ = group[before],group[item]
                same_group = True if from_ == to_ else False

                # 这里要找到group, from 的group,也就是before的group.tail 连接到to_,item group.head            
                out_ = before if same_group or from_ == -1 else tails[from_]
                to_ = item if same_group or to_ == -1 else heads[to_]
                out_edges[out_].append(to_)
                in_degrees[to_] += 1

        # apply topological sort
        # first find the node, in_degree = 0
        starts =[node for node in nodes if not in_degrees[node]]   
        order = []       
        for start in starts:      
            order += self.topsort(collections.deque([start]),out_edges,in_degrees)
            #remember to remove dummy node

        order = [node for node in order if node < n]      
        # 如果有环,那么in_degrees[有环的任意点] 不可能为0,直接不执行                                     
        return order if len(order) == n else []

Time complexity: O((n+M)2)
Space complexity: O(n+M)
2)

BadCoderChou commented 2 years ago
class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer, Integer> indegreeMap = new HashMap();
        Map<Integer, List<Integer>> graph = new HashMap<>();
        Map<Integer, List<Integer>> groupMap = new HashMap<>();
        Map<Integer, Integer> grpIndegreeMap = new HashMap();
        Map<Integer, List<Integer>> grpGraph = new HashMap<>();

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

        int nextGrpId = m;
        for (int i = 0; i < group.length; i++) {
            if (group[i] == -1) {
                group[i] = nextGrpId;
                nextGrpId++;
            }
            if (groupMap.get(group[i]) == null) {
                groupMap.put(group[i], new ArrayList<>());
            }
            groupMap.get(group[i]).add(i);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] == group[id]) {
                        indegreeMap.put(i, indegreeMap.get(i) + 1);
                        graph.get(id).add(i);
                    }
                }
            }
        }

        Map<Integer, List<Integer>> sortedGroupItemMap = new HashMap<>();
        for (int grpId : groupMap.keySet()) {
            List<Integer> sortedGroupItem = topoLogicSort(groupMap.get(grpId), indegreeMap, graph);
            if (sortedGroupItem.size() == 0) {
                return new int[0];
            }
            sortedGroupItemMap.put(grpId, sortedGroupItem);
        }

        for (int grpId : group) {
            grpIndegreeMap.put(grpId, 0);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] != group[id]) {
                        grpIndegreeMap.put(group[i], grpIndegreeMap.getOrDefault(group[i], 0) + 1);
                        if (grpGraph.get(group[id]) == null) {
                            grpGraph.put(group[id], new ArrayList<>());
                        }
                        grpGraph.get(group[id]).add(group[i]);
                    }
                }
            }
        }
        List<Integer> grpIDs = new ArrayList<>(grpIndegreeMap.keySet());
        List<Integer> sortedGrp = topoLogicSort(grpIDs, grpIndegreeMap, grpGraph);
        List<Integer> tempList = new ArrayList<>();
        for (int grpId : sortedGrp) {
            tempList.addAll(sortedGroupItemMap.get(grpId));
        }
        int[] res = new int[tempList.size()];
        for (int i = 0; i < tempList.size(); i++) {
            res[i] = tempList.get(i);
        }
        return res;
    }

    private List<Integer> topoLogicSort(List<Integer> items, Map<Integer, Integer> indegreeMap, Map<Integer, List<Integer>> graph) {
        Queue<Integer> q = new LinkedList<>();
        for (int id : items) {
            if (indegreeMap.get(id) == 0) {
                q.add(id);
            }
        }
        List<Integer> tempList = new ArrayList<>();

        while (!q.isEmpty()) {
            int curId = q.poll();
            tempList.add(curId);
            List<Integer> nextIDs = graph.get(curId);
            if (nextIDs != null) {
                for (Integer nextID : nextIDs) {
                    indegreeMap.put(nextID, indegreeMap.get(nextID) - 1);
                    if (indegreeMap.get(nextID) == 0) {
                        q.add(nextID);
                    }
                }
            }
        }
        if (tempList.size() != items.size()) {
            return new ArrayList<>();
        }
        return tempList;
    }
}
flame0409 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<>();
    }
naivecoder-irl commented 2 years ago

思路

不是很懂,先打卡再学 2次拓扑排序

代码

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer, Integer> indegreeMap = new HashMap();
        Map<Integer, List<Integer>> graph = new HashMap<>();
        Map<Integer, List<Integer>> groupMap = new HashMap<>();
        Map<Integer, Integer> grpIndegreeMap = new HashMap();
        Map<Integer, List<Integer>> grpGraph = new HashMap<>();

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

        int nextGrpId = m;
        for (int i = 0; i < group.length; i++) {
            if (group[i] == -1) {
                group[i] = nextGrpId;
                nextGrpId++;
            }
            if (groupMap.get(group[i]) == null) {
                groupMap.put(group[i], new ArrayList<>());
            }
            groupMap.get(group[i]).add(i);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] == group[id]) {
                        indegreeMap.put(i, indegreeMap.get(i) + 1);
                        graph.get(id).add(i);
                    }
                }
            }
        }

        Map<Integer, List<Integer>> sortedGroupItemMap = new HashMap<>();
        for (int grpId : groupMap.keySet()) {
            List<Integer> sortedGroupItem = topoLogicSort(groupMap.get(grpId), indegreeMap, graph);
            if (sortedGroupItem.size() == 0) {
                return new int[0];
            }
            sortedGroupItemMap.put(grpId, sortedGroupItem);
        }

        for (int grpId : group) {
            grpIndegreeMap.put(grpId, 0);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] != group[id]) {
                        grpIndegreeMap.put(group[i], grpIndegreeMap.getOrDefault(group[i], 0) + 1);
                        if (grpGraph.get(group[id]) == null) {
                            grpGraph.put(group[id], new ArrayList<>());
                        }
                        grpGraph.get(group[id]).add(group[i]);
                    }
                }
            }
        }
        List<Integer> grpIDs = new ArrayList<>(grpIndegreeMap.keySet());
        List<Integer> sortedGrp = topoLogicSort(grpIDs, grpIndegreeMap, grpGraph);
        List<Integer> tempList = new ArrayList<>();
        for (int grpId : sortedGrp) {
            tempList.addAll(sortedGroupItemMap.get(grpId));
        }
        int[] res = new int[tempList.size()];
        for (int i = 0; i < tempList.size(); i++) {
            res[i] = tempList.get(i);
        }
        return res;
    }

    private List<Integer> topoLogicSort(List<Integer> items, Map<Integer, Integer> indegreeMap, Map<Integer, List<Integer>> graph) {
        Queue<Integer> q = new LinkedList<>();
        for (int id : items) {
            if (indegreeMap.get(id) == 0) {
                q.add(id);
            }
        }
        List<Integer> tempList = new ArrayList<>();

        while (!q.isEmpty()) {
            int curId = q.poll();
            tempList.add(curId);
            List<Integer> nextIDs = graph.get(curId);
            if (nextIDs != null) {
                for (Integer nextID : nextIDs) {
                    indegreeMap.put(nextID, indegreeMap.get(nextID) - 1);
                    if (indegreeMap.get(nextID) == 0) {
                        q.add(nextID);
                    }
                }
            }
        }
        if (tempList.size() != items.size()) {
            return new ArrayList<>();
        }
        return tempList;
    }
}

复杂度

时间复杂度 O(n)

空间复杂度 O(n)

Lydia61 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

复杂度分析

LareinaWei commented 2 years ago

Thinking

Referring to the solution. Do one topological sort on the groups, then do another topological sort on the projects taken by each groups.

Code

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

        max_gid = m
        for p in range(n):
            if group[p] == -1:
                group[p] = max_gid
                max_gid += 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 before in beforeItems[project]:
                if group[before] != group[project]:
                    group_indegree[group[project]] += 1
                    group_neighbors[group[before]].append(group[project])
                else:
                    project_indegree[project] += 1
                    project_neighbors[before].append(project)

        ans = []

        group_queue = self.topo_sort([i for i in range(max_gid)], group_indegree, group_neighbors)

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

        for group_id in group_queue:

            project_queue = self.topo_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 topo_sort(self, lst, indegree, neighbors):
        queue = collections.deque([])
        ans = []
        for i in lst:
            if not indegree[i]:
                queue.append(i)
        while queue:
            cur = queue.popleft()
            ans.append(cur)

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

        return ans

Complexity

Time complexity: O(V+E). Space complexity: O(V+E)

BpointA commented 2 years ago

思路

两次拓扑

Python3代码

class Solution:
    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        def topo(graph,degree,num):
            res=[]
            stk=[]
            for i in range(num):
                if degree[i]==0:
                    stk.append(i)
            while len(stk)>0:
                m=len(stk)
                for i in range(m):
                    p=stk.pop(0)
                    res.append(p)
                    for j in graph[p]:
                        degree[j]-=1
                        if degree[j]==0:
                            stk.append(j)
            if len(res)==num:
                return res
            return []
        g=m
        for i in range(n):
            if group[i]==-1:
                group[i]=g
                g+=1
        groupgraph=[[] for i in range(g)]
        groupdegree=[0]*g
        itemgraph=[[] for i in range(n)]
        itemdegree=[0]*n

        for i in range(n):
            if len(beforeItems[i])>0:
                for j in beforeItems[i]:
                    if group[j]!=group[i]:
                        groupgraph[group[j]].append(group[i])
                        groupdegree[group[i]]+=1
                    itemgraph[j].append(i)
                    itemdegree[i]+=1

        grp=topo(groupgraph,groupdegree,g)
        itm=topo(itemgraph,itemdegree,n)
        if len(grp)==0 or len(itm)==0:
            return []

        grp2itm=defaultdict(list)
        for i in range(n):
            idx=itm[i]
            gp=group[idx]
            grp2itm[gp].append(idx)
        res=[]
        for i in grp:
            res+=grp2itm[i]
        if len(res)==n:
            return res
        else:
            return []

复杂度

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

Awenbocc commented 2 years ago

思路

copy官方 (先打开,在学习)

代码

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
liuyangqiQAQ commented 2 years ago
class Solution {
    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<>());
        }

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

        int[] groupDegree = new int[n + m];
        int[] itemDegree = new int[n];

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

        int leftId = m;
        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<>();
        for (int item : items) {
            if (deg[item] == 0) {
                queue.offer(item);
            }
        }
        List<Integer> res = new ArrayList<>();
        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>();
    }
}
iambigchen commented 2 years ago

思路

先打卡

代码

var sortItems = function(n, m, group, beforeItems) {
    const grahG = [], degG = new Uint16Array(n + m), idsG = [], 
          grahI = [], degI = new Uint16Array(n), idsI = [], r = []
    for (let i = 0; i < n; i++) {
        if (group[i] === -1) {
            idsG[m] = m // 从组数起分配,避免重复
            group[i] = m++
        } else idsG[group[i]] = group[i]
        if (!idsI[group[i]]) idsI[group[i]] = [] // 同组项目,放入到一起
        idsI[group[i]].push(i)
    }
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < beforeItems[i].length; j++) {
            const itemI = beforeItems[i][j]
            if (group[i] === group[itemI]) {// 同组,收集 项目 依赖
                degI[i]++
                if (!grahI[itemI]) grahI[itemI] = []
                grahI[itemI].push(i)
            } else {// 不同组,收集 组 依赖
                degG[group[i]]++
                if (!grahG[group[itemI]]) grahG[group[itemI]] = []
                grahG[group[itemI]].push(group[i])
            }
        }
    }
    const idsGS = sort(idsG.filter(v => v !== void 0), grahG, degG) // 组排序
    if (idsGS.length === 0) return []
    for (let i = 0; i < idsGS.length; i++) {// 组有序,组内项目排序
        if (!idsI[idsGS[i]]) continue
        const idsIS = sort(idsI[idsGS[i]], grahI, degI)
        if (idsIS.length === 0) return []
        r.push(...idsIS)
    }
    return r
};
const sort = (ids, grah, deg) => {// 拓扑排序:id列表,图,入度
    const q = [], r = []
    let start = 0
    for (let i = 0; i < ids.length; i++) if (deg[ids[i]] === 0) q.push(ids[i])
    while (start < q.length) {
        const n = q[start++]
        r.push(n)
        if (!grah[n]) continue
        for (let i = 0; i < grah[n].length; i++) if (--deg[grah[n][i]] === 0) q.push(grah[n][i])
    }
    return r.length === ids.length ? r : []
}
learning-go123 commented 2 years ago

思路

代码

Go Code:


func topSort(graph [][]int, deg, items []int) (orders []int) {
    var q []int
    for _, i := range items {
        if deg[i] == 0 {
            q = append(q, i)
        }
    }
    for len(q) > 0 {
        from := q[0]
        q = q[1:]
        orders = append(orders, from)
        for _, to := range graph[from] {
            deg[to]--
            if deg[to] == 0 {
                q = append(q, to)
            }
        }
    }
    return
}

func sortItems(n, m int, group []int, beforeItems [][]int) (ans []int) {
    groupItems := make([][]int, m+n) 
    for i := range group {
        if group[i] == -1 {
            group[i] = m + i
        }
        groupItems[group[i]] = append(groupItems[group[i]], i)
    }

    groupGraph := make([][]int, m+n) 
    groupDegree := make([]int, m+n)
    itemGraph := make([][]int, n)
    itemDegree := make([]int, n)
    for cur, items := range beforeItems {
        curGroupID := group[cur]
        for _, pre := range items {
            preGroupID := group[pre]
            if preGroupID != curGroupID { 
                groupGraph[preGroupID] = append(groupGraph[preGroupID], curGroupID)
                groupDegree[curGroupID]++
            } else { 
                itemGraph[pre] = append(itemGraph[pre], cur)
                itemDegree[cur]++
            }
        }
    }

    items := make([]int, m+n)
    for i := range items {
        items[i] = i
    }
    groupOrders := topSort(groupGraph, groupDegree, items)
    if len(groupOrders) < len(items) {
        return nil
    }

    for _, groupID := range groupOrders {
        items := groupItems[groupID]
        orders := topSort(itemGraph, itemDegree, items)
        if len(orders) < len(items) {
            return nil
        }
        ans = append(ans, orders...)
    }
    return
}

复杂度分析

令 n 为数组长度。

ysy0707 commented 2 years ago

思路:图 拓扑排序 学习别人的题解 整理下思路会发现全部需要的结构为一个 组表、一个组依赖表、一项目依赖表、组依赖数量表、项目依赖数量表 各个表的建立目的: 组表:为了记录每个组都有哪些项目; 组依赖表:为了记录都有哪些组依赖了这个组; 项目依赖表:为了记录都有哪些项目依赖了这个项目; 组依赖数量表: 为了找到哪个组可以第一个开始(即没有依赖); 项目依赖数量表:为了找到哪个项目可以第一个开始(即没有依赖)。

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        // 创建组表:id是组号,value是组里面的项目号
        List<List<Integer>> groupList = new ArrayList<>();
        // 初始化
        for(int i = 0; i < m + n; i++)
            groupList.add(new ArrayList<Integer>());

        // 记录都有哪些组号
        List<Integer> groupIds = new ArrayList<>();
        // 初始化
        for(int i = 0; i < m + n; i++)
            groupIds.add(i);

        // 为-1的组分配新组,并给groupList加入数据
        int newGroup = m;
        for(int i = 0; i < n; i++){
            if(group[i] == -1)
                group[i] = newGroup++;
            groupList.get(group[i]).add(i);
        }

        // 组的依赖关系:id为组号,value为依赖该组(id)的其他组
        // 对于value来说,该组(id组)没完成之前value无法完成
        List<List<Integer>> groupGraph = new ArrayList<>();
        // 项目依赖,同上
        List<List<Integer>> itemGraph = new ArrayList<>();

        // 初始化
        for(int i = 0; i < m + n; i++)
            groupGraph.add(new ArrayList<Integer>());
        for(int i = 0; i < n; i++)
            itemGraph.add(new ArrayList<Integer>());

        // 只有上面的依赖,对于每个组来说,很方便找到他们的后续
        // 但是找到自由的组(自己不依赖其他人)却不方便
        // 记录组/项目的依赖数就是用来解决这个问题的
        int[] groupDegree = new int[m + n];
        int[] itemDegree = new int[n];

        // 遍历beforeItems,初始化上面四个值
        for(int i = 0; i < n; i++){
            // 当前项目的组号
            int nowGroup = group[i];
            // 当前项目所依赖的项目
            List<Integer> depended = beforeItems.get(i);
            for(int item : depended){
                // depended 所属的组
                int itemGroup = group[item];
                // 如果属于同一个组,则需要更新项目依赖,否则更新组依赖
                // 并记录依赖数
                if(nowGroup == itemGroup){
                    itemGraph.get(item).add(i);
                    itemDegree[i]++;
                }else{
                    groupGraph.get(itemGroup).add(nowGroup);
                    groupDegree[nowGroup]++;
                }
            }
        }

        // 好了,该记录的都记录完了
        // 接下来根据组记录更新下组的先后顺序
        List<Integer> sortedGroupList = sortByGraph(groupDegree, groupGraph, groupIds);
        if(sortedGroupList.size() == 0)
            return new int[0];

        // 知道组的先后顺序了,那么一个组一个组的更新每个组内的先后顺序就ok了
        int answer[] = new int[n];
        int index = 0;
        for(int sortedGroup : sortedGroupList){
            List<Integer> items = groupList.get(sortedGroup);
            List<Integer> sortedItems = sortByGraph(itemDegree, itemGraph, items);
            if(sortedItems.size() != items.size())
                return new int[0];

            for(int item : sortedItems)
                answer[index++] = item;
        }

        return answer;
    }

    private List<Integer> sortByGraph(int[] degree, 
                                      List<List<Integer>> graph, List<Integer> items){
        // 根据依赖数量、依赖关系 来更新 items 列表
        Queue<Integer> queue = new LinkedList<>();
        List<Integer> answer = new ArrayList<>();

        for(int item : items){
            if(degree[item] == 0)
                queue.offer(item);
        }

        while(!queue.isEmpty()){
            int tmp = queue.poll();
            answer.add(tmp);
            List<Integer> depend = graph.get(tmp);
            for(int item : depend){
                if(--degree[item] == 0)
                    queue.offer(item);
            }
        }

        if(answer.size() != items.size())
            return new ArrayList<Integer>();

        return answer;
    }
}
HouHao1998 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<>();
    }
}

复杂度

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

chun1hao 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;
};
lizzy-123 commented 2 years ago

思路

  1. 主要是拓扑排序,但是这个题不仅仅需要组内排序,还需要组间排序
  2. 对图不熟悉,直接看题解 代码

    class Solution {
    public:
    
    using UMIV = unordered_map<int,vector<int>>;  //顶点邻接边
    using UMII = unordered_map<int, int>;
    using VVI = vector<vector<int>>;
    using VI = vector<int>;
    UMIV sub;  //保存小组内可执行任务序列
    vector<int> sortItems(int n, int m, vector<int>& group, 
        vector<vector<int>>& beforeItems) {
      //如果任务不属于任何小组,说明该任务可以独属于一组,给该任务新建虚拟组
        //直接加入到sub中,不走后续组内拓扑排序
        for (int i = 0; i < n;i++)
        {
            if (group[i]==-1)
            {
                group[i] = m++;
            }
        }
    
        //保存一个小组内任务
        vector<vector<int>> gg(m);
        for (int i = 0; i < n;++i)
        {
            gg[group[i]].push_back(i);
        }
    
         //按照项目拓扑排序
        for (int i = 0; i < gg.size();++i)
        {
             if (gg[i].size()==1)
             {
                 int itemId = gg[i][0];
                 sub[group[itemId]] = { itemId };
                 continue;
             }
    
             bool f = topoSort(i, gg[i], beforeItems);
             if (f==false)
             {
                 return{};
             }
        }
    
        //按照小组排序
        VI groups;
        for (int i = 0; i < m;++i)
        {
            groups.push_back(i);
        }
    
        VVI newbefore(m);
        for (int i = 0; i < n;i++)
        {
            for (int j = 0; j < beforeItems[i].size();j++)
            {
                if (group[i]!=beforeItems[i][j])
                {
                    newbefore[i].push_back(beforeItems[i][j]);
                }
            }
        }
    
         if (!topoSort(m,groups,newbefore))
         {
             return{};
         }
    
         VI rs;
         for (auto v:sub[m])
         {
             rs.insert(rs.end(), sub[v].begin(), sub[v].end());
         }
    
         return rs;
    }
    
    bool topoSort(int gpid,VI& group,VVI& before)
    {
        int n = group.size();//顶点个数
        UMII degree;//每个顶点入度
        UMIV g; //图
    
        for (int i = 0; i < n;i++)
        {
            degree[group[i]] = 0;
            g[group[i]] = vector<int>();
        }
    
        for (int i = 0; i < n;i++)
        {
            int idx = group[i];
            for (int j = 0; j < before[idx].size(); ++j)
            {
                int last = before[idx][j];
                if (degree.find(last)==degree.end())
                {
                    continue;
                }
    
                g[last].push_back(idx);
                degree[idx]++;
            }
        }
    
        VI res;
        queue<int> q;
        for (int i = 0; i < n;i++)
        {
            if (degree[group[i]] == 0)
            {
                q.push(group[i]);
            }
        }
    
        while (!q.empty())
        {
            int cur = q.front();
            q.pop();
            res.push_back(cur);
            if (g[cur].size()!=0)
            {
                for (int i = 0; i < g[cur].size();++i)
                {
                    if (--degree[g[cur][i]]==0)
                    {
                        q.push(g[cur][i]);
                    }
                }
            }
        }
    
        sub[gpid] = move(res);
        return sub[gpid].size() == n;
    }
    };
shibingchao123 commented 2 years ago

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 : []; };

Cartie-ZhouMo 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
jinmenghan 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<>();
    }
}
naomiwufzz commented 2 years ago

思路:双层拓扑排序

学习官方题解。首先,要掌握拓扑排序,拓扑排序适用于一个任务中有多个子任务,子任务可能有优先级(但不能互相交叉,能够满足有向无环图)的情况。这题项目管理,说明有的项目要在一些项目之前,其实就很好的满足拓扑排序的定义,但这题,除了项目有顺序,组也有顺序,这个就需要建立双层的图关系,也就是项目图关系和组图关系。项目图关系是题目给出的,那么组图关系是什么呢?其实就是当组里面项目有优先关系的时候,组的优先关系就定了。最后就是没有分组的怎么办呢?没有分组的直接自增地给一个组即可。官方思路非常清晰,但是这题知识点比较多,不是太好想

代码

from collections import deque, defaultdict
class Solution:
    def tp_sort(self, items, indegree, neighbors) -> List[int]:
        res = []
        q = deque()
        for item in items:
            if not indegree[item]:
                q.append(item)
        while q:
            cur = q.popleft()
            res.append(cur)
            for neighbor in neighbors[cur]:
                indegree[neighbor] -= 1
                if not indegree[neighbor]:
                    q.append(neighbor)
        return res

    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        max_group_id = m
        # 无组的项目,从m开始自增地+1分配给一个组
        for project in range(n):
            if group[project] == -1:
                group[project] = max_group_id
                max_group_id += 1

        # 项目关系
        project_indegree = defaultdict(int)
        project_neighbors = defaultdict(list)

        # 小组关系
        group_indegree = defaultdict(int)
        group_neighbors = defaultdict(list)

        # 组和项目的对应关系
        group_projects = defaultdict(list)

        for project in range(n):
            group_projects[group[project]].append(project)
            # 每个项目都可能有before
            for pre in beforeItems[project]:
                # 如果pre和project不在一个组,说明pre的组要在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)
        res = []
        # 对组拓扑排序,因为无组的都分配了一个组,所以现在有max_group_id个组
        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 []
            res += project_queue
        return res

复杂度分析

yj9676 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<>();
    }
last-Battle commented 2 years ago

思路

关键点

代码

C++ Code:


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;
        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;
    }
};
HuijunXu commented 2 years ago

代码

抄作业

class Solution:
    def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]:
        dep = [0] * n # 项目依赖数,为0时,该项目才能执行
        group_dep = [0] * m # 小组依赖数(小组管理的所有项目的依赖总数),为0时,该小组才能开始执行项目
        other_queue = [] # 暂无分组的子队列
        group_queue = [[] for _ in range(m)] # 各组的子队列
        queue_queue = [] # 组队列,控制组与组之间的执行顺序,因为每个组的项目要一起执行
        dependedBy = [[] for _ in range(n)] # 项目间的具体依赖
        # 初始化依赖数、小组依赖数、项目间的具体依赖
        for i, l in enumerate(beforeItems):
            dep[i] = len(l)
            gi = group[i] # 项目i对应的组号
            # 项目i没有依赖的项目,直接入队,准备执行
            if not l:
                (group_queue[gi] if gi != -1 else other_queue).append(i)
            # 遍历项目i的依赖项目
            for j in l:
                # j是i的依赖
                dependedBy[j].append(i)
                # 项目i有已知分组,且没有形成了环,小组依赖数增加1
                if gi != -1 and group[j] != gi:
                    group_dep[gi] += 1
        # 初始化组队列,组队列的每个元素就是对应小组的子队列
        for i, gd in enumerate(group_dep):
            if not gd:
                queue_queue.append(group_queue[i])
        result = [] # 存储结果的列表
        while True:
            # 取出当前执行任务的小组,优先是有分组的
            if queue_queue:
                current_queue = queue_queue.pop()
            elif other_queue:
                current_queue = other_queue
            else:
                # 不能形成有效的序列,直接返回[]
                if len(result) < n:
                    return []
                # 可以形成有效的序列,返回result
                else:
                    return result
            # 遍历当前组的子队列
            while current_queue:
                p = current_queue.pop() # 项目p
                result.append(p) # 项目p执行
                gp = group[p] # 执行项目p的组号
                # 遍历所有依赖p的项目(即p执行了,这些才能执行)
                for i in dependedBy[p]:
                    gi = group[i] # 项目i的组号
                    dep[i] -= 1 # 依赖数减一
                    # 没有依赖了,可以入队准备执行
                    if not dep[i]:
                        (group_queue[gi] if gi != -1 else other_queue).append(i)
                    # 项目i有已知分组,且没有形成了环
                    if gi != -1 and gi != gp:
                        group_dep[gi] -= 1 # 组依赖数减一
                        # 没有依赖了,该组可以开始执行
                        if not group_dep[gi]:
                            queue_queue.append(group_queue[gi])
nonevsnull commented 2 years ago

思路

NONE

代码

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(V+E) O(V+E)

HWFrankFung commented 2 years ago

Codes

var sortItems = function(n, m, group, beforeItems) {
    let ans = [];
    const groupGraph = new Map, itemGraph = new Map(), groupItemMap = new Map();
    let maxGroupNum = m;
    for(let i = 0; i < n; i++) {
        if(group[i] === -1) {
            group[i] = maxGroupNum; maxGroupNum++;
        }
    }
    // groupItemMap
    for(let i = 0; i < n; i++) {
        if(!groupItemMap.has(group[i])) groupItemMap.set(group[i], []);
        groupItemMap.get(group[i]).push(i);
    }
    for(let i = 0; i < beforeItems.length; i++) {
        let afterItemId = i, beforeItemIds = beforeItems[i];
        for(let beforeItemId of beforeItemIds) {
            let beforeGroupId = group[beforeItemId], afterGroupId = group[afterItemId];
            if(beforeGroupId !== afterGroupId) { 
                if(!groupGraph.has(beforeGroupId)) groupGraph.set(beforeGroupId, []);
                groupGraph.get(beforeGroupId).push(afterGroupId);
            } else { 
                if(!itemGraph.has(beforeGroupId)) itemGraph.set(beforeGroupId, new Map());
                if(!itemGraph.get(beforeGroupId).has(beforeItemId)) itemGraph.get(beforeGroupId).set(beforeItemId, []);
                itemGraph.get(beforeGroupId).get(beforeItemId).push(afterItemId);
            }
        }
    }
    let groupDegrees = Array(maxGroupNum).fill(0);
    groupGraph.forEach((values) => values.forEach((value) => groupDegrees[value]++));
    let sortedGroupList = topoSort(groupDegrees, groupGraph, maxGroupNum);
    let items, itemDegrees, sortedItemList;
    for(let groupId of sortedGroupList) {
        if(!groupItemMap.has(groupId)) continue; 
        items = groupItemMap.get(groupId);
        if(!itemGraph.has(groupId)) { 
            ans = ans.concat(items);
        } else { 
            itemDegrees = Array(n).fill(-1);
            items.forEach((item) => itemDegrees[item] = 0); // 存在的item置为0
            itemGraph.get(groupId).forEach((values) => values.forEach((value) => itemDegrees[value]++));
            sortedItemList = topoSort(itemDegrees, itemGraph.get(groupId), items.length);
            if(sortedItemList.length === 0) return []; 
            ans = ans.concat(sortedItemList);
        }
    }
    return ans;
};

var topoSort = function(degrees, graph, len) {
    const ans = [], q = [];
    for(let i = 0; i < degrees.length; i++) {
        if(degrees[i] === 0) q.push(i);
    }
    while(q.length !== 0) {
        let item = q.shift(); ans.push(item);
        if(graph.has(item)) {
            for(let child of graph.get(item)) {
                degrees[child]--;
                if(degrees[child] === 0) q.push(child);
            }
        }
    }
    return ans.length === len ? ans : []; 
}
JAYWX 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
lxy030988 commented 2 years ago

思路

代码 js

/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function (n, m, group, beforeItems) {
  const grahG = [],
    degG = new Uint16Array(n + m),
    idsG = [],
    grahI = [],
    degI = new Uint16Array(n),
    idsI = [],
    r = []
  for (let i = 0; i < n; i++) {
    if (group[i] === -1) {
      idsG[m] = m // 从组数起分配,避免重复
      group[i] = m++
    } else idsG[group[i]] = group[i]
    if (!idsI[group[i]]) idsI[group[i]] = [] // 同组项目,放入到一起
    idsI[group[i]].push(i)
  }
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < beforeItems[i].length; j++) {
      const itemI = beforeItems[i][j]
      if (group[i] === group[itemI]) {
        // 同组,收集 项目 依赖
        degI[i]++
        if (!grahI[itemI]) grahI[itemI] = []
        grahI[itemI].push(i)
      } else {
        // 不同组,收集 组 依赖
        degG[group[i]]++
        if (!grahG[group[itemI]]) grahG[group[itemI]] = []
        grahG[group[itemI]].push(group[i])
      }
    }
  }
  const idsGS = sort(
    idsG.filter((v) => v !== void 0),
    grahG,
    degG
  ) // 组排序
  if (idsGS.length === 0) return []
  for (let i = 0; i < idsGS.length; i++) {
    // 组有序,组内项目排序
    if (!idsI[idsGS[i]]) continue
    const idsIS = sort(idsI[idsGS[i]], grahI, degI)
    if (idsIS.length === 0) return []
    r.push(...idsIS)
  }
  return r
}
const sort = (ids, grah, deg) => {
  // 拓扑排序:id列表,图,入度
  const q = [],
    r = []
  let start = 0
  for (let i = 0; i < ids.length; i++) if (deg[ids[i]] === 0) q.push(ids[i])
  while (start < q.length) {
    const n = q[start++]
    r.push(n)
    if (!grah[n]) continue
    for (let i = 0; i < grah[n].length; i++)
      if (--deg[grah[n][i]] === 0) q.push(grah[n][i])
  }
  return r.length === ids.length ? r : []
}

复杂度分析

xingkong1994 commented 2 years ago
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;
        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;
    }
};
asterqian commented 2 years ago

思路

两次拓扑排序,第一次先排各个组内的项目,第二次排组,具体看注释

代码

class Solution {
public:
    unordered_map<int, vector<int>> map; // group -> projects
    // projs指的是在第groupId个组当中,他有的项目。projs[0][1][2]即为第1,2,3个项目
    bool topoSort(int groupId, vector<int>& projs, vector<vector<int>>& beforeItems) {
        int n = projs.size();
        unordered_map<int, int> indegree; // degree
        unordered_map<int, vector<int>> neighbors; // graph
        // initialization
        for (int i = 0; i < n; ++i) {
            indegree[projs[i]] = 0;
            neighbors[projs[i]] = vector<int>();
        }
        for (int i = 0; i < n; ++i) {
            int idx = projs[i];
            if (beforeItems[idx].size() == 0) continue;
            for (int j = 0; j < beforeItems[idx].size(); ++j) {
                // 
                if (indegree.find(beforeItems[idx][j]) == indegree.end()) continue;
                indegree[idx]++;
                // proj needed be done first -> others
                neighbors[beforeItems[idx][j]].push_back(idx);
            }
        }
        vector<int> ans;
        queue<int> q;
        // 入度为0的(不需要前置的)先加入队列
        for (int i = 0; i < n; ++i) {
            if (indegree[projs[i]] == 0) {
                q.push(projs[i]);
            }
        }
        while (!q.empty()) {
            int curr = q.front();
            q.pop();
            ans.push_back(curr);
            if (neighbors[curr].size() != 0) {
                for (auto& neighbor: neighbors[curr]) {
                    if (--indegree[neighbor] == 0) {
                        q.push(neighbor);
                    }
                }
            }
        }
        map[groupId] = move(ans);
        return map[groupId].size() == n;
    }
    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        // 给没有分组的小组加入一个新的组
        for (int i = 0; i < n; i++) {
            if (group[i] == -1) {
                group[i] = m++;
            }
        }
        // 在拓扑排序之前保存各个小组有的项目
        vector<vector<int>> projInGroup(m); // groupId -> projectIds
        for (int i = 0; i < n; ++i) {
            projInGroup[group[i]].push_back(i);
        }

        // 根据项目拓扑排序
        for (int i = 0; i < projInGroup.size(); ++i) {
            // 如果只有一个项目,不需要拓扑
            if (projInGroup[i].size() == 1) {
                int projId = projInGroup[i][0]; // group i中的唯一一个project id
                map[group[projId]].push_back(projId);
                continue;
            }
            bool hasResult = topoSort(i, projInGroup[i], beforeItems);
            if (!hasResult) return {};
        }

        // 根据小组排序
        vector<int> team;
        // 总共原有的组(不包括未被分组的),用来做排序
        for (int i = 0; i < m; ++i) team.push_back(i);
        vector<vector<int>> newBefore(m);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < beforeItems[i].size(); ++j) {
                int prev = group[beforeItems[i][j]];
                // 当前项目的小组的前置项目小组和当前小组如果不一致(跨小组前置要求)
                if (prev != group[i]) {
                    // 把这个其他组的项目的组号加入到vector当中
                    // 例如组3有一个项目需要先完成组0;idx[3] -> {0},是一个beforeGroup的概念
                    newBefore[group[i]].push_back(prev);
                }
            }   
        }
        if (!topoSort(m, team, newBefore)) return {};
        vector<int> ans;
        for (auto v: map[m]) {
            ans.insert(ans.end(), map[v].begin(), map[v].end());
        }
        return ans;
    }
};

复杂度分析

时间复杂度: O(V+E) ?need further consideration
空间复杂度: O(V+E) ?need further consideration
machuangmr commented 2 years ago

题目:1203. 项目管理

思路

Serena9 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
CruiseYuGH commented 2 years ago

"" 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:
            group[task] = max_group_id
            max_group_id += 1

    task_indegree = [0] * n    
    group_indegree = [0] * max_group_id
    task_neighbors = [[] for _ in range(n)]
    group_neighbors = [[] for _ in range(max_group_id)]
    group_to_tasks = [[] for _ in range(max_group_id)]

    for task in range(n):
        group_to_tasks[group[task]].append(task)

        for prerequisite in beforeItems[task]:

            # 判断相关联的两个项目是否属于同一组
            if group[prerequisite] != group[task]:

                # 不是同组,给小组建图
                group_indegree[group[task]] += 1
                group_neighbors[group[prerequisite]].append(group[task])
            else:
                # 同组,给组内项目建图
                task_indegree[task] += 1
                task_neighbors[prerequisite].append(task)

    res = []

    # 得到小组的访问顺序
    group_queue = self.topological_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:
        # 得到每组项目的访问顺序
        task_queue = self.topological_sort(group_to_tasks[group_id],task_indegree,task_neighbors)

        if len(task_queue) != len(group_to_tasks[group_id]):
            return []
        res += task_queue

    return res

""

Forschers commented 2 years ago

class Solution: def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]: dep = [0] n # 项目依赖数,为0时,该项目才能执行 group_dep = [0] m # 小组依赖数(小组管理的所有项目的依赖总数),为0时,该小组才能开始执行项目 other_queue = [] # 暂无分组的子队列 groupqueue = [[] for in range(m)] # 各组的子队列 queuequeue = [] # 组队列,控制组与组之间的执行顺序,因为每个组的项目要一起执行 dependedBy = [[] for in range(n)] # 项目间的具体依赖

初始化依赖数、小组依赖数、项目间的具体依赖

    for i, l in enumerate(beforeItems):
        dep[i] = len(l)
        gi = group[i] # 项目i对应的组号
        # 项目i没有依赖的项目,直接入队,准备执行
        if not l:
            (group_queue[gi] if gi != -1 else other_queue).append(i)
        # 遍历项目i的依赖项目
        for j in l:
            # j是i的依赖
            dependedBy[j].append(i)
            # 项目i有已知分组,且没有形成了环,小组依赖数增加1
            if gi != -1 and group[j] != gi:
                group_dep[gi] += 1
    # 初始化组队列,组队列的每个元素就是对应小组的子队列
    for i, gd in enumerate(group_dep):
        if not gd:
            queue_queue.append(group_queue[i])
    result = [] # 存储结果的列表
    while True:
        # 取出当前执行任务的小组,优先是有分组的
        if queue_queue:
            current_queue = queue_queue.pop()
        elif other_queue:
            current_queue = other_queue
        else:
            # 不能形成有效的序列,直接返回[]
            if len(result) < n:
                return []
            # 可以形成有效的序列,返回result
            else:
                return result
        # 遍历当前组的子队列
        while current_queue:
            p = current_queue.pop() # 项目p
            result.append(p) # 项目p执行
            gp = group[p] # 执行项目p的组号
            # 遍历所有依赖p的项目(即p执行了,这些才能执行)
            for i in dependedBy[p]:
                gi = group[i] # 项目i的组号
                dep[i] -= 1 # 依赖数减一
                # 没有依赖了,可以入队准备执行
                if not dep[i]:
                    (group_queue[gi] if gi != -1 else other_queue).append(i)
                # 项目i有已知分组,且没有形成了环
                if gi != -1 and gi != gp:
                    group_dep[gi] -= 1 # 组依赖数减一
                    # 没有依赖了,该组可以开始执行
                    if not group_dep[gi]:
                        queue_queue.append(group_queue[gi])
KennethAlgol commented 2 years ago

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer, Integer> groupIndegree = new HashMap<>(); // <GroupId: Indegree of groupId>
        Map<Integer, Integer> memberIndegree = new HashMap<>(); /// <MemberId: Indegree of memberId>
        Map<Integer, List<Integer>> groupMember = new HashMap<>(); // <GroupId: List of memberIds in the group>
        Map<Integer, Set<Integer>> groupGraph = new HashMap<>(); // <GroupId: List of groupIds that have to go after>
        Map<Integer, List<Integer>> memberGraph = new HashMap<>(); // <MemberId: List of memberIds that have to go after>

        // if a group is -1 change it to -i-1;
        for (int i = 0; i < n; i++) {
            if (group[i] < 0) {
                group[i] = - i - 1;
            }
            groupIndegree.put(group[i], 0);
            groupGraph.putIfAbsent(group[i], new HashSet<>());
            groupMember.putIfAbsent(group[i], new ArrayList<>());
            groupMember.get(group[i]).add(i);
        }

        for (int i = 0; i < n; i++) {
            memberGraph.putIfAbsent(i, new ArrayList<>());
            memberIndegree.put(i, beforeItems.get(i).size());
            for (int before : beforeItems.get(i)) {
                memberGraph.putIfAbsent(before, new ArrayList<>());    
                memberGraph.get(before).add(i);
                if (group[before] != group[i] && groupGraph.get(group[before]).add(group[i])) {
                    groupIndegree.put(group[i], groupIndegree.get(group[i]) + 1);
                }
            }
        }

        // topological sort on the group first. 
        Deque<Integer> groupQ = new ArrayDeque<>();
        Deque<Integer> memberQ = new ArrayDeque<>();

        for (int g : groupIndegree.keySet()) {
            if (groupIndegree.get(g) == 0) groupQ.addLast(g);
        }

        int[] res = new int[n];
        int k = 0;

        while (!groupQ.isEmpty()) {
            int curGroup = groupQ.removeFirst();

             // For each group, topological sort the members in the group.
            for (int member : groupMember.get(curGroup)) {
                if (memberIndegree.get(member) == 0) {
                    memberQ.add(member);
                }
            }
            int count = 0;
            while (!memberQ.isEmpty()) {
                int member = memberQ.removeFirst();
                res[k++] = member;
                count++;
                for (int nextMember : memberGraph.get(member)) {
                    memberIndegree.put(nextMember, memberIndegree.get(nextMember) - 1);
                    if (group[nextMember] == g && memberIndegree.get(nextMember) == 0) {
                        memberQ.addLast(nextMember);
                    }
                }
            }
            if (count < groupMember.get(curGroup).size()) break;

            for (int nextGroup : groupGraph.get(curGroup)) {
                groupIndegree.put(nextGroup, groupIndegree.get(nextGroup) - 1);
                if (groupIndegree.get(nextGroup) == 0) groupQ.addLast(nextGroup);
            }
        }

        if (k < n) return new int[0];
        return res;
    }
}
carterrr commented 2 years ago

/**

HydenLiu commented 2 years ago

js

/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function(n, m, group, beforeItems) {
    const grahG = [], degG = new Uint16Array(n + m), idsG = [], 
          grahI = [], degI = new Uint16Array(n), idsI = [], r = []
    for (let i = 0; i < n; i++) {
        if (group[i] === -1) {
            idsG[m] = m 
            group[i] = m++
        } else idsG[group[i]] = group[i]
        if (!idsI[group[i]]) idsI[group[i]] = [] 
        idsI[group[i]].push(i)
    }
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < beforeItems[i].length; j++) {
            const itemI = beforeItems[i][j]
            if (group[i] === group[itemI]) {
                degI[i]++
                if (!grahI[itemI]) grahI[itemI] = []
                grahI[itemI].push(i)
            } else {
                degG[group[i]]++
                if (!grahG[group[itemI]]) grahG[group[itemI]] = []
                grahG[group[itemI]].push(group[i])
            }
        }
    }
    // 组排序
    const idsGS = sort(idsG.filter(v => v !== void 0), grahG, degG) 
    if (idsGS.length === 0) return []
    for (let i = 0; i < idsGS.length; i++) {
        if (!idsI[idsGS[i]]) continue
        const idsIS = sort(idsI[idsGS[i]], grahI, degI)
        if (idsIS.length === 0) return []
        r.push(...idsIS)
    }
    return r
};

// 排序
const sort = (ids, grah, deg) => {
    const q = [], r = []
    let start = 0
    for (let i = 0; i < ids.length; i++) if (deg[ids[i]] === 0) q.push(ids[i])
    while (start < q.length) {
        const n = q[start++]
        r.push(n)
        if (!grah[n]) continue
        for (let i = 0; i < grah[n].length; i++) if (--deg[grah[n][i]] === 0) q.push(grah[n][i])
    }
    return r.length === ids.length ? r : []
}
yibenxiao commented 2 years ago

【Day 31】1203. 项目管理

思路

放弃治疗

代码

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

复杂度

时间复杂度:O(M+N)

空间复杂度:O(M+N)

vincentLW commented 2 years ago
/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
var sortItems = function(n, m, group, beforeItems) {
    const grahG = [], degG = new Uint16Array(n + m), idsG = [], 
          grahI = [], degI = new Uint16Array(n), idsI = [], r = []
    for (let i = 0; i < n; i++) {
        if (group[i] === -1) {
            idsG[m] = m 
            group[i] = m++
        } else idsG[group[i]] = group[i]
        if (!idsI[group[i]]) idsI[group[i]] = [] 
        idsI[group[i]].push(i)
    }
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < beforeItems[i].length; j++) {
            const itemI = beforeItems[i][j]
            if (group[i] === group[itemI]) {
                degI[i]++
                if (!grahI[itemI]) grahI[itemI] = []
                grahI[itemI].push(i)
            } else {
                degG[group[i]]++
                if (!grahG[group[itemI]]) grahG[group[itemI]] = []
                grahG[group[itemI]].push(group[i])
            }
        }
    }
    // 组排序
    const idsGS = sort(idsG.filter(v => v !== void 0), grahG, degG) 
    if (idsGS.length === 0) return []
    for (let i = 0; i < idsGS.length; i++) {
        if (!idsI[idsGS[i]]) continue
        const idsIS = sort(idsI[idsGS[i]], grahI, degI)
        if (idsIS.length === 0) return []
        r.push(...idsIS)
    }
    return r
};

const sort = (ids, grah, deg) => {
    const q = [], r = []
    let start = 0
    for (let i = 0; i < ids.length; i++) if (deg[ids[i]] === 0) q.push(ids[i])
    while (start < q.length) {
        const n = q[start++]
        r.push(n)
        if (!grah[n]) continue
        for (let i = 0; i < grah[n].length; i++) if (--deg[grah[n][i]] === 0) q.push(grah[n][i])
    }
    return r.length === ids.length ? r : []
}
KennethAlgol commented 2 years ago

class Solution {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        Map<Integer, Integer> indegreeMap = new HashMap();
        Map<Integer, List<Integer>> graph = new HashMap<>();
        Map<Integer, List<Integer>> groupMap = new HashMap<>();
        Map<Integer, Integer> grpIndegreeMap = new HashMap();
        Map<Integer, List<Integer>> grpGraph = new HashMap<>();

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

        int nextGrpId = m;
        for (int i = 0; i < group.length; i++) {
            if (group[i] == -1) {
                group[i] = nextGrpId;
                nextGrpId++;
            }
            if (groupMap.get(group[i]) == null) {
                groupMap.put(group[i], new ArrayList<>());
            }
            groupMap.get(group[i]).add(i);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] == group[id]) {
                        indegreeMap.put(i, indegreeMap.get(i) + 1);
                        graph.get(id).add(i);
                    }
                }
            }
        }

        Map<Integer, List<Integer>> sortedGroupItemMap = new HashMap<>();
        for (int grpId : groupMap.keySet()) {
            List<Integer> sortedGroupItem = topoLogicSort(groupMap.get(grpId), indegreeMap, graph);
            if (sortedGroupItem.size() == 0) {
                return new int[0];
            }
            sortedGroupItemMap.put(grpId, sortedGroupItem);
        }

        for (int grpId : group) {
            grpIndegreeMap.put(grpId, 0);
        }

        for (int i = 0; i < n; i++) {
            List<Integer> beforeList = beforeItems.get(i);
            if (beforeList != null) {
                for (Integer id : beforeList) {
                    if (group[i] != group[id]) {
                        grpIndegreeMap.put(group[i], grpIndegreeMap.getOrDefault(group[i], 0) + 1);
                        if (grpGraph.get(group[id]) == null) {
                            grpGraph.put(group[id], new ArrayList<>());
                        }
                        grpGraph.get(group[id]).add(group[i]);
                    }
                }
            }
        }
        List<Integer> grpIDs = new ArrayList<>(grpIndegreeMap.keySet());
        List<Integer> sortedGrp = topoLogicSort(grpIDs, grpIndegreeMap, grpGraph);
        List<Integer> tempList = new ArrayList<>();
        for (int grpId : sortedGrp) {
            tempList.addAll(sortedGroupItemMap.get(grpId));
        }
        int[] res = new int[tempList.size()];
        for (int i = 0; i < tempList.size(); i++) {
            res[i] = tempList.get(i);
        }
        return res;
    }

    private List<Integer> topoLogicSort(List<Integer> items, Map<Integer, Integer> indegreeMap, Map<Integer, List<Integer>> graph) {
        Queue<Integer> q = new LinkedList<>();
        for (int id : items) {
            if (indegreeMap.get(id) == 0) {
                q.add(id);
            }
        }
        List<Integer> tempList = new ArrayList<>();

        while (!q.isEmpty()) {
            int curId = q.poll();
            tempList.add(curId);
            List<Integer> nextIDs = graph.get(curId);
            if (nextIDs != null) {
                for (Integer nextID : nextIDs) {
                    indegreeMap.put(nextID, indegreeMap.get(nextID) - 1);
                    if (indegreeMap.get(nextID) == 0) {
                        q.add(nextID);
                    }
                }
            }
        }
        if (tempList.size() != items.size()) {
            return new ArrayList<>();
        }
        return tempList;
    }
}
JinhMa commented 2 years ago

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) {
    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];
    }

    // 第 5 步:根据项目的拓扑排序结果,项目到组的多对一关系,建立组到项目的一对多关系
    // key:组,value:在同一组的项目列表
    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<>();
}

}

ymwang-2020 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
chenming-cao commented 2 years ago

解题思路

拓扑排序,BFS。分别对组和项目进行拓扑排序。然后根据组和项目的一对多对应关系,合并成最终的拓扑排序结果。

代码

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

        // 2. initiate adjacent list for group and project
        List<Integer>[] groupGraph = new ArrayList[m];
        List<Integer>[] itemGraph = new ArrayList[n];
        for (int i = 0; i < m; i++) groupGraph[i] = new ArrayList<>();
        for (int i = 0; i < n; i++) itemGraph[i] = new ArrayList<>();

        // 3. build graphs and get indegree information
        int[] groupIndegree = new int[m];
        int[] itemIndegree = new int[n];

        int len = group.length; // m changed
        for (int i = 0; i < len; i++) {
            int curGroup = group[i];
            for (int beforeItem: beforeItems.get(i)) {
                int beforeGroup = group[beforeItem];
                if (beforeGroup != curGroup) {
                    groupGraph[beforeGroup].add(curGroup);
                    groupIndegree[curGroup]++;
                }
            }
        } 

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

        // 4. Get topo sort results
        List<Integer> groupList = tpsort(groupGraph, groupIndegree, m);
        if (groupList.size() == 0) return new int[0];
        List<Integer> itemList = tpsort(itemGraph, itemIndegree, n);
        if (itemList.size() == 0) return new int[0];

        // 5. create map from group to project
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int item: itemList) {
            map.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
        }

        // 6. get the final project topo sort result
        List<Integer> res = new ArrayList<>();
        for (int groupId: groupList) {
            List<Integer> items = map.getOrDefault(groupId, new ArrayList<>());
            res.addAll(items);
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();

    }

    private List<Integer> tpsort(List<Integer>[] graph, int[]indegree, int n) {
        List<Integer> res = new ArrayList<>();
        Deque<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) queue.offer(i);
        }

        while (!queue.isEmpty()) {
            int node = queue.poll();
            res.add(node);
            for (int next: graph[node]) {
                indegree[next]--;
                if (indegree[next] == 0) {
                    queue.offer(next);
                }
            }
        }
        if (res.size() == n) return res;
        return new ArrayList<>();
    }
}

复杂度分析

user1689 commented 2 years ago

题目

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

思路

拓扑排序

python3

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], beforeItems: 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 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

时间复杂度

相关题目

  1. https://leetcode-cn.com/problems/course-schedule/
  2. https://leetcode-cn.com/problems/course-schedule-ii/
MissNanLan commented 2 years ago

思路

花了很久的时间的才弄懂题目的意思。然后默默去看大佬们的题解

1、 啥是拓步排序 2、 如何确定拓扑排序? 3、 项目和组的依赖关系、项目的顺序等 4、 无人负责的项目

关键点

代码

JavaScript Code:


/**
 * @param {number} n
 * @param {number} m
 * @param {number[]} group
 * @param {number[][]} beforeItems
 * @return {number[]}
 */
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;
};
BreezePython commented 2 years ago

思路

建图+拓扑排序

代码

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:
                group[task] = max_group_id
                max_group_id += 1

        task_indegree = [0] * n    
        group_indegree = [0] * max_group_id
        task_neighbors = [[] for _ in range(n)]
        group_neighbors = [[] for _ in range(max_group_id)]
        group_to_tasks = [[] for _ in range(max_group_id)]

        for task in range(n):
            group_to_tasks[group[task]].append(task)

            for prerequisite in beforeItems[task]:

                # 判断相关联的两个项目是否属于同一组
                if group[prerequisite] != group[task]:

                    # 不是同组,给小组建图
                    group_indegree[group[task]] += 1
                    group_neighbors[group[prerequisite]].append(group[task])
                else:
                    # 同组,给组内项目建图
                    task_indegree[task] += 1
                    task_neighbors[prerequisite].append(task)

        res = []

        # 得到小组的访问顺序
        group_queue = self.topological_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:
            # 得到每组项目的访问顺序
            task_queue = self.topological_sort(group_to_tasks[group_id],task_indegree,task_neighbors)

            if len(task_queue) != len(group_to_tasks[group_id]):
                return []
            res += task_queue

        return res
Moin-Jer commented 2 years ago

思路


拓扑排序

代码


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; 
        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>();
    }
}

复杂度分析


MonkofEast commented 2 years ago

1203. Sort Items by Groups Respecting Dependencies

Click Me

Algo

  1. NEED REVISE!!!!!

Code

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

Comp

T: O(E+V)

S: O(E+V)