带权图的最小生成树问题

带权图的最小生成树问题

假设共有六个定点,分别为命名为0-5,如下图所示:
带权图的最小生成树问题_第1张图片
根据图构建出邻接矩阵adjMat[][]如下(其中权值为-1表示该两点之间没有路径):

adjMat[][] 0 1 2 3 4 5
0 -1 6 -1 4 -1 -1
1 6 -1 10 7 7 -1
2 -1 10 -1 8 5 6
3 -1 7 8 -1 12 -1
4 -1 -1 12 -1 -1 7
5 -1 -1 -1 -1 7 -1

分析

优先级队列以数组的方式来实现,并且数组中,权值越大的值数组下标越小。
首先从任意节点开始,这里从0节点开始,通过搜索邻接矩阵找到0节点到所有相连接点的边:
1、以当前节点号为行遍历该行所有的列,其中列不能等于行号(行号等于列号时代表自身与自身相连)。
2、当该行上某一列的值部不为-1时,代表该点与行值点相连,该值为连线的权值。该案例中邻接矩阵adjMat[0][1]和adjMat[0][3]两个元素不为-1。
3、将该边放在优先级队列中。本案例中队列中有边0-1(6)、0-3(4)。
4、得到优先级队列中的队首元素(即队列中权值最小的元素)并将该边从队列中删除,这里为边0-3。
5、以该边的终点作为新的顶点,这里即边0-3中3为新的顶点。重复以上五个步骤。
注意并不是要把搜索的到邻接矩阵中所有非-1边都放入队列中的,必须符合以下条件的才能进入队列:
1、边的的起点和终点不能是同一个。
2、边的终点已经在树中。
3、边的权值不为-1。

代码的实现

程序主要有四个类构成

1、顶点类Vertex
顶点类具有两个属性,顶点的名字和改点是否已经在树中

class Vertex {
    public char label;
    public boolean isInTree;

    public Vertex(char label) {
        this.label = label;
        isInTree = false;
    }
}

2、边类Edge
边类包含三个属性,分别时起点srcVert、终点destVert、边的权重这里用distance表示。

class Edge {
    //    starting edge
    public int srcVert;
    //    ending edge
    public int destVert;
    public int distance;

    public Edge(int srcVert, int destVert, int distance) {
        this.srcVert = srcVert;
        this.destVert = destVert;
        this.distance = distance;
    }
}

3、优先级队列类PriorityQ
优先级队列采用数组queArray实现,实现方法比较简单但不严谨,比如队列只能朝一个方向插入,抽取。因此打算专门写一篇文章讨论队列的实现,这里不做重点讨论。并且优先级队列还可以用堆来实现。
队列中用size来标示队列中的元素个数。
队列中包含insert()、removeMin()、peekMin()、peekN()、find()等方法。
insert()用来队列的插入,将权值小的放在队列的首部,即放在数组下标较大的位置;
removeMin()是移除队队首元素即分析部分中所描述的步骤4;
peekMin()用来查看队首元素;
peekN()用来查看队列中下表为n的元素;
find()用来查找边的终点是否已经包含在队列中。

class PriorityQ {
    private final int SIZE = 20;
    private Edge[] queArray;
    private int size;

    public PriorityQ() {
        queArray = new Edge[SIZE];
        size = 0;
    }

    public void insert(Edge item) {
        int j;
        for (j = 0; j < size; j++) {
            if (item.distance >= queArray[j].distance) {
                break;
            }
        }
        for (int k = size - 1; k >= j; k--) {
            queArray[k+1] = queArray[k];
        }
        queArray[j] = item;
        size++;
    }

    public Edge removeMin() {
        return queArray[--size];
    }

    public void removeN(int n) {
        for (int j = n; j < size - 1; j++) {
            queArray[j] = queArray[j + 1];
        }
        size--;
    }

    public Edge peekMin() {
        return queArray[size--];
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return (size == 0);
    }

    public Edge peekN(int n) {
        return queArray[n];
    }

    public int find(int findDex) {
        for (int j = 0; j < size; j++) {
            if (queArray[j].destVert == findDex) {
                return j;
            }
        }
        return -1;
    }
}

