编程语言
首页 > 编程语言> > Day35-数据结构与算法-图

Day35-数据结构与算法-图

作者:互联网


title: Day35-数据结构与算法-图
date: 2020-12-19 14:26:31
tags: Liu_zimo


常用的经典数据结构

回顾数据结构

图(Graph)

概念

图的概念

图的应用:

有向图

有向图

出度、入度

无向图

无向图

混合图(Mixed Graph)

混合图

简单图、多重图

简单、多重图

无向完全图(Undirected Complete Graph)

无向完全图

有向完全图(Directed Complete Graph)

有权图(Weighted Graph)

有权图

连通图(Connected Graph)

连通分量(Connected Component)

连通分量

强连通图(Strongly Connected Graph)

强连通图

强连通分量(Strongly Connected Component)

强连通分量

图的实现

邻接矩阵(Adjacent Matrix)

邻接矩阵存储

邻接表(Adjacency List)

邻接表

package com.zimo.算法.图;

import java.util.List;
import java.util.Set;

/**
 * 图 - 公共接口
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/12/19 15:47
 */
public interface Graph<V, E> {
    int edgesSize();                                // 边的数量
    int verticesSize();                             // 顶点数量

    void addVertext(V v);                           // 添加一个顶点
    void addEdge(V from, V to);                     // 添加一条边,没有权值
    void addEdge(V from, V to, E weight);           // 添加一条边,有权值

    void removeVertext(V v);                        // 删除一个顶点
    void removeEdge(V from, V to);                  // 删除一条边

    void bfs(V begin, VertexVisitor<V> visitor);    // 广度优先遍历
    void dfs(V begin, VertexVisitor<V> visitor);    // 深度优先遍历

    Set<EdgeInfo<V,E>> minimumSpanningTree();       // 最小生成树
    List<V> topologicalSort();                      // 拓扑排序

    interface VertexVisitor<V> {
        boolean visit(V v);
    }

    class EdgeInfo<V,E>{
        private V from;
        private V to;
        private E weight;

        public EdgeInfo(V from, V to, E weight) {
            this.from = from;
            this.to = to;
            this.weight = weight;
        }

        public V getFrom() {
            return from;
        }

        public void setFrom(V from) {
            this.from = from;
        }

        public V getTo() {
            return to;
        }

        public void setTo(V to) {
            this.to = to;
        }

        public E getWeight() {
            return weight;
        }

        public void setWeight(E weight) {
            this.weight = weight;
        }

        @Override
        public String toString() {
            return "EdgeInfo{" +
                    "from=" + from +
                    ", to=" + to +
                    ", weight=" + weight +
                    '}';
        }
    }
}

遍历

图的遍历

从图中某一顶点出发访问图中其余顶点,且每一个顶点仅被访问一次

遍历方式

常见的有两种遍历方式(有向图,无向图都适用)

广度优先搜索

之前所学的二叉树层序遍历就是一种广度优先搜索

广度优先搜索

public void bfs(V begin) {
    Vertex<V, E> beginVertex = vertices.get(begin);
    if (beginVertex == null)return;

    Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
    Queue<Vertex<V,E>> queue = new LinkedList<>();
    queue.offer(beginVertex);
    visitedVertices.add(beginVertex);

    while (!queue.isEmpty()){
        Vertex<V, E> vertex = queue.poll();
        System.out.println(vertex.value);
        for (Edge<V,E> edge : vertex.outEdges){
            if (visitedVertices.contains(edge.to)) continue;
            queue.offer(edge.to);
            visitedVertices.add(edge.to);
        }
    }
}
// 非递归版
public void dfs_1(V begin) {
    Vertex<V, E> beginVertex = vertices.get(begin);
    if (beginVertex == null) return;
    Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
    Stack<Vertex<V,E>> stack = new Stack<>();

    // 先访问起点
    stack.push(beginVertex);
    visitedVertices.add(beginVertex);
    System.out.println(beginVertex.value);

    while (!stack.isEmpty()){
        Vertex<V, E> vertex = stack.pop();
        for(Edge<V,E> edge : vertex.outEdges){
            if (visitedVertices.contains(edge.to)) continue;
            stack.push(edge.from);
            stack.push(edge.to);
            visitedVertices.add(edge.to);
            System.out.println(edge.to.value);
            break;
        }
    }
}

