1203. 项目管理(拓扑排序)

博客围绕Java和图的拓扑排序展开,涉及信息技术领域中后端开发和算法相关内容。Java作为后端开发常用语言,拓扑排序是图算法的重要应用,可解决有向无环图节点排序问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.heu.wsq.leetcode.graph;

import java.util.*;

/**
 * 1203. 项目管理
 * @author wsq
 * @date 2021/1/12
 * 公司共有 n 个项目和  m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。
 * group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。
 * 请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:
 * 同一小组的项目,排序后在列表中彼此相邻。
 * 项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
 * 如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。
 *
 * 示例 1:
 * 输入:n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]]
 * 输出:[6,3,4,1,5,2,0,7]
 *
 * 链接:https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies
 */
public class SortItems {
    public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) {
        // 1.由于group[i]=-1表示该任务没有被分配小组,影响后续组的拓扑序
        // 预处理将group[i]=-1的分配不同的小组编号
        int totalM = m;
        for (int i = 0; i < group.length; i++){
            if (group[i] == -1){
                group[i] = totalM;
                totalM++;
            }
        }
        // 2.构建项目和组的邻接表
        List<Integer>[] groupAdj = new ArrayList[totalM];
        List<Integer>[] itemAdj = new ArrayList[n];
        for (int i = 0; i < totalM; i++) {
            groupAdj[i] = new ArrayList<>();
        }
        for (int i = 0; i < n; i++){
            itemAdj[i] = new ArrayList<>();
        }
        // 3.建图和统计入度数组
        int[] groupsIndegree = new int[totalM];
        int[] itemsIndegree = new int[n];
        int len = group.length;
        for (int i = 0; i < len; i++){
            int currGroup = group[i];
            List<Integer> befores = beforeItems.get(i);
            for (Integer before : befores) {
                int beforeGroup = group[before];
                if (beforeGroup == currGroup){
                    itemsIndegree[i]++;
                    itemAdj[before].add(i);
                }else{
                    groupsIndegree[currGroup]++;
                    groupAdj[beforeGroup].add(currGroup);
                }
            }
        }
        // 4.得到组和项目的拓扑排序结果
        List<Integer> groupList = topoSort(groupAdj, groupsIndegree, totalM);
        if (groupList.isEmpty()){
            return new int[0];
        }
        List<Integer> itemList = topoSort(itemAdj, itemsIndegree, n);
        if (itemList.isEmpty()){
            return new int[0];
        }
        // 5.按照项目的拓扑顺序,保存组与项目的多对一关系,这样做的好处是保留了项目的拓扑顺序
        Map<Integer, List<Integer>> group2Items = new HashMap<>();
        for (Integer item : itemList) {
            group2Items.computeIfAbsent(group[item], key -> new ArrayList<>()).add(item);
        }
        // 6.根据组的拓扑排序获取最后的结果
        List<Integer> res = new ArrayList<>();
        for (Integer gp : groupList) {
            List<Integer> items = group2Items.getOrDefault(gp, new ArrayList<>());
            res.addAll(items);
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }

    private List<Integer> topoSort(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 (Integer successor : adj[front]) {
                indegree[successor]--;
                if (indegree[successor] == 0){
                    queue.offer(successor);
                }
            }
            if (res.size() == n){
                return res;
            }
        }
        return new ArrayList<>();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值