4、图类Graph
图类中包含邻接矩阵adjMat[][]、顶点数组列表vertexList[]、优先级队列thePQ等,包含mstw()和putInPQ()两个重要的方法
mstw()是实现最小生成树的核心代码:
从顶点0开始搜索,当树中的元素个数和顶点的个数相同时while循环结束算法退出。
按照分析中所述的方法,一次遍历每一行的每一个值,当遇到符合条件的边时调用putInPQ()方法进行队列插入;
然后从优先级队列中取出队收的边元素,以改变元素的终点作为新的顶点,进行下一步。
putInPQ()时队列执行插入的方法
要插入的边的终点不在队列中是,表示这是一个新的节点,因此直接将该边插入队列中,当该边的终点已经在队列中时,查找出队列中到达该终点的旧边,查看旧边的权值,如果旧边的权值比新边的权值大那么将旧的边删除,将新的边插入,否者新的边不再插入队列。解释为如果又发现了一条到达节点的新路径,如果新路径权值比原来的路径权值要小那么就采用新的路径,否者沿用旧的路径。

class Graph {
    private final int MAX_VERTS = 8;
    private final int INIFITY = -1;
    private Vertex[] vertexList;
    private int adjMat[][];
    private int nVerts;
    private int currentVert;
    private PriorityQ thePQ;
    private int nTree;

    public Graph() {
        vertexList = new Vertex[MAX_VERTS];
        adjMat = new int[MAX_VERTS][MAX_VERTS];
        nVerts = 0;
        for (int i = 0; i < MAX_VERTS; i++) {
            for (int j = 0; j < MAX_VERTS; j++) {
                adjMat[i][j] = INIFITY;
            }
        }
        thePQ = new PriorityQ();
    }

    public void addVertex(char lab) {
        vertexList[nVerts++] = new Vertex(lab);
    }

    public void addEdge(int start, int end, int weight) {
        adjMat[start][end] = weight;
        adjMat[end][start] = weight;
    }

    public void displayVertex(int v) {
        System.out.println(vertexList[v].label);
    }

    public void mstw() {
        currentVert = 0;
        while (nTree < nVerts - 1) {
            vertexList[currentVert].isInTree = true;
            nTree++;
            for (int j = 0; j < nVerts; j++) {
                if (j == currentVert) {
                    continue;
                }
                if (vertexList[j].isInTree) {
                    continue;
                }
                int distance = adjMat[currentVert][j];
                if (distance == INIFITY) {
                    continue;
                }
                putInPQ(j, distance);
            }
            if (thePQ.size() == 0) {
                System.out.println("GRAPH NOT CONNECTED");
                return;
            }
            Edge theEdge = thePQ.removeMin();
            int sourceVert = theEdge.srcVert;
            currentVert = theEdge.destVert;
            System.out.print(vertexList[sourceVert].label);
            System.out.print(vertexList[currentVert].label);
            System.out.print(" ");

        }
        for (int i = 0; i < nVerts; i++) {
            vertexList[i].isInTree = false;
        }
    }

    public void putInPQ(int newVert, int newDist) {
        int queueIndex = thePQ.find(newVert);
        if (queueIndex != -1) {
            Edge tempEdge = thePQ.peekN(queueIndex);
            int oldDist = tempEdge.distance;
            if (oldDist > newDist) {
                thePQ.removeN(queueIndex);
                Edge theEdge = new Edge(currentVert, newVert, newDist);
                thePQ.insert(theEdge);
            }
        } else {
            Edge theEdge = new Edge(currentVert, newVert, newDist);
            thePQ.insert(theEdge);
        }
    }
}

程序测试:

public class MSTWApp {
    public static void main(String[] args) {
        Graph theGraph = new Graph();
        theGraph.addVertex('A');
        theGraph.addVertex('B');
        theGraph.addVertex('C');
        theGraph.addVertex('D');
        theGraph.addVertex('E');
        theGraph.addVertex('F');
        theGraph.addEdge(0,1,6);
        theGraph.addEdge(0,3,4);
        theGraph.addEdge(1,2,10);
        theGraph.addEdge(1,3,7);
        theGraph.addEdge(1,4,7);
        theGraph.addEdge(2,3,8);
        theGraph.addEdge(2,4,5);
        theGraph.addEdge(2,5,6);
        theGraph.addEdge(3,4,12);
        theGraph.addEdge(4,5,7);
        System.out.println("Minimum spanning tree : ");
        theGraph.mstw();
        System.out.println();
    }
}

结果为:

Minimum spanning tree : 
AD AB BE EC CF 

你可能感兴趣的:(数据结构)