深度优先搜索

之前所学的二叉树前序遍历就是一种深度优先搜索

深度优先搜索

@Override
public void dfs(V begin) {
    Vertex<V, E> beginVertex = vertices.get(begin);
    if (beginVertex == null) return;
    Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
    dfs(beginVertex, visitedVertices);
}
// 递归版
private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> visitedVertices) {
    System.out.println(vertex.value);
    visitedVertices.add(vertex);
    for (Edge<V,E> edge : vertex.outEdges){
        if (visitedVertices.contains(edge.to)) continue;
        dfs(edge.to,visitedVertices);
    }
}

图的实现

package com.zimo.算法.图;

import java.util.*;

/**
 * 邻接表 - 实现图
 *
 * @author Liu_zimo
 * @version v0.1 by 2020/12/19 15:58
 */
public class ListGraph<V, E> implements Graph<V, E>{
    private Map<V, Vertex<V, E>> vertices = new HashMap<>();    // 所有的顶点
    private Set<Edge<V, E>> edges = new HashSet<>();            // 所有的边
    @Override
    public int edgesSize() {
        return edges.size();
    }

    @Override
    public int verticesSize() {
        return vertices.size();
    }

    @Override
    public void addVertext(V v) {
        if (vertices.containsKey(v)) return;
        vertices.put(v, new Vertex<>(v));
    }

    @Override
    public void addEdge(V from, V to) {
        addEdge(from,to,null);
    }

    @Override
    public void addEdge(V from, V to, E weight) {
        // 判断from to顶点是否存在
        Vertex<V, E> fromVertex = vertices.get(from);
        if (fromVertex == null){
            fromVertex = new Vertex<>(from);
            vertices.put(from, fromVertex);
        }
        Vertex<V, E> toVertex = vertices.get(to);
        if (toVertex == null){
            toVertex = new Vertex<>(to);
            vertices.put(to, toVertex);
        }
        Edge<V, E> edge = new Edge<>(fromVertex, toVertex);
        edge.weight = weight;
        // 尝试删除旧线成功,将另一个方向一起删除,再重新添加,就不需要遍历set集合,找出线再更新权值
        if (fromVertex.outEdges.remove(edge)) {
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
        // 重新添加
        fromVertex.outEdges.add(edge);
        toVertex.inEdges.add(edge);
        edges.add(edge);
    }

    @Override
    public void removeVertext(V v) {
        Vertex<V, E> vertex = vertices.remove(v);
        if (vertex == null) return;
        // 删除顶点对应的边,一边遍历一边删除,使用迭代器
        for (Iterator<Edge<V, E>> iterator = vertex.outEdges.iterator(); iterator.hasNext();){
            Edge<V, E> edge = iterator.next();
            edge.to.inEdges.remove(edge);  // 将 to中的 from -> to 删掉
            iterator.remove();  // 将 from中的 from -> to 删掉
            edges.remove(edge);
        }
        for (Iterator<Edge<V, E>> iterator = vertex.inEdges.iterator(); iterator.hasNext();){
            Edge<V, E> edge = iterator.next();
            edge.from.outEdges.remove(edge);
            iterator.remove();  // 将当前遍历到的元素edge从集合vertex.inEdges中删掉
            edges.remove(edge);
        }
    }

    @Override
    public void removeEdge(V from, V to) {
        // 顶点是否存在
        Vertex<V, E> fromVertex = vertices.get(from);
        if (fromVertex == null) return;
        Vertex<V, E> toVertex = vertices.get(to);
        if (toVertex == null) return;

        Edge<V, E> edge = new Edge<>(fromVertex, toVertex);
        if (fromVertex.outEdges.remove(edge)) {
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
    }

    @Override
    public void bfs(V begin) {
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null)return;

        Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
        Queue<Vertex<V,E>> queue = new LinkedList<>();
        queue.offer(beginVertex);
        visitedVertices.add(beginVertex);

        while (!queue.isEmpty()){
            Vertex<V, E> vertex = queue.poll();
            System.out.println(vertex.value);
            for (Edge<V,E> edge : vertex.outEdges){
                if (visitedVertices.contains(edge.to)) continue;
                queue.offer(edge.to);
                visitedVertices.add(edge.to);
            }
        }
    }

    @Override
    public void dfs(V begin) {
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
        dfs(beginVertex, visitedVertices);
    }
    // 递归版
    private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> visitedVertices) {
        System.out.println(vertex.value);
        visitedVertices.add(vertex);
        for (Edge<V,E> edge : vertex.outEdges){
            if (visitedVertices.contains(edge.to)) continue;
            dfs(edge.to,visitedVertices);
        }
    }

