Java实现图和网的bfs、dfs、dijkstra、floyd

图的实现、包括bfs,dfs,dijkstra、floyd

package pri.wdw.algorithm.graph;


import java.util.*;

/**
 * <p>@author wdw</p>
 * <p>@date 2020/5/6 22:08</p>
 * <p>@description </p>
 */
public class Graph {

    //顶点
    protected final List<String> vertexes;

    //边
    protected final int[][] edges;


    /**
     * @param vertexes 每个节点的名称
     */
    public Graph(String... vertexes) {

        int length = vertexes.length;

        this.vertexes = new ArrayList<>(length);

        edges = new int[length][length];

        //自己结点设为 0 ,不可到达结点设为 -1
        for (int i = 0; i < edges.length; i++) {
            Arrays.fill(edges[i], -1);
            edges[i][i] = 0;
        }

        this.vertexes.addAll(Arrays.asList(vertexes));
    }


    /**
     * 无向图
     *
     * @param v1     在邻接矩阵的横坐标
     * @param v2     临界矩阵纵坐标
     * @param weight 权值
     * @return 插入是否成功
     */
    public boolean addEdge(int v1, int v2, int weight) {
        if (v1 < 0 || v1 >= edges.length || v2 < 0 || v2 >= edges.length || weight <= 0) {
            return false;
        } else {
            this.edges[v1][v2] = weight;
            this.edges[v2][v1] = weight;
            return true;
        }
    }


    /**
     * @return
     */
    public int[][] getEdge() {
        return this.edges;
    }


    public String getVertex(int index) {
        return this.vertexes.get(index);
    }

    /**
     * 添加一个节点
     *
     * @param vertex 节点
     */
    public void addVertexes(String vertex) {
        vertexes.add(vertex);
    }

    /**
     * @param v1
     * @param v2
     * @return 获取权值
     */
    public int getWeight(int v1, int v2) {
        return this.edges[v1][v2];
    }


    /**
     * @return 获取结点的个数
     */
    public int size() {
        return vertexes.size();
    }


    /**
     * @return 打印邻接矩阵
     */
    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < edges.length; i++) {
            builder.append(vertexes.get(i))
                    .append(' ')
                    .append(Arrays.toString(edges[i]))
                    .append('\n');
        }

        builder.deleteCharAt(builder.length() - 1);

        return builder.toString();
    }


    /**
     * 暴漏给外部的方法
     *
     * @param index 当前正在被访问的结点
     */
    public void dfs(int index) {

        int length = this.edges.length;

        //鲁棒性
        if (index >= length || index < 0) throw new IllegalArgumentException();

        this.dfs(index, new boolean[length]);
    }


    /**
     * DFS
     * <p>
     * 每次都是优先访问 当前节点的邻接节点,并且以这个邻接节点,访问下一个邻接结点。
     * <p>
     * 1、访问初始节点A,并且标记节点被访问
     * 2、访问 A 的下一个邻接结点 B未被访问,并且A-B连通,以B为根递归,否则访问A 的 下一个邻接结点C
     * <p>
     * 访问顺序 A-B-C-D-E
     *
     * @param index     当前正在被访问的结点
     * @param isVisited 当前结点是否被访问标志
     */
    private void dfs(int index, boolean[] isVisited) {

        int length = this.edges.length;

        //标记已经访问
        isVisited[index] = true;

        //打印
        System.out.println(this.vertexes.get(index));

        //每次取模,可以从任意一个结点开始,每次都访问逻辑意义上(循环)的下一个结点,直到i返回和 index 一样
        for (int i = (index + 1) % length; i != index; i = (i + 1) % length) {
            if (!isVisited[i] && this.edges[index][i] != -1) {
                dfs(i, isVisited);
            }
        }
    }


    /**
     * BFS最大广度遍历,和二叉树的层序遍历类似
     * <p>
     * ① 访问顶点vi ;
     * <p>
     * ② 访问vi 的所有未被访问的邻接点w1 ,w2 , …wk ;
     * <p>
     * ③ 依次从这些邻接点(在步骤②中访问的顶点)出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的
     */
    public void bfs(int index) {

        int length = this.edges.length;

        //鲁棒性
        if (index >= length || index < 0) throw new IllegalArgumentException();

        //辅助队列
        Queue<Integer> queue = new LinkedList<>();

        //记录是否被访问
        boolean[] isVisited = new boolean[length];

        isVisited[index] = true;
        queue.offer(index);

        //递归调用顺序为 访问vi 的所有未被访问的邻接点w1 ,w2 , …wk
        while (!queue.isEmpty()) {

            index = queue.poll();
            System.out.println(this.vertexes.get(index));

            //依次压入该结点的邻接结点,之后再进行打印。
            for (int j = (index + 1) % length; j != index; j = (j + 1) % length) {
                if (!isVisited[j] && this.edges[index][j] != -1) {
                    queue.offer(j);
                    isVisited[j] = true;
                }
            }
        }
    }


    /**
     * dijkstra算法应用到了广度优先遍历,本质上是一个贪心算法。
     * 可以解决  单元最短路径(一个结点到所有结点之间的最短距离)。
     *
     * @param index 该结点在邻接矩阵的位置
     * @return 该结点到每个结点的最短路径
     */
    public int[] dijkstra(int index) {

        int length = this.size();

        //0、初始化两个容器,未计算的结点、计算的距离。
        int[] distances = this.edges[index].clone();

        HashSet<Integer> notCalculatedVertexes = new HashSet<>(length - 1);

        for (int i = 0; i < this.size(); i++) {
            if (i != index) notCalculatedVertexes.add(i);
        }

        //1、初始化的minIndex,就是需要求的结点本事。
        int minIndex = index;

        while (!notCalculatedVertexes.isEmpty()) {

            int minDis = Integer.MAX_VALUE;

            //2、在未找到最短路径的邻接结点中找到距离最短的,记录 minDis和index(根据选择排序算法)
            for (Integer res : notCalculatedVertexes) {
                if ((distances[res] != -1) && distances[res] < minDis) {
                    minDis = distances[res];
                    minIndex = res;
                }
            }

            //2.1、当未更新minDis代表其他的结点都不可达,直接跳出循环。(在无向图和网中都适应,如果是无向图那么不需要这句)
            if (minDis == Integer.MAX_VALUE) break;


            //3、每次循环距离最短的邻接结点的距离就是最短距离,在未找到的容器内删除。
            notCalculatedVertexes.remove(minIndex);

            //4、根据该 index对应的,找到根据该邻接结点到达某个结点的距离,如果更短,那么更新容器 distances[]
            for (int i = (minIndex + 1) % length; i != minIndex; i = (i + 1) % length) {

                if (i == index) continue;//4.1、循环过程中遇到index,代表就是最短路径,跳出本次循环。

                int dis = this.edges[minIndex][i];

                if ((dis != -1 && (distances[i] == -1 || minDis + dis < distances[i]))) {
                    distances[i] = minDis + dis;
                }
            }

        }
        return distances;
    }


    /**
     * floyd解决多源之间的最小距离,时间复杂度为 O(n^3),空间复杂度为O(n^2)
     *
     * @return 每个顶点之间的最小距离
     */
    public int[][] floyd() {

        int length = this.edges.length;

        //克隆edges数组,初始数据。
        int[][] distances = new int[length][];

        for (int i = 0; i < distances.length; i++) {
            distances[i] = this.edges[i].clone();
        }


        //最外层循环代表每个结点都加入一次矩阵,内层的两个循环代表矩阵。
        for (int k = 0; k < length; k++) {
            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {

                    if (i == j) continue; //不判断对角线

                    int dis = distances[i][k] + distances[k][j];

                    if ((dis < distances[i][j] || distances[i][j] == -1) &&
                            distances[i][k] != -1 && distances[k][j] != -1) {//在连通的情况下进行判断

                        distances[i][j] = dis;
                    }
                }
            }
        }

        return distances;
    }
   


    /**
     * @return 生成一个简单图
     */
    public static Graph buildGraph() {

        String[] vertexes = {"A", "B", "C", "D", "E"};

        Graph graph = new Graph(vertexes);

        //添加边A-B A-C B-C B-D B-E

        graph.addEdge(0, 1, 1);
        graph.addEdge(0, 2, 1);
        graph.addEdge(1, 2, 1);
        graph.addEdge(1, 3, 1);
        graph.addEdge(1, 4, 1);

        return graph;
    }

}

网的实现、重写了addEdge() 方法

package pri.wdw.algorithm.graph;

/**
 * <p>@author wdw</p>
 * <p>@date 2020/5/8 10:22</p>
 * <p>@description 有向图(网) </p>
 */
public class Net extends Graph {


    /**
     * 有向图和无向图使用注意 :
     * 无向图遍历任意一个结点都能遍历图中所有的结点(图的所有结点都是连接的)
     * 在有向图当中,考虑到某些结点没有入度,会导致不能根据此结点遍历到所有的结点。
     */
    public Net(String... vertexes) {
        super(vertexes);
    }


    /**
     * 有向图,v1到v2的距离
     *
     * @param v1     在邻接矩阵的横坐标
     * @param v2     临界矩阵纵坐标
     * @param weight 权值
     * @return 插入是否成功
     */
    @Override
    public boolean addEdge(int v1, int v2, int weight) {
        if (v1 < 0 || v1 >= edges.length || v2 < 0 || v2 >= edges.length || weight <= 0) {
            return false;
        } else {
            this.edges[v1][v2] = weight;
            return true;
        }
    }


    /**
     * @return 生成一个网(每个边带方向)
     */
    public static Net buildNet() {

        String[] vertexes = {"v0", "v1", "v2", "v3", "v4", "v5", "v6"};

        Net net = new Net(vertexes);

        net.addEdge(0, 1, 13);
        net.addEdge(0, 2, 8);
        net.addEdge(0, 4, 30);
        net.addEdge(0, 6, 32);
        net.addEdge(1, 5, 9);
        net.addEdge(1, 6, 7);
        net.addEdge(2, 3, 5);
        net.addEdge(4, 5, 2);
        net.addEdge(5, 6, 17);
        net.addEdge(3, 4, 6);

        return net;
    }



    public static void main(String[] args) {

        Net net = Net.buildNet();

        net.dfs(0);

        System.out.println("-----");

        net.bfs(5);
    }
}

附:dijitra和flody算法图解

dijitra

flody

posted @ 2020-05-08 16:32  wongdw  阅读(231)  评论(0编辑  收藏  举报