IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 必会算法总结3—拓扑排序 -> 正文阅读

[数据结构与算法]必会算法总结3—拓扑排序

必会算法总结(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));
    }
    
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:29:50  更:2021-07-27 16:30:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 18:05:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码