    public void print(){
        vertices.forEach((V key, Vertex<V,E> vertex)->{
            System.out.println(key + "  --out:" + vertex.outEdges + "  --in:" + vertex.inEdges);
        });
        edges.forEach((Edge<V,E> edge)->{
            System.out.println(edge);
        });
    }

    // 顶点
    private static class Vertex<V, E> {
        V value;
        Set<Edge<V, E>> inEdges = new HashSet<>();   // 以这个点为重点的边
        Set<Edge<V, E>> outEdges = new HashSet<>();  // 以这个点为起点的边
        public Vertex(V value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            return Objects.equals(value, ((Vertex<V,E>)obj).value);
        }

        @Override
        public int hashCode() {
            return value == null ? 0 : value.hashCode();
        }

        @Override
        public String toString() {
            return value == null ? "null" : value.toString();
        }
    }
    // 边
    private static class Edge<V, E>{
        Vertex<V, E> from;      // 头
        Vertex<V, E> to;        // 尾
        E weight;               // 权值

        public Edge(Vertex<V, E> from, Vertex<V, E> to) {
            this.from = from;
            this.to = to;
        }

        @Override
        public boolean equals(Object obj) {
            Edge<V,E> edge = (Edge<V, E>) obj;
            return Objects.equals(from, edge.from) && Objects.equals(to, edge.to);
        }

        @Override
        public int hashCode() {
            return from.hashCode() * 31 + to.hashCode();
        }

        @Override
        public String toString() {
            return "Edge{" +
                    "from=" + from +
                    ", to=" + to +
                    ", weight=" + weight +
                    '}';
        }
    }
}

AOV网(Activity On Vertex Network)

拓扑排序(Topological Sort)

拓扑排序

/**
 * 拓扑排序
 * @return
 */
@Override
public List<V> topologicalSort() {
    ArrayList<V> list = new ArrayList<>();
    Queue<Vertex<V,E>> queue = new LinkedList<>();
    HashMap<Vertex<V,E>, Integer> ins = new HashMap<>();    // 入度表

    // 初始化(将度为0的节点都放入队列)
    vertices.forEach((V v, Vertex<V,E> vertex)->{
        int size = vertex.inEdges.size();
        if (size == 0){
            queue.offer(vertex);
        }else {
            ins.put(vertex, size);
        }
    });
    while (!queue.isEmpty()){
        Vertex<V, E> vertex = queue.poll();
        list.add(vertex.value);

        for (Edge<V,E> edge : vertex.outEdges){
            int toIn = ins.get(edge.to) - 1;
            if (toIn == 0){
                queue.offer(edge.to);
            }else {
                ins.put(edge.to, toIn);
            }
        }
    }
    return list;
}

生成树(Spanning Tree)

生成树

最小生成树(Minimum Spanning Tree)

最小生成树

Prim(普里姆算法)

切分定理

切分

执行过程

Prim切分算法

Kruskal(克鲁斯克尔算法)

执行流程

kruskal算法

package com.zimo.算法.图;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 图 - 抽象类
 *
 * @author Liu_zimo
 * @version v0.1 by 2021/1/9 9:59:59
 */
public abstract class Graph_abs<V,E> {
    protected WeightManager<E> weightManager;
    // 无权图
    public Graph_abs() {
    }
    // 有权图
    public Graph_abs(WeightManager<E> weightManager) {
        this.weightManager = weightManager;
    }

    public abstract int edgesSize();                                // 边的数量
    public abstract int verticesSize();                             // 顶点数量

    public abstract void addVertext(V v);                           // 添加一个顶点
    public abstract void addEdge(V from, V to);                     // 添加一条边,没有权值
    public abstract void addEdge(V from, V to, E weight);           // 添加一条边,有权值

    public abstract void removeVertext(V v);                        // 删除一个顶点
    public abstract void removeEdge(V from, V to);                  // 删除一条边

    public abstract void bfs(V begin, VertexVisitor<V> visitor);    // 广度优先遍历
    public abstract void dfs(V begin, VertexVisitor<V> visitor);    // 深度优先遍历

