zoukankan      html  css  js  c++  java
  • 目录

    1 生成图

    图的表示

    图表示有很多中,一般在面试中常见的如下:

    代码实现

    图的节点

    //图的节点的定义
    public class Node {
        //value的类型不一定是整型可以是泛型
        public int value;
        //节点的入度
        public int in;
        //节点的出度
        public int out;
        //从当前的节点出发可以到达的所有节点即当前节点的所有邻居节点
        public ArrayList<Node> nexts;
        //从当前节点出发,发散出边的集合
        public ArrayList<Edge> edges;
    
        public Node(int value) {
            this.value = value;
            in = 0;
            out = 0;
            nexts = new ArrayList<>();
            edges = new ArrayList<>();
        }
    }

    图的边

    //图的边的的定义
    public class Edge {
        //边的权值是多少
        public int weight;
        //从哪个节点出发
        public Node from;
        //去向哪个节点
        public Node to;
    
        public Edge(int weight, Node from, Node to) {
            this.weight = weight;
            this.from = from;
            this.to = to;
        }
    }

    图的定义

    //图的定义
    public class Graph {
        //图就是所有点的集合和边的集合
        public HashMap<Integer,Node> nodes;
        public HashSet<Edge> edges;
    
        public Graph() {
            nodes = new HashMap<>();
            edges = new HashSet<>();
        }
    }

    生成图

    public class GraphGenerator {
        //生成图
        public static Graph createGraph(Integer[][] matrix) {
            Graph graph = new Graph();
            //如博客中给出的示例,矩阵的行数不定但是列数为3即每一行的长度为3
            for (int i = 0; i < matrix.length; i++) {
                Integer from = matrix[i][0];
                Integer to = matrix[i][1];
                Integer weight = matrix[i][2];
                //如果from这个点的没有在图中定义,则建上该点
                if (!graph.nodes.containsKey(from)) {
                    graph.nodes.put(from, new Node(from));
                }
                //如果to这个点的没有在图中定义,则建上该点
                if (!graph.nodes.containsKey(to)) {
                    graph.nodes.put(to, new Node(to));
                }
                //拿到from点和to点
                Node fromNode = graph.nodes.get(from);
                Node toNode = graph.nodes.get(to);
                //根据两个点生成一个边
                Edge newEdge = new Edge(weight, fromNode, toNode);
                //from的邻居增加to这个点
                fromNode.nexts.add(toNode);
                //from点出度自增
                fromNode.out++;
                //to点入度自增
                toNode.in++;
                //from点的边集中增加这个边
                fromNode.edges.add(newEdge);
                //整个图的边集中增加这个边
                graph.edges.add(newEdge);
            }
            return graph;
        }
    }

    2 图的遍历

    宽度(广度)优先遍历

     依照距离起始节点的距离确定输出的先后顺序,同等距离则可能会出现不同情况,注意图的遍历顺序是不确定的

    //整体与树中的BFS很类似的,不同点在于需要注意重复元素
    public static void bfs(Node node) {
        if (node == null) {
            return;
        }
        //用队列实现BFS
        Queue<Node> queue = new LinkedList<>();
        //用来记录哪些节点已经进入了
        HashSet<Node> map = new HashSet<>();
        queue.add(node);
        map.add(node);
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            System.out.println(cur.value);
            for (Node next : cur.nexts) {
                //只有没进过set集合的节点才添加,避免重复添加的问题
                if (!map.contains(next)) {
                    map.add(next);
                    queue.add(next);
                }
            }
        }
    }

     深度优先

    public static void dfs(Node node) {
        if (node == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        HashSet<Node> set = new HashSet<>();
        stack.add(node);
        set.add(node);
        System.out.println(node.value);
        while (!stack.isEmpty()) {
            Node cur = stack.pop();
            for (Node next : cur.nexts) {
                //如果当前节点的后代不在set中,那就把当前节点和其后代都放入栈中
                if (!set.contains(next)) {
                    stack.push(cur);
                    stack.push(next);
                    set.add(next);
                    System.out.println(next.value);
                    break;
                }
            }
        }
    }

    3 进阶算法

    1 拓扑排序算法

    适用范围:有向图,且有入度为0的节点,且没有环

    使用场景:编辑一个工程A其依赖有BCDE配置文件,配置文件内部又相互依赖,决定应该先编译哪一个文件。

    算法思想:

    代码实现:

    public static List<Node> sortedTopology(Graph graph) {
        //统计当前所有节点的入度
        HashMap<Node, Integer> inMap = new HashMap<>();
        //保存入度为0的点
        Queue<Node> zeroInQueue = new LinkedList<>();
        for (Node node : graph.nodes.values()) {
            inMap.put(node, node.in);
            if (node.in == 0) {
                zeroInQueue.add(node);
            }
        }
        List<Node> result = new ArrayList<>();
        while (!zeroInQueue.isEmpty()) {
            //弹出一个入度为0的点,并在结果中保存
            Node cur = zeroInQueue.poll();
            result.add(cur);
            //把当前节点的所有后代入度自减1,实质就是消除该节点的影响
            for (Node next : cur.nexts) {
                inMap.put(next, inMap.get(next) - 1);
                //如果有入度为0的点进队列
                if (inMap.get(next) == 0) {
                    zeroInQueue.add(next);
                }
            }
        }
        return result;
    }

    最小生成树算法

    适用范围:无向图

    使用场景:在保证图的连通性前提下,使得图的权值最小

    1 Kruskal 算法

    算法思想:选择权值最小的边,如下图中先选择1,然后选择2,如果选择的边构成了一个回路则舍弃,当选择的边能够遍历到所有的点时算法结束。

    public static Set<Edge> kruskalMST(Graph graph) {
        //把所有的点放入并查集中
        UnionFind unionFind = new UnionFind();
        unionFind.makeSets(graph.nodes.values());
        //按照边的权重组成一个小根堆,堆中放的都是边
        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>();
        for (Edge edge : graph.edges) {
            priorityQueue.add(edge);
        }
        //保存最后的结果
        Set<Edge> result = new HashSet<>();
        while (!priorityQueue.isEmpty()) {
            Edge edge = priorityQueue.poll();
            //每次从最小堆弹出一个边,如果边的出入节点已经属于一个集合,则舍弃该边
            if (!unionFind.isSameSet(edge.from, edge.to)) {
                //如果没有则添加该边,并把该边的出入节点合并在一起
                result.add(edge);
                unionFind.union(edge.from, edge.to);
            }
        }
        return result;
    }

    2 prim 算法

    算法思想:从一个点出发(V1)找其发出边中最小的边,到达下一个新的节点,然后从这两个点的所有发出边中找到最小可以到达新的点的边,直至达到所有的点。

    public static Set<Edge> primMST(Graph graph) {
        //最小堆中放边
        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>();
        //保存已经遍历过的点
        HashSet<Node> set = new HashSet<>();
        //保存结果的边
        Set<Edge> result = new HashSet<>();
        for (Node node : graph.nodes.values()) {
            if (!set.contains(node)) {
                //如果没有遍历过该点,加入该点
                set.add(node);
                //加入该点的所有发出边
                for (Edge edge : node.edges) {
                    priorityQueue.add(edge);
                }
                //依次弹出权值最小的边
                while (!priorityQueue.isEmpty()) {
                    Edge edge = priorityQueue.poll();
                    Node toNode = edge.to;
                    //只有没遍历过的新点才加入
                    if (!set.contains(toNode)) {
                        set.add(toNode);
                        result.add(edge);
                        //加入新的节点的所有发出边
                        for (Edge nextEdge : node.edges) {
                            priorityQueue.add(nextEdge);
                        }
                    }
                }
            }
        }
        return result;
    }

    0

  • 相关阅读:
    Silverlight 自定义表格 转
    Application_BeginRequest事件过滤恶意提交
    存储过程学习1
    我是博客园新博客
    努力将SQL Server像玩游戏一样熟练
    【Demo 0003】支持交互的应用
    【Demo 0003】支持交互的应用
    linux终端快捷键
    vim与windows/linux之间的复制粘贴小结
    vimgrep简单使用
  • 原文地址:https://www.cnblogs.com/youngao/p/12109422.html
Copyright © 2011-2022 走看看