Java實現(xiàn)最小生成樹MST的兩種解法
一、prim算法
時間復(fù)雜度較之kruskal較高
通俗的解釋就是:
(1)從哪個點開始生成最小生成樹都一樣,最后的權(quán)值都是相同的
(2)從哪個點開始,先標記這個點是訪問過的,用visited數(shù)組表示所有節(jié)點的訪問情況
(3)訪問節(jié)點開始都每個沒訪問結(jié)點的距離選取形成的邊的權(quán)值最小值
綜合以上三點就是我們prim算法寫代碼實現(xiàn)的重要思路
代碼實現(xiàn):
package Prim; import java.util.Arrays; public class PrimAlgorithm { public static void main(String[] args) { //測試看看圖是否創(chuàng)建ok char[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'}; int verxs = data.length; //鄰接矩陣的關(guān)系使用二維數(shù)組表示,10000這個大數(shù),表示兩個點不聯(lián)通 int[][] weight = new int[][]{ {10000, 5, 7, 10000, 10000, 10000, 2}, {5, 10000, 10000, 9, 10000, 10000, 3}, {7, 10000, 10000, 10000, 8, 10000, 10000}, {10000, 9, 10000, 10000, 10000, 4, 10000}, {10000, 10000, 8, 10000, 10000, 5, 4}, {10000, 10000, 10000, 4, 5, 10000, 6}, {2, 3, 10000, 10000, 4, 6, 10000},}; MGraph mGraph = new MGraph(verxs); MinTree minTree = new MinTree(); minTree.createGraph(mGraph, verxs, data, weight); minTree.showGraph(mGraph); minTree.Prim(mGraph, 0); } } class MinTree { /** * 創(chuàng)造圖 * @param graph 圖對象 * @param verxs 圖節(jié)點個數(shù) * @param data 圖每個頂點的數(shù)據(jù)值 * @param weight 圖的邊(鄰接矩陣) */ public void createGraph(MGraph graph, int verxs, char[] data, int[][] weight) { int i, j; for (i = 0; i < verxs; i++) { graph.data[i] = data[i]; for (j = 0; j < verxs; j++) { graph.weight[i][j] = weight[i][j]; } } } // 顯示圖的鄰接矩陣 public void showGraph(MGraph graph) { for (int[] link : graph.weight) { System.out.println(Arrays.toString(link)); } } /** * 編寫prim算法 * * @param graph 圖對象 * @param v 從哪個節(jié)點開始生成最小生成樹 */ public void Prim(MGraph graph, int v) { //定義一個數(shù)組,判斷節(jié)點是不是被訪問過了 int[] visited = new int[graph.verxs]; //v這個點已經(jīng)被訪問了,從這個點開始訪問 visited[v] = 1; //找到節(jié)點下標 int h1 = -1; int h2 = -1; int minWeight = 10000;//定義初始值為最大值,只要出現(xiàn)小的就會替換 int sum = 0; // 從1開始循環(huán),相當于就是生成graph.verx - 1條邊 for (int k = 1; k < graph.verxs; k++) { for (int i = 0; i < graph.verxs; i++) {//遍歷已經(jīng)訪問過的點 if (visited[i] == 1){ for (int j = 0; j < graph.verxs; j++) {//遍歷沒有訪問過的點 //在未訪問點中尋找所有與訪問過的點相連的邊中權(quán)值最小值 if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) { minWeight = graph.weight[i][j]; h1 = i; h2 = j; } } } } sum += minWeight; // 求最小生成熟的總權(quán)值 //此時已經(jīng)找到一條邊是最小了 System.out.println("邊<" + graph.data[h1] + "," + graph.data[h2] + "> 權(quán)值:" + minWeight); //然后標記點 visited[h2] = 1; //將權(quán)值重新變成最大值 minWeight = 10000; } System.out.println("最小生成樹的權(quán)值是:" + sum); } } // 圖 class MGraph { int verxs; // 表示圖節(jié)點個數(shù) char[] data; // 表示節(jié)點數(shù)據(jù) int[][] weight; // 表示邊 public MGraph(int verxs) { this.verxs = verxs; data = new char[verxs]; weight = new int[verxs][verxs]; } }
二、kruskal算法
時間復(fù)雜度低一些,但是代碼量會大一些
對克魯斯卡爾算法的通俗解釋:
(1)對每條邊的權(quán)值進行排序
(2)按照從小到大依次選取邊構(gòu)成最小生成樹,但是要注意是否構(gòu)成回路,樹的概念是不能生成回路
(3)此處用的方法比較巧妙使用了getEnd方法來判斷兩者終點是不是一樣,用ends數(shù)組保存最小生成樹中每個頂點的終點
代碼實現(xiàn):
package Kruskal; import java.util.Arrays; public class KruskalCase { private int edgeNum; //邊的個數(shù) private char[] vertexs; //頂點數(shù)組 private int[][] matrix; //鄰接矩陣 //使用 INF 表示兩個頂點不能連通 private static final int INF = Integer.MAX_VALUE; public static void main(String[] args) { char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; //克魯斯卡爾算法的鄰接矩陣 int matrix[][] = { /*A*//*B*//*C*//*D*//*E*//*F*//*G*/ /*A*/ {0, 12, INF, INF, INF, 16, 14}, /*B*/ {12, 0, 10, INF, INF, 7, INF}, /*C*/ {INF, 10, 0, 3, 5, 6, INF}, /*D*/ {INF, INF, 3, 0, 4, INF, INF}, /*E*/ {INF, INF, 5, 4, 0, 2, 8}, /*F*/ {16, 7, 6, INF, 2, 0, 9}, /*G*/ {14, INF, INF, INF, 8, 9, 0}}; //大家可以在去測試其它的鄰接矩陣,結(jié)果都可以得到最小生成樹. //創(chuàng)建KruskalCase 對象實例 KruskalCase kruskalCase = new KruskalCase(vertexs, matrix); //輸出構(gòu)建的 kruskalCase.print(); kruskalCase.kruskal(); } //構(gòu)造器 public KruskalCase(char[] vertexs, int[][] matrix) { //初始化頂點數(shù)和邊的個數(shù) int vlen = vertexs.length; //初始化頂點, 復(fù)制拷貝的方式 this.vertexs = new char[vlen]; for (int i = 0; i < vertexs.length; i++) { this.vertexs[i] = vertexs[i]; } //初始化邊, 使用的是復(fù)制拷貝的方式 this.matrix = new int[vlen][vlen]; for (int i = 0; i < vlen; i++) { for (int j = 0; j < vlen; j++) { this.matrix[i][j] = matrix[i][j]; } } //統(tǒng)計邊的條數(shù) for (int i = 0; i < vlen; i++) { for (int j = i + 1; j < vlen; j++) { if (this.matrix[i][j] != INF) { edgeNum++; } } } } public void kruskal() { int index = 0; //表示最后結(jié)果數(shù)組的索引 int[] ends = new int[edgeNum]; //用于保存"已有最小生成樹" 中的每個頂點在最小生成樹中的終點 //創(chuàng)建結(jié)果數(shù)組, 保存最后的最小生成樹 EData[] rets = new EData[edgeNum]; //獲取圖中 所有的邊的集合 , 一共有12邊 EData[] edges = getEdges(); System.out.println("圖的邊的集合=" + Arrays.toString(edges) + " 共" + edges.length); //12 //按照邊的權(quán)值大小進行排序(從小到大) sortEdges(edges); //遍歷edges 數(shù)組,將邊添加到最小生成樹中時,判斷是準備加入的邊否形成了回路,如果沒有,就加入 rets, 否則不能加入 for (int i = 0; i < edgeNum; i++) { //獲取到第i條邊的第一個頂點(起點) int p1 = getPosition(edges[i].start); //p1=4 //獲取到第i條邊的第2個頂點 int p2 = getPosition(edges[i].end); //p2 = 5 //獲取p1這個頂點在已有最小生成樹中的終點 int m = getEnd(ends, p1); //m = 4 //獲取p2這個頂點在已有最小生成樹中的終點 int n = getEnd(ends, p2); // n = 5 //是否構(gòu)成回路 if (m != n) { //沒有構(gòu)成回路 ends[m] = n; // 設(shè)置m 在"已有最小生成樹"中的終點 <E,F> [0,0,0,0,5,0,0,0,0,0,0,0] rets[index++] = edges[i]; //有一條邊加入到rets數(shù)組 } } //<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。 //統(tǒng)計并打印 "最小生成樹", 輸出 rets System.out.println("最小生成樹為"); for (int i = 0; i < index; i++) { System.out.println(rets[i]); } } //打印鄰接矩陣 public void print() { System.out.println("鄰接矩陣為: \n"); for (int i = 0; i < vertexs.length; i++) { for (int j = 0; j < vertexs.length; j++) { System.out.printf("%12d", matrix[i][j]); } System.out.println();//換行 } } /** * 功能:對邊進行排序處理, 冒泡排序 * * @param edges 邊的集合 */ private void sortEdges(EData[] edges) { for (int i = 0; i < edges.length - 1; i++) { for (int j = 0; j < edges.length - 1 - i; j++) { if (edges[j].weight > edges[j + 1].weight) {//交換 EData tmp = edges[j]; edges[j] = edges[j + 1]; edges[j + 1] = tmp; } } } } /** * @param ch 頂點的值,比如'A','B' * @return 返回ch頂點對應(yīng)的下標,如果找不到,返回-1 */ private int getPosition(char ch) { for (int i = 0; i < vertexs.length; i++) { if (vertexs[i] == ch) {//找到 return i; } } //找不到,返回-1 return -1; } /** * 功能: 獲取圖中邊,放到EData[] 數(shù)組中,后面我們需要遍歷該數(shù)組 * 是通過matrix 鄰接矩陣來獲取 * EData[] 形式 [['A','B', 12], ['B','F',7], .....] * * @return */ private EData[] getEdges() { int index = 0; EData[] edges = new EData[edgeNum]; for (int i = 0; i < vertexs.length; i++) { for (int j = i + 1; j < vertexs.length; j++) { if (matrix[i][j] != INF) { edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]); } } } return edges; } /** * 功能: 獲取下標為i的頂點的終點(), 用于后面判斷兩個頂點的終點是否相同 * * @param ends : 數(shù)組就是記錄了各個頂點對應(yīng)的終點是哪個,ends 數(shù)組是在遍歷過程中,逐步形成 * @param i : 表示傳入的頂點對應(yīng)的下標 * @return 返回的就是 下標為i的這個頂點對應(yīng)的終點的下標, 一會回頭還有來理解 */ private int getEnd(int[] ends, int i) { // i = 4 [0,0,0,0,5,0,0,0,0,0,0,0] while (ends[i] != 0) { i = ends[i]; } return i; } } //創(chuàng)建一個類EData ,它的對象實例就表示一條邊 class EData { char start; //邊的一個點 char end; //邊的另外一個點 int weight; //邊的權(quán)值 //構(gòu)造器 public EData(char start, char end, int weight) { this.start = start; this.end = end; this.weight = weight; } //重寫toString, 便于輸出邊信息 @Override public String toString() { return "EData [<" + start + ", " + end + ">= " + weight + "]"; } }
到此這篇關(guān)于Java實現(xiàn)最小生成樹MST的兩種解法的文章就介紹到這了,更多相關(guān)Java最小生成樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?IoC容器Bean作用域的singleton與prototype使用配置
這篇文章主要為大家介紹了Spring?IoC容器Bean作用域的singleton與prototype使用配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12MyBatis JdbcType 與Oracle、MySql數(shù)據(jù)類型對應(yīng)關(guān)系說明
這篇文章主要介紹了MyBatis JdbcType 與Oracle、MySql數(shù)據(jù)類型對應(yīng)關(guān)系說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09JAVA中 redisTemplate 和 jedis的配合使用操作
這篇文章主要介紹了JAVA中 redisTemplate 和 jedis的配合使用操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Java中ThreadLocal使用原理及Synchronized區(qū)別
ThreadLocal叫做線程變量,本文詳細的介紹了ThreadLocal使用原理及Synchronized區(qū)別,有需要的朋友可以參考一下,希望對你有所幫助。2023-05-05Java線程中斷機制interrupt、isInterrupted、interrupted方法詳解
這篇文章主要介紹了Java線程中斷機制interrupt、isInterrupted、interrupted方法詳解,一個線程不應(yīng)該由其他線程來強制中斷或停止,而是應(yīng)該由線程自己自行停止,所以,Thread.stop、Thread.suspend、Thread. resume都已經(jīng)被廢棄了,需要的朋友可以參考下2024-01-01Java讀取json數(shù)據(jù)并存入數(shù)據(jù)庫的操作代碼
很多朋友問大佬們JAVA怎么把json存入數(shù)據(jù)庫啊,這一問題就把我難倒了,糾結(jié)如何操作呢,下面小編把我的經(jīng)驗分享給大家,感興趣的朋友一起看看吧2021-08-08