大话数据结构--图的最小生成树-java实现

时间:2021-08-17 12:29:07

大话数据结构--图的最小生成树-java实现

  • 普利姆(Prim)算法
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
最小生成树
 *    A
 * /  |  \
 * B- -F- -E
 * \  / \  /
 * C --  D
 * A B C D E F
 * 0 1 2 3 4 5
 * 
 * A-B 6   A-E 5   A-F 1
 * B-C 3   B-F 2
 * C-F 8   C-D 7
 * D-F 4   D-E 2
 * E-F 9

仔细看最小生成树的边比点少一个。
普利姆(Prim)算法是先根据一个点找出与这个点相连接的边来,在从待选边的集合中找到权值最小的边,依次往下找。

  • 克鲁斯卡尔算法
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    大话数据结构--图的最小生成树-java实现
    克鲁斯卡尔算法是先遍历所有的边,在从边中找到最小权值的边。

就需要定义一个Edge类

package graph;

/** * Created by yxf on 2018/4/6. */
public class Edge {

    private int nodeIndexA; //第一个点
    private int nodeIndexB; //第二个点
    private int wegiht; //权重
    private boolean selected=false; //是否被选择


    public Edge() {
    }

    public Edge(int nodeIndexA, int nodeIndexB, int wegiht) {
        this.nodeIndexA = nodeIndexA;
        this.nodeIndexB = nodeIndexB;
        this.wegiht = wegiht;
    }

    public int getNodeIndexA() {
        return nodeIndexA;
    }

    public void setNodeIndexA(int nodeIndexA) {
        this.nodeIndexA = nodeIndexA;
    }

    public int getNodeIndexB() {
        return nodeIndexB;
    }

    public void setNodeIndexB(int nodeIndexB) {
        this.nodeIndexB = nodeIndexB;
    }

    public int getWegiht() {
        return wegiht;
    }

    public void setWegiht(int wegiht) {
        this.wegiht = wegiht;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}
  • graph类
package graph;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/** * Created by yxf on 2018/4/6. * <p> * A * / \ * B D * / \ / \ * C F G- H * \ / * E * <p> * A B C D E F G H * A 1 1 * B 1 1 1 * C 1 1 1 * D 1 1 1 * E 1 * F 1 1 * G 1 1 * H 1 1 * 最小生成树 * A * / | \ * B- -F- -E * \ / \ / * C -- D * A B C D E F * 0 1 2 3 4 5 * * A-B 6 A-E 5 A-F 1 * B-C 3 B-F 2 * C-F 8 C-D 7 * D-F 4 D-E 2 * E-F 9 */
public class Graph<T> {

    private final int DEFAULT_SIZE = 8;//设置默认尺寸
    private int capacity; //图中最多可容纳的顶点数
    private int nodeCount;//已经添加的顶点(结点)个数
    private Node[] nodeArray; //用来存放顶点数组
    private int[] matrix;  //用来存放邻接矩阵
    private final int value = 1; //默认权重
    private Edge[] pEdge;  //最小生成树边的集合

    public static class Node<T> {
        private T data;   //数据
        private boolean m_bIsVisted = false; //当前结点是否访问

        public Node(T data) {
            this.data = data;
        }

        public Node(T data, boolean m_bIsVisted) {
            this.data = data;
            this.m_bIsVisted = m_bIsVisted;
        }
    }

    public Graph() {
        capacity = DEFAULT_SIZE;
        nodeArray = new Node[capacity];
        matrix = new int[capacity * capacity];
        pEdge = new Edge[capacity - 1];
    }

    public Graph(int capacity) {
        this.capacity = capacity;
        nodeArray = new Node[capacity];
        matrix = new int[this.capacity * this.capacity];
        pEdge = new Edge[capacity - 1];
    }