    public abstract Set<EdgeInfo<V,E>> minimumSpanningTree();       // 最小生成树
    public abstract List<V> topologicalSort();                      // 拓扑排序

//    public abstract Map<V,E> shortestPath(V begin);
    public abstract Map<V,PathInfo<V,E>> shortestPath(V begin);

    public interface WeightManager<E> {
        int compare(E w1, E w2);
        E add(E w1, E w2);
        E zero();
    }

    public interface VertexVisitor<V> {
        boolean visit(V v);
    }

    public static class PathInfo<V,E>{
        private E weight;
        private List<EdgeInfo<V,E>> edgeInfos = new LinkedList<>();
        public PathInfo() { }
        public PathInfo(E weight) { this.weight = weight; }
        public E getWeight() { return weight; }
        public void setWeight(E weight) { this.weight = weight; }
        public List<EdgeInfo<V, E>> getEdgeInfos() { return edgeInfos; }

        public void setEdgeInfos(List<EdgeInfo<V, E>> edgeInfos) { this.edgeInfos = edgeInfos; }
        @Override
        public String toString() {
            return "PathInfo{" + "weight=" + weight + ", edgeInfos=" + edgeInfos + '}';
        }
    }

    public static class EdgeInfo<V,E>{
        private V from;
        private V to;
        private E weight;

        public EdgeInfo(V from, V to, E weight) {
            this.from = from;
            this.to = to;
            this.weight = weight;
        }
        public V getFrom() { return from; }
        public void setFrom(V from) { this.from = from; }
        public V getTo() { return to; }
        public void setTo(V to) { this.to = to; }
        public E getWeight() { return weight; }
        public void setWeight(E weight) { this.weight = weight; }
        @Override
        public String toString() {
            return "EdgeInfo{" + "from=" + from + ", to=" + to + ", weight=" + weight +  '}';
        }
    }
}
package com.zimo.算法.图;

import com.zimo.算法.并查集.QuickUnion.GenericUnionFind;

import java.util.*;

/**
 * 邻接表 - 实现图
 *
 * @author Liu_zimo
 * @version v0.2 by 2020/01/09 11:02:37
 */
public class ListGraph<V, E> extends Graph_abs<V, E>{
    private Map<V, Vertex<V, E>> vertices = new HashMap<>();    // 所有的顶点
    private Set<Edge<V, E>> edges = new HashSet<>();            // 所有的边
    private Comparator<Edge<V,E>> edgeComparator = (Edge<V,E> e1, Edge<V,E> e2) -> {
        return weightManager.compare(e1.weight, e2.weight);
    };

    public ListGraph() {}

    public ListGraph(WeightManager<E> weightManager) {
        super(weightManager);
    }

    @Override
    public int edgesSize() {
        return edges.size();
    }

    @Override
    public int verticesSize() {
        return vertices.size();
    }

    @Override
    public void addVertext(V v) {
        if (vertices.containsKey(v)) return;
        vertices.put(v, new Vertex<>(v));
    }

    @Override
    public void addEdge(V from, V to) {
        addEdge(from,to,null);
    }

