带权图的最小生成树问题
2017-04-21 15:30
141 查看
带权图的最小生成树问题
假设共有六个定点,分别为命名为0-5,如下图所示:根据图构建出邻接矩阵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 ; } 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
相关文章推荐
- POJ1251(基础的最小代价生成树问题)
- nyoj--38 布线问题(最小生成树)
- POJ 1789 Truck History (prim解决最小生成树问题)
- NYOJ 38 布线问题 最小生成树 prim与Kruskal
- HDOJ 题目38 布线问题(最小生成树)
- 每日一题 No.53 最小生成树问题(Prim算法)
- USTC机试之——最小生成树问题(文件中读取顶点信息并将最小生成树输出到另一文件中)
- Arctic Network POJ - 2349(最小生成树问题)
- nyist 38 布线问题(最小生成树kruskal)
- [最小生成树]最优布线问题
- HDU 1875 最小生成树问题
- NYOJ 32 布线问题(最小生成树)
- 更多和最小生成树相关的问题
- [置顶] 并查集解决最优灌溉问题(最小生成树问题)
- 第一个人的解释:最小生成树中的最短路问题:差分约束系统
- poj 1258 Agri-Net dijkstra迪杰斯特拉算法,最短路径问题的变形,最小生成树
- 最小生成树问题(——模板习题与总结)
- 无向带权图的最小生成树算法——Prim及Kruskal算法思路
- 最小生成树问题
- 最小生成树问题