ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

必会算法总结3—拓扑排序

2021-07-26 23:01:19  阅读:183  来源: 互联网

标签:Node deque node map 拓扑 必会 node1 排序 public


必会算法总结(3) - 拓扑排序

​ 可能很多人第一次看到拓扑排序这个名字还以为它是种数值排序算法,它其实是对有向无环图AOV的一种特殊的遍历算法。拓扑排序是图论中比较重要的算法,在笔试中也是很常见的,需要知道的是只有无环图才有拓扑排序,所以使用我们可以使用拓扑排序判断一个图中是否存在环。那么下面我们就一起学习拓扑排序。

邻接表

在本示例中我们选用邻接表存储图,代码如下:

  • Node

    public class Node {
        int value;
        List<Node> neighbors;
        
        public Node () {}
        
        public Node (int value) {
            this.value = value;
        }
        
        public Node (int value, List<Node> neighbors) {
            this.value = value;
            this.neighbors = neighbors;
        }
    }
    
  • Graph

    public class Graph {
        List<Node> nodes;
        
        public Graph() {}
        
        public Graph(List<Node> nodes) {
            this.nodes = nodes;
        }
    }
    

节点入度

在拓扑排序中,我们要计算每一个节点的入度inDegree,在这里我选取图的广度优先遍历算法计算节点的入度,代码如下:

public Map<Node, Integer> findInDegree(Graph graph) {
    Map<Node, Integer> map = new HashMap<>();
    Deque<Node> deque = new LinkedList<>();
    Set<Node> visited = new HashSet<>();
    // 初始化map
    for (Node node : graph.nodes) {
        map.put(node, 0);
    }
    // 图的BFS遍历算法
    for (Node node : graph.nodes) {
        if (!visited.contains(node)) {
            deque.add(node);
            visited.add(node);
            while (!deque.isEmpty()) {
                int size = deque.size();
                for (int i = 0; i < size; i++) {
                    // 遍历节点的每个相邻节点
                    for (Node node1 : deque.remove().neighbors) {
                        map.put(node1, map.get(node1) + 1); // 更新节点的入度
                        if (!visited.contains(node1)) {
                            deque.add(node1);
                            visited.add(node1);
                        }
                    }
                }
            }
        }
    }
    return map;
}

核心算法

现在我们已经知道了每一个节点的入度,就可以进行拓扑排序了,代码如下:

public List<Node> topologicalSort(Graph graph) {
    // 计算每一个节点的入度
    Map<Node, Integer> map = findInDegree(graph);
    // 将所有入度为0的节点加入队列中
    Deque<Node> deque = new LinkedList<>();
    for (Map.Entry<Node, Integer> entry : map.entrySet()) {
        if (entry.getValue() == 0) {
            deque.add(entry.getKey());
        }
    }
    // 重复拆边直到队列为空
    List<Node> list = new LinkedList<>();
    while (!deque.isEmpty()) {
        Node node = deque.remove();
        list.add(node);
        for (Node node1 : node.neighbors) {
            map.put(node1, map.get(node1) - 1);
            if (map.get(node1) == 0) {
                deque.add(node1);
            }
        }
    }
    // 如果图中存在环,则不存在拓扑排序,即不能遍历所有的节点
    if (list.size() != graph.nodes.size()) {
        throw new IllegalArgumentException("图中存在环");
    }
    return list;
}

最终总结

下面的代码是将上面所有代码的汇总,如下:

public class Main {
    
    static class Node {
        int value;
        List<Node> neighbors;
        
        public Node() {}
        
        public Node(int value) {
            this.value = value;
        }
        
        public Node(int value, List<Node> neighbors) {
            this.value = value;
            this.neighbors = neighbors;
        }
    }
    
    static class Graph {
        List<Node> nodes;
        
        public Graph() {}
        
        public Graph(List<Node> nodes) {
            this.nodes = nodes;
        }
    }
    
    public static Map<Node, Integer> findInDegree(Graph graph) {
        Map<Node, Integer> map = new HashMap<>();
        Deque<Node> deque = new LinkedList<>();
        Set<Node> visited = new HashSet<>();
        for (Node node : graph.nodes) {
            map.put(node, 0);
        }
        for (Node node : graph.nodes) {
            if (!visited.contains(node)) {
                deque.add(node);
                visited.add(node);
                while (!deque.isEmpty()) {
                    int size = deque.size();
                    for (int i = 0; i < size; i++) {
                        for (Node node1 : deque.remove().neighbors) {
                            map.put(node1, map.get(node1) + 1);
                            if (!visited.contains(node1)) {
                                deque.add(node1);
                                visited.add(node1);
                            }
                        }
                    }
                }
            }
        }
        return map;
    }
    
    public static List<Node> topologicalSort(Graph graph) {
        Map<Node, Integer> map = findInDegree(graph);
        Deque<Node> deque = new LinkedList<>();
        for (Map.Entry<Node, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 0) {
                deque.add(entry.getKey());
            }
        }
        List<Node> list = new LinkedList<>();
        while (!deque.isEmpty()) {
            Node node = deque.remove();
            list.add(node);
            for (Node node1 : node.neighbors) {
                map.put(node1, map.get(node1) - 1);
                if (map.get(node1) == 0) {
                    deque.add(node1);
                }
            }
        }
    	if (list.size() != graph.nodes.size()) {
        	throw new IllegalArgumentException("图中存在环");
    	}
        return list;
    }
    
    public static void main(String[] args) {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);

        node1.neighbors = Arrays.asList(node2);
        node2.neighbors = Arrays.asList(node3);
        node3.neighbors = Arrays.asList(node4);
        node4.neighbors = Arrays.asList(node2);

        Graph graph = new Graph(Arrays.asList(node1, node2, node3, node4));

        System.out.println(topologicalSort(graph));
    }
    
}

标签:Node,deque,node,map,拓扑,必会,node1,排序,public
来源: https://blog.csdn.net/m0_60255778/article/details/119120520

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有