    @Override
    public void addEdge(V from, V to, E weight) {
        // 判断from to顶点是否存在
        Vertex<V, E> fromVertex = vertices.get(from);
        if (fromVertex == null){
            fromVertex = new Vertex<>(from);
            vertices.put(from, fromVertex);
        }
        Vertex<V, E> toVertex = vertices.get(to);
        if (toVertex == null){
            toVertex = new Vertex<>(to);
            vertices.put(to, toVertex);
        }
        Edge<V, E> edge = new Edge<>(fromVertex, toVertex);
        edge.weight = weight;
        // 尝试删除旧线成功,将另一个方向一起删除,再重新添加,就不需要遍历set集合,找出线再更新权值
        if (fromVertex.outEdges.remove(edge)) {
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
        // 重新添加
        fromVertex.outEdges.add(edge);
        toVertex.inEdges.add(edge);
        edges.add(edge);
    }

    @Override
    public void removeVertext(V v) {
        Vertex<V, E> vertex = vertices.remove(v);
        if (vertex == null) return;
        // 删除顶点对应的边,一边遍历一边删除,使用迭代器
        for (Iterator<Edge<V, E>> iterator = vertex.outEdges.iterator(); iterator.hasNext();){
            Edge<V, E> edge = iterator.next();
            edge.to.inEdges.remove(edge);  // 将 to中的 from -> to 删掉
            iterator.remove();  // 将 from中的 from -> to 删掉
            edges.remove(edge);
        }
        for (Iterator<Edge<V, E>> iterator = vertex.inEdges.iterator(); iterator.hasNext();){
            Edge<V, E> edge = iterator.next();
            edge.from.outEdges.remove(edge);
            iterator.remove();  // 将当前遍历到的元素edge从集合vertex.inEdges中删掉
            edges.remove(edge);
        }
    }

    @Override
    public void removeEdge(V from, V to) {
        // 顶点是否存在
        Vertex<V, E> fromVertex = vertices.get(from);
        if (fromVertex == null) return;
        Vertex<V, E> toVertex = vertices.get(to);
        if (toVertex == null) return;

        Edge<V, E> edge = new Edge<>(fromVertex, toVertex);
        if (fromVertex.outEdges.remove(edge)) {
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
    }

    @Override
    public void bfs(V begin, VertexVisitor<V> visitor) {
        if (visitor == null) return;
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null)return;

        Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
        Queue<Vertex<V,E>> queue = new LinkedList<>();
        queue.offer(beginVertex);
        visitedVertices.add(beginVertex);

        while (!queue.isEmpty()){
            Vertex<V, E> vertex = queue.poll();
            if (visitor.visit(vertex.value)) return;
            for (Edge<V,E> edge : vertex.outEdges){
                if (visitedVertices.contains(edge.to)) continue;
                queue.offer(edge.to);
                visitedVertices.add(edge.to);
            }
        }
    }

    public void dfs_1(V begin) {
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
        dfs_1(beginVertex, visitedVertices);
    }
    // 递归版
    private void dfs_1(Vertex<V, E> vertex, Set<Vertex<V, E>> visitedVertices) {
        System.out.println(vertex.value);
        visitedVertices.add(vertex);
        for (Edge<V,E> edge : vertex.outEdges){
            if (visitedVertices.contains(edge.to)) continue;
            dfs_1(edge.to, visitedVertices);
        }
    }

    // 非递归版

    @Override
    public void dfs(V begin, VertexVisitor<V> visitor) {
        if (visitor == null) return;
        Vertex<V, E> beginVertex = vertices.get(begin);
        if (beginVertex == null) return;
        Set<Vertex<V,E>> visitedVertices = new HashSet<>();     // 遍历过的放到集合中去
        Stack<Vertex<V,E>> stack = new Stack<>();

        // 先访问起点
        stack.push(beginVertex);
        visitedVertices.add(beginVertex);
        if (visitor.visit(begin)) return;

        while (!stack.isEmpty()){
            Vertex<V, E> vertex = stack.pop();
            for(Edge<V,E> edge : vertex.outEdges){
                if (visitedVertices.contains(edge.to)) continue;
                stack.push(edge.from);
                stack.push(edge.to);
                visitedVertices.add(edge.to);
                if (visitor.visit(edge.to.value)) return;
                break;
            }
        }
    }

    /**
     * 最小生成树
     * @return
     */
    @Override
    public Set<EdgeInfo<V, E>> minimumSpanningTree() {
//        return prim();
        return kruskal();
    }

    /**
     * Prim切分算法,每次切分选择最小边
     * @return
     */
    private Set<EdgeInfo<V, E>> prim() {
        Iterator<Vertex<V, E>> it = vertices.values().iterator();
        if (!it.hasNext()) return null;

        HashSet<EdgeInfo<V, E>> edgeInfos = new HashSet<>();    // 返回计算完的结果
        HashSet<Vertex<V, E>> addedVertices = new HashSet<>();        // 检测过的节点
        Vertex<V, E> vertex = it.next();    // 拿到一个点

//        PriorityQueue<Edge<V,E>> heap = new PriorityQueue<>((Edge<V,E> e1, Edge<V,E> e2)->{
//            return 0;
//        });  // 构建一个小顶堆
//        for (Edge<V, E> edge : vertex.outEdges) {
//            heap.offer(edge);
//        } // nlogn
        addedVertices.add(vertex);
        MinHeap<Edge<V, E>> heap = new MinHeap<>(vertex.outEdges, edgeComparator);
        int edgeSize = vertices.size() - 1;
        while (!heap.isEmpty() && edgeSize > edgeInfos.size()){
            Edge<V, E> edge = heap.remove();
            if (addedVertices.contains(edge.to)) continue;

            edgeInfos.add(edge.info());     // 最小的边放进
            addedVertices.add(edge.to);     // 添加扫描完成的节点
            heap.addAll(edge.to.outEdges);  // 将添加的点发散出去的边加进去
        }
        return edgeInfos;
    }

    /**
     * kruskal算法:选择权重最小无法构成环的边
     *      (使用并查集判断是否有环,如果是不同集合的边就可以,同一个集合的边就会构成环)
     * @return
     */
    private Set<EdgeInfo<V, E>> kruskal() {
        int edgeSize = vertices.size() - 1;
        if (edgeSize == -1) return null;
        // O(E)
        HashSet<EdgeInfo<V, E>> edgeInfos = new HashSet<>();
        MinHeap<Edge<V, E>> heap = new MinHeap<>(edges, edgeComparator);  // 最小堆

        GenericUnionFind<Vertex<V,E>> unionFind = new GenericUnionFind<>();
        // O(V)
        vertices.forEach((V v, Vertex<V,E> vertex)->{
            unionFind.makeSet(vertex);
        });
        // O(ElogE)
        while (!heap.isEmpty() && edgeInfos.size() < edgeSize){     // E
            Edge<V, E> edge = heap.remove();    // O(logE)
            // 如果边构成环就跳过
            if (unionFind.isSame(edge.from, edge.to)) continue;
            edgeInfos.add(edge.info());
            unionFind.union(edge.from, edge.to);
        }
        return edgeInfos;
    }

    /**
     * 拓扑排序
     * @return
     */
    @Override
    public List<V> topologicalSort() {
        ArrayList<V> list = new ArrayList<>();
        Queue<Vertex<V,E>> queue = new LinkedList<>();
        HashMap<Vertex<V,E>, Integer> ins = new HashMap<>();    // 入度表

        // 初始化(将度为0的节点都放入队列)
        vertices.forEach((V v, Vertex<V,E> vertex)->{
            int size = vertex.inEdges.size();
            if (size == 0){
                queue.offer(vertex);
            }else {
                ins.put(vertex, size);
            }
        });
        while (!queue.isEmpty()){
            Vertex<V, E> vertex = queue.poll();
            list.add(vertex.value);

            for (Edge<V,E> edge : vertex.outEdges){
                int toIn = ins.get(edge.to) - 1;
                if (toIn == 0){
                    queue.offer(edge.to);
                }else {
                    ins.put(edge.to, toIn);
                }
            }
        }

        return list;
    }

    public void print(){
        vertices.forEach((V key, Vertex<V,E> vertex)->{
            System.out.println(key + "  --out:" + vertex.outEdges + "  --in:" + vertex.inEdges);
        });
        edges.forEach((Edge<V,E> edge)->{
            System.out.println(edge);
        });
    }

    // 顶点
    private static class Vertex<V, E> {
        V value;
        Set<Edge<V, E>> inEdges = new HashSet<>();   // 以这个点为重点的边
        Set<Edge<V, E>> outEdges = new HashSet<>();  // 以这个点为起点的边
        public Vertex(V value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            return Objects.equals(value, ((Vertex<V,E>)obj).value);
        }

        @Override
        public int hashCode() {
            return value == null ? 0 : value.hashCode();
        }

        @Override
        public String toString() {
            return value == null ? "null" : value.toString();
        }
    }
    // 边
    private static class Edge<V, E>{
        Vertex<V, E> from;      // 头
        Vertex<V, E> to;        // 尾
        E weight;               // 权值

        public Edge(Vertex<V, E> from, Vertex<V, E> to) {
            this.from = from;
            this.to = to;
        }

        EdgeInfo<V,E> info(){
            return new EdgeInfo<>(from.value, to.value, weight);
        }

        @Override
        public boolean equals(Object obj) {
            Edge<V,E> edge = (Edge<V, E>) obj;
            return Objects.equals(from, edge.from) && Objects.equals(to, edge.to);
        }

        @Override
        public int hashCode() {
            return from.hashCode() * 31 + to.hashCode();
        }

        @Override
        public String toString() {
            return "Edge{" +
                    "from=" + from +
                    ", to=" + to +
                    ", weight=" + weight +
                    '}';
        }
    }
}

最短路径(Shortest Path)

最短路径1

最短路径无权图

最短路径负权边

public class ListGraph<V, E> extends Graph_abs<V, E> {
    private Map<V, Vertex<V, E>> vertices = new HashMap<>();    // 所有的顶点
    private Set<Edge<V, E>> edges = new HashSet<>();            // 所有的边
    private Comparator<Edge<V, E>> edgeComparator = (Edge<V, E> e1, Edge<V, E> e2) -> {
        return weightManager.compare(e1.weight, e2.weight);
    };