    //向图中加入顶点(结点)
    public boolean addNode(T element) {
        if (nodeCount < 0 && nodeCount > capacity)
            throw new IndexOutOfBoundsException("数组异常出界..");
        Node node = new Node(element);
        nodeArray[nodeCount] = node;
        nodeCount++;
        return true;
    }

    //重置结点
    public void resetNode() {
        for (int i = 0; i < nodeCount; i++) {
            nodeArray[i].m_bIsVisted = false;
        }
    }

    /** * 为有向图设置邻接矩阵 * * @param row * @param col * @param val 权值 默认为1 * @return */
    public boolean setValueToMatrixForDirectedGraph(int row, int col, int val) {
        if (row < 0 || row > capacity || col < 0 || col > capacity) {
            return false;
        }
        matrix[row * capacity + col] = val;
        return true;
    }

    public boolean setValueToMatrixForDirectedGraph(int row, int col) {
        if (row < 0 || row > capacity || col < 0 || col >= capacity) {
            return false;
        }
        matrix[row * capacity + col] = value;
        return true;
    }

    /** * 为无向图设置邻接矩阵 * * @param row * @param col * @param val 权值 默认为1 * @return */
    public boolean setValueToMatrixForUndirectedGraph(int row, int col, int val) {
        if (row < 0 || row > capacity || col < 0 || col >= capacity) {
            return false;
        }
        matrix[row * capacity + col] = val;
        matrix[col * capacity + row] = val;
        return true;
    }

    public boolean setValueToMatrixForUndirectedGraph(int row, int col) {
        if (row < 0 || row > capacity || col < 0 || col >= capacity) {
            return false;
        }
        matrix[row * capacity + col] = value;
        matrix[col * capacity + row] = value;
        return true;
    }

    //从矩阵中获取权值
    private int getValueFromMatrix(int row, int col) {
        if (row < 0 || row > capacity || col < 0 || col >= capacity) {
            return -1;
        }
        return matrix[row * capacity + col];
    }

    // 深度优先遍历
    public void depthFirstTraverse(int nodeIndex) {
        int value;
        System.out.print(nodeArray[nodeIndex].data + " ");
        nodeArray[nodeIndex].m_bIsVisted = true;

        for (int i = 0; i < capacity; i++) {
            value = getValueFromMatrix(nodeIndex, i);
            if (value != 0) {
                //访问过了就退出
                if (nodeArray[i].m_bIsVisted) {
                    continue;
                }
                depthFirstTraverse(i);
            }
        }
    }

    //广度优先遍历
    public void breadthFirstTraverse(int nodeIndex) {
        System.out.println();
        System.out.print(nodeArray[nodeIndex].data + " ");
        nodeArray[nodeIndex].m_bIsVisted = true;
        ArrayList list = new ArrayList();
        list.add(nodeIndex);
        breadthFirstTraverseImpl(list);
    }

    public void breadthFirstTraverseImpl(ArrayList list) {
        int value;
        ArrayList curList = new ArrayList();
        for (int i = 0; i < list.size(); i++) {  //上一层结点
            for (int j = 0; j < capacity; j++) {  //上一层的结点与其他点是否有连接
                value = getValueFromMatrix((Integer) list.get(i), j);
                if (value != 0) {
                    if (nodeArray[j].m_bIsVisted) {  //访问过了就退出
                        continue;
                    }
                    System.out.print(nodeArray[j].data + " ");
                    nodeArray[j].m_bIsVisted = true;
                    curList.add(j);
                }
            }
        }
        if (curList.size() == 0)
            return;
        else {
            breadthFirstTraverseImpl(curList);
        }
    }

    //普里姆生成树
    public void primTree(int nodeIndex) {
        int value;
        int edgeCount = 0; //存储边
        LinkedList<Integer> nodeList = new LinkedList();//存储点集合
        List<Edge> edgeList = new ArrayList<Edge>();//存储待选边集合

        System.out.println("点:" + nodeArray[nodeIndex].data);
        nodeList.add(nodeIndex);
        nodeArray[nodeIndex].m_bIsVisted = true;
        //找出的边比点少1个
        while (edgeCount < capacity - 1) {
            int temp = nodeList.getLast();
            for (int i = 0; i < capacity; i++) {
                value = getValueFromMatrix(temp, i);
                if (value != 0) {
                    if (nodeArray[i].m_bIsVisted) {
                        continue;
                    }
                    Edge edge = new Edge(temp, i, value);
                    //存入待选边集合
                    edgeList.add(edge);
                }
            }
            //从待选边集合中找出最小的边
            int edgeIndex = getMinEdge(edgeList);
            //将最小边设置为true
            edgeList.get(edgeIndex).setSelected(true);

            System.out.print("最小边:" + edgeList.get(edgeIndex).getNodeIndexA() + "----" + edgeList.get(edgeIndex).getNodeIndexB() + " ");
            System.out.print("权值: " + edgeList.get(edgeIndex).getWegiht());
            System.out.println();
            //将选出来的边放到最小生成树边的集合中
            pEdge[edgeCount] = edgeList.get(edgeIndex);
            edgeCount++;
            //找出下一个点
            int nextNodeIndex = edgeList.get(edgeIndex).getNodeIndexB();
            nodeList.add(nextNodeIndex);
            //将这个点标记为true
            nodeArray[nextNodeIndex].m_bIsVisted = true;
            System.out.println("下一个点:" + nodeArray[nextNodeIndex].data);
        }
    }

    //从待选边集合中找出最小的边
    private int getMinEdge(List<Edge> edgeList) {
        int minWeight = 0; //最小权重
        int edgeIndex = 0;
        int i = 0;
        for (; i < edgeList.size(); i++) {
            //边都没有被选择过
            if (!edgeList.get(i).isSelected()) {
                minWeight = edgeList.get(i).getWegiht();
                edgeIndex = i;
                break;
            }
        }
        if (minWeight == 0)
            return -1;
        for (; i < edgeList.size(); i++) {
            if (edgeList.get(i).isSelected()) {
                continue;
            }
            if (minWeight > edgeList.get(i).getWegiht()) {
                minWeight = edgeList.get(i).getWegiht();
                edgeIndex = i;
            }
        }
        return edgeIndex;
    }

    //克鲁斯卡尔算法
    public void kruskalTree(int nodeIndex) {
        int value;
        int edgeCount = 0; //存储边
        ArrayList<ArrayList<Integer>> nodeList = new ArrayList();//存储点集合
        List<Edge> edgeList = new ArrayList<Edge>();//存储待选边集合

        //取出所有边
        for (int i = 0; i < capacity; i++) {
            for (int j = i + 1; j < capacity; j++) {
                value = getValueFromMatrix(i, j);
                if (value != 0) {
                    Edge edge = new Edge(i, j, value);
                    //存入待选边集合
                    edgeList.add(edge);
                }
            }
        }

        //1.找到算法结束条件
        while (edgeCount < capacity - 1) {
            //2.从集合中找到最小边
            int minEdgeIndex = getMinEdge(edgeList);
            //将最小边设置为true
            edgeList.get(minEdgeIndex).setSelected(true);
            //3.找到最小边连接的点
            int nodeIndexA = edgeList.get(minEdgeIndex).getNodeIndexA();
            int nodeIndexB = edgeList.get(minEdgeIndex).getNodeIndexB();
            //4.找出点所在的点集合
            int nodeALabel = -1;
            int nodeBLabel = -1;
            boolean nodeA = false;
            boolean nodeB = false;

            for (int i = 0; i < nodeList.size(); i++) {
                nodeA = isInList(nodeList.get(i), nodeIndexA);
                if (nodeA)
                    nodeALabel = i;
            }
            for (int i = 0; i < nodeList.size(); i++) {
                nodeB = isInList(nodeList.get(i), nodeIndexB);
                if (nodeB)
                    nodeBLabel = i;
            }

            //5.根据点所在集合的不同做出不同处理
            if (nodeALabel == -1 && nodeBLabel == -1) {
                ArrayList<Integer> nodeList1 = new ArrayList<Integer>();
                nodeList1.add(nodeIndexA);
                nodeList1.add(nodeIndexB);
                nodeList.add(nodeList1);
            } else if (nodeALabel == -1 && nodeBLabel != -1) {
                nodeList.get(nodeBLabel).add(nodeIndexA);
            } else if (nodeALabel != -1 && nodeBLabel == -1) {
                nodeList.get(nodeALabel).add(nodeIndexB);
            } else if (nodeALabel != -1 && nodeBLabel != -1 && nodeALabel != nodeBLabel) {
                mergeNodeList(nodeList.get(nodeALabel), nodeList.get(nodeBLabel));
                for (int i = nodeBLabel; i < nodeList.size() - 1; i++) {
                    nodeList.remove(i);
                }
            } else if (nodeALabel != -1 && nodeBLabel != -1 && nodeALabel == nodeBLabel) {
                continue;
            }
            pEdge[edgeCount] = edgeList.get(minEdgeIndex);
            edgeCount++;
            System.out.print("最小边:" + edgeList.get(minEdgeIndex).getNodeIndexA() + "----" + edgeList.get(minEdgeIndex).getNodeIndexB() + " ");
            System.out.print("权值: " + edgeList.get(minEdgeIndex).getWegiht());
            System.out.println();
        }
    }