    public ListGraph() {
    }

    public ListGraph(WeightManager<E> weightManager) {
        super(weightManager);
    }
}

Dijkstra

执行过程

最短路径Dijkstar

// dijkstra算法:不能有负权边
private Map<V, PathInfo<V, E>> dijkstra(V begin) {
    Vertex<V, E> vertex = vertices.get(begin);
    if (vertex == null) return null;

    Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>();    // 保存确定的最小路径
    Map<Vertex<V, E>, PathInfo<V, E>> paths = new HashMap<>();      // 存放所有路径,用来松弛更新操作
    // 初始化paths
//        for (Edge<V, E> edge : vertex.outEdges) {
//            PathInfo<V, E> path = new PathInfo<>();
//            path.setWeight(edge.weight);
//            path.getEdgeInfos().add(edge.info());
//            paths.put(edge.to, path);
//        }
    // 优化初始化  将起始点当作松弛操作
    paths.put(vertex, new PathInfo<>(weightManager.zero()));

    while (!paths.isEmpty()) {
        Map.Entry<Vertex<V, E>, PathInfo<V, E>> minEntry = getMinPath(paths);
        // minEntry离开桌面,对它的outEdges进行松弛操作
        Vertex<V, E> minVertex = minEntry.getKey();
        PathInfo<V, E> minPath = minEntry.getValue();
        selectedPaths.put(minVertex.value, minPath);
        paths.remove(minVertex);
        // 对它的minVertex的outEdges进行松弛操作
        for (Edge<V, E> edge : minVertex.outEdges) {
            // 如果edge.to已经离开桌面,就没必要进行松弛操作
            if (selectedPaths.containsKey(edge.to.value)) continue;

            relaxForDijkstra(edge, minPath, paths);
        }
    }
    selectedPaths.remove(begin);
    return selectedPaths;
}
/**
 * 松弛操作
 *
 * @param edge     需要松弛的边
 * @param fromPath edge的from的最短路径信息
 * @param paths    存放其他点(对于dijkstra是还没有离开桌面的点)的最短信息
 */
private void relaxForDijkstra(Edge<V, E> edge, PathInfo<V, E> fromPath, Map<Vertex<V, E>, PathInfo<V, E>> paths) {
    // 以前的最短路径:vertex 到 edge.to
    E newWeight = weightManager.add(fromPath.getWeight(), edge.weight);
    // 新的可选择的最短路径:vertex 到 edge.from 的最短路径 + edge.weight
    PathInfo<V, E> oldPath = paths.get(edge.to);
    if (oldPath != null && weightManager.compare(newWeight, oldPath.getWeight()) >= 0) return;
    if (oldPath == null) {
        oldPath = new PathInfo<>();
        paths.put(edge.to, oldPath);
    } else {
        oldPath.getEdgeInfos().clear();
    }
    oldPath.setWeight(newWeight);
    oldPath.getEdgeInfos().addAll(fromPath.getEdgeInfos());
    oldPath.getEdgeInfos().add(edge.info());
}

Bellman - Ford

Bellman-ford_1

// bellmanFord算法:可以有负权边
private Map<V, PathInfo<V, E>> bellmanFord(V begin) {
    Vertex<V, E> vertex = vertices.get(begin);
    if (vertex == null) return null;

    Map<V, PathInfo<V, E>> selectedPaths = new HashMap<>();    // 保存确定的最小路径
    PathInfo<V, E> beginPath = new PathInfo<>();               // 初始化第一个点
    beginPath.setWeight(weightManager.zero());
    selectedPaths.put(begin, beginPath);
    int count = vertices.size() - 1;
    for (int i = 0; i < count; i++) { // 最坏的情况: v - 1 次
        for (Edge<V, E> edge : edges) {
            PathInfo<V, E> fromPath = selectedPaths.get(edge.from.value);
            if (fromPath == null) continue;
            relaxForBellmanFord(edge, fromPath, selectedPaths);
        }
    }
    // 再进行一次松弛
    for (Edge<V, E> edge : edges) {
        PathInfo<V, E> fromPath = selectedPaths.get(edge.from.value);
        if (fromPath == null) continue;
        boolean relax = relaxForBellmanFord(edge, fromPath, selectedPaths);
        if (relax) {
            System.out.println("存在负权环,找不到最短路径");
            return null;
        }
    }
    selectedPaths.remove(begin);
    return selectedPaths;
}
/**
 * BellmanFord松弛操作
 *
 * @param edge     需要松弛的边
 * @param fromPath edge的from的最短路径信息
 * @param paths    存放其他点的最短信息
 */
private boolean relaxForBellmanFord(Edge<V, E> edge, PathInfo<V, E> fromPath, Map<V, PathInfo<V, E>> paths) {
    // 以前的最短路径:vertex 到 edge.to
    E newWeight = weightManager.add(fromPath.getWeight(), edge.weight);
    // 新的可选择的最短路径:vertex 到 edge.from 的最短路径 + edge.weight
    PathInfo<V, E> oldPath = paths.get(edge.to.value);
    if (oldPath != null && weightManager.compare(newWeight, oldPath.getWeight()) >= 0) return false;
    if (oldPath == null) {
        oldPath = new PathInfo<>();
        paths.put(edge.to.value, oldPath);
    } else {
        oldPath.getEdgeInfos().clear();
    }
    oldPath.setWeight(newWeight);
    oldPath.getEdgeInfos().addAll(fromPath.getEdgeInfos());
    oldPath.getEdgeInfos().add(edge.info());
    return true;
}

Floyd

/**
 * 多源最短路径算法 -floyd
 *
 * @return
 */
@Override
public Map<V, Map<V, PathInfo<V, E>>> shortestPath() {
    Map<V, Map<V, PathInfo<V, E>>> paths = new HashMap<>();
    // 初始化 将所有边都添加进去
    for (Edge<V, E> edge : edges) {
        Map<V, PathInfo<V, E>> map = paths.get(edge.from.value);
        if (map == null){
            map = new HashMap<>();
            paths.put(edge.from.value, map);
        }
        PathInfo<V, E> pathInfo = new PathInfo<>(edge.weight);
        pathInfo.getEdgeInfos().add(edge.info());
        map.put(edge.to.value, pathInfo);
    }
    vertices.forEach((V v2, Vertex<V,E> vertex2)->{
        vertices.forEach((V v1, Vertex<V,E> vertex1)->{
            vertices.forEach((V v3, Vertex<V,E> vertex3)->{
                if (v1.equals(v2) || v2.equals(v3) || v1.equals(v3)) return;
                // v1 ~ v2  + v2 ~ v3  < v1 - v3
                PathInfo<V, E> path_12 = getPathInfo(v1, v2, paths); // v1 - v2
                if (path_12 == null) return;
                PathInfo<V, E> path_23 = getPathInfo(v2, v3, paths); // v2 - v3
                if (path_23 == null) return;
                PathInfo<V, E> path_13 = getPathInfo(v1, v3, paths); // v1 - v3
                E newWeight = weightManager.add(path_12.getWeight(), path_23.getWeight());
                if (path_13 != null && weightManager.compare(newWeight, path_13.getWeight()) >= 0) {
                    return; // 结束本次循环,并没有退出函数
                }
                if (path_13 == null){
                    path_13 = new PathInfo<>();
                    paths.get(v1).put(v3,path_13);
                }else {
                    path_13.getEdgeInfos().clear();
                }
                path_13.setWeight(newWeight);
                path_13.getEdgeInfos().clear();
                path_13.getEdgeInfos().addAll(path_12.getEdgeInfos());
                path_13.getEdgeInfos().addAll(path_23.getEdgeInfos());
            });
        });
    });
    return paths;
}

标签:return,Vertex,Day35,vertex,算法,edge,new,数据结构,public
来源: https://blog.csdn.net/qq_38205875/article/details/113444500