    private void mergeNodeList(ArrayList<Integer> nodeListA, ArrayList<Integer> nodeListB) {
        for (int i=0;i<nodeListB.size();i++){
            nodeListA.add(nodeListB.get(i));
        }
    }

    //判断target是否在列表中
    private boolean isInList(ArrayList<Integer> nodeList, int target) {

        for (int i=0;i<nodeList.size();i++){
            if(nodeList.get(i)==target){
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < capacity; i++) {
            for (int j = 0; j < capacity; j++) {
                sb.append(matrix[i * capacity + j] + " ");
            }
            sb.append("\r\n");
        }
        int len = sb.length();
        return sb.delete(len - 2, len).toString();
    }
}
  • 测试类
 @Test
    public void minSpanTreePrimTest(){
        Graph graph = new Graph(6);
        graph.addNode("A");
        graph.addNode("B");
        graph.addNode("C");
        graph.addNode("D");
        graph.addNode("E");
        graph.addNode("F");


        graph.setValueToMatrixForUndirectedGraph(0,1,6);
        graph.setValueToMatrixForUndirectedGraph(0,4,5);
        graph.setValueToMatrixForUndirectedGraph(0,5,1);
        graph.setValueToMatrixForUndirectedGraph(1,2,3);
        graph.setValueToMatrixForUndirectedGraph(1,5,2);
        graph.setValueToMatrixForUndirectedGraph(2,5,8);
        graph.setValueToMatrixForUndirectedGraph(2,3,7);
        graph.setValueToMatrixForUndirectedGraph(3,5,4);
        graph.setValueToMatrixForUndirectedGraph(3,4,2);
        graph.setValueToMatrixForUndirectedGraph(4,5,9);


        graph.primTree(0);
        //graph.kruskalTree(0);
    }

输出

普里姆:
点:A
最小边:0----5 权值: 1
下一个点:F
最小边:5----1 权值: 2
下一个点:B
最小边:1----2 权值: 3
下一个点:C
最小边:5----3 权值: 4
下一个点:D
最小边:3----4 权值: 2
下一个点:E
克鲁斯卡尔算法:
最小边:0----5 权值: 1
最小边:1----5 权值: 2
最小边:3----4 权值: 2
最小边:1----2 权值: 3
最小边:3----5 权值: 4

Process finished with exit code 0