日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA)

發(fā)布時間:2025/3/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

之前我們介紹了求最短路徑算法,現(xiàn)在又講最小生成樹算法,這兩個算法有什么區(qū)別呢?

首先要明確,最短路徑和最小生成樹是兩個不同的概念。
最短路徑是對于一個圖的兩個結(jié)點而言的。在一個圖中,結(jié)點A通過某些結(jié)點和邊可以走到結(jié)點B,這些結(jié)點和邊組成的從A到B的路徑中,最短路徑就這些路徑中權(quán)值總和最小的那一條(或多條)。
最短路徑常用算法有:Floyd、Dijkstra、SPFA、A*等

最小生成樹是對于一個圖本身而言的。對于一個有n個結(jié)點的無向連通圖,必然可以去掉某些邊,使得最終剩下n-1條邊,與n個結(jié)點共同組成原圖的一個生成樹,而最小生成樹就是所有可能的生成樹中n-1條邊的權(quán)值總和最小的那一個(或多個)。
常用算法有:Kruskal、Prim


我們現(xiàn)在在1號位置,我們要走遍圖中的所有頂點,各條路上的數(shù)值即為權(quán)值,求這個過程中的最小權(quán)值是多少?

Input:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
Output:
19

Kruskal

又稱 “加邊法”
適用于稀疏圖
時間復雜度:O(MlogN),但通常邊M的數(shù)目要比頂點N的數(shù)目多很多,所以最終時間復雜度為O(MlogM)

import java.util.Scanner;class edge {int u, v, w;edge(int u, int v, int w) {this.u = u;this.v = v;this.w = w;} } public class Kruskal {static edge[] e = new edge[10];static int n, m;static int[] f = new int[7];static int sum = 0;static int count = 0;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= m; i++) {int a = input.nextInt();int b = input.nextInt();int c = input.nextInt();e[i] = new edge(a, b, c);}/*** 按權(quán)值排序* */quicksort(e, 1, m);for (int i = 1; i <= n; i++) {f[i] = i;}kruskal();System.out.println(sum);}private static void kruskal() {/*** 從小到大枚舉每一條邊* */for (int i = 1; i <= m; i++) {/*** 檢查一條邊的兩個頂點是否已經(jīng)連通,即判斷是否在同一個集合中* */if (merge(e[i].u, e[i].v)) {count++;sum = sum + e[i].w;}/*** 選到n-1邊之后,退出循環(huán)* */if (count == n - 1) {break;}}}public static int partition(edge[] a, int p, int q) {int x = a[p].w;int i = p;for (int j = p+1; j <= q; j++) {if (a[j].w <= x) {i += 1;edge temp = a[i];a[i] = a[j];a[j] = temp;}}edge temp = a[p];a[p] = a[i];a[i] = temp;return i;}public static void quicksort(edge[] a,int p, int q) {if (p < q) {int r = partition(a ,p ,q);quicksort(a, p, r - 1);quicksort(a, r + 1, q);}}private static int getf(int v) {if (f[v] == v) {return v;} else {/*** 壓縮路徑,每次函數(shù)返回時,將該位置的編號轉(zhuǎn)成祖宗編號* */f[v] = getf(f[v]);return f[v];}}private static boolean merge(int v, int u) {int t1 = getf(v);int t2 = getf(u);/*** 判斷祖先是否相同* */if (t1 != t2) {/*** 靠左原則* */f[t2] = t1;return true;}return false;} }

prim

又稱 “加點法”
適用于稠密圖
時間復雜度:O(N^2)

import java.util.Scanner;public class prim {static int[][] e = new int[7][7];static int[] book = new int[7];static int[] dis = new int[7];static int count = 0;static int sum = 0;static int n, m;static int min, mark;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;} else {e[i][j] = 99999999;}}}for (int i = 1; i <= m; i++) {int a = input.nextInt();int b = input.nextInt();int c = input.nextInt();e[a][b] = c;e[b][a] = c;}for (int i = 1; i <= n; i++) {dis[i] = e[1][i];}book[1] = 1;prime();System.out.println(sum);}private static void prime() {count++;while (count < n) {min = 99999999;for (int i = 1; i <= n; i++) {if(book[i] == 0 && dis[i] < min) {min = dis[i];mark = i;}}book[mark] = 1;count++;sum += dis[mark];for (int i = 1; i <= n; i++) {if (book[i] == 0 && dis[i] > e[mark][i]) {dis[i] = e[mark][i];}}}} }

而如果借助“堆”,每次選邊的時間復雜度可以變?yōu)镺(logM)

方法:
數(shù)組dis用來記錄生成樹到各個頂點的距離。
數(shù)組h是一個最小堆,堆里面存儲的是頂點的編號。這里不是按照頂點編號的大小創(chuàng)建的,而是按照頂點在數(shù)組dis中對應(yīng)的值建立這個最小堆。
**數(shù)組po**s用來記錄每個頂點的最小堆的位置
下面的左圖代表,
1號結(jié)點到2號點的距離為1
1號結(jié)點到3號點的距離為2
1號結(jié)點到6號點的距離為max
1號結(jié)點到4號點的距離為max
1號結(jié)點到5號點的距離為max
下面的右圖代表
dis數(shù)組存放了左圖的信息
h數(shù)組存放堆
poa數(shù)組存放了頂點對應(yīng)在堆中的位置

import java.util.Scanner; public class prim2 {static int[] book = new int[7];static int[] dis = new int[7];static int[] h = new int[7];static int[] pos = new int[7];static int[] u = new int[19];static int[] v = new int[19];static int[] w = new int[19];static int[] first = new int[7];static int[] next = new int[19];static int count = 0, size;static int sum = 0;static int n, m;static int min, mark, mark2;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= m; i++) {u[i] = input.nextInt();v[i] = input.nextInt();w[i] = input.nextInt();}/*** 無向圖需要再將所有邊反向存儲一次* */for (int i = m + 1; i <= 2 * m; i++) {u[i] = v[i - m];v[i] = u[i - m];w[i] = w[i - m];}/*** 鄰接表* */for (int i = 1; i <= n; i++) {first[i] = -1;}for (int i = 1; i <= 2 * m; i++) {next[i] = first[u[i]];first[u[i]] = i;}prime();System.out.println(sum);}private static void prime() {book[1] = 1;count++;dis[1] = 0;/*** 初始化dis數(shù)組,存放1到其他各點的距離* */for (int i = 2; i <= n; i++) {dis[i] = 99999999;}mark2 = first[1];while (mark2 != -1) {dis[v[mark2]] = w[mark2];mark2 = next[mark2];}/*** 初始化堆* */size = n;for (int i = 1; i <= size; i++) {h[i] = i;pos[i] = i;}for (int i = size / 2; i >= 1; i--) {siftdown(i);}/*** 直接彈出堆頂元素* */pop();while (count < n) {mark = pop();book[mark] = 1;count++;sum += dis[mark];/*** 掃描mark的所有邊,以j為中間節(jié)點,進行松弛* */mark2 = first[mark];while (mark2 != -1) {if (book[v[mark2]] == 0 && dis[v[mark2]] > w[mark2]) {dis[v[mark2]] = w[mark2];/*** 對該點在堆中進行向上調(diào)整,pos[v[k]]是訂點v[k]在堆中的位置* */siftup(pos[v[mark2]]);}mark2 = next[mark2];}}}private static void siftup(int i) {int flag = 0;/*** 堆頂* */if (i == 1) {return;}while (i != 1 && flag == 0) {/*** 當前節(jié)點是否小于父結(jié)點* */if (dis[h[i]] < dis[h[i/2]]) {swap(i, i/2);} else {flag = 1;}/*** 向上調(diào)整* */i = i/2;}}private static void swap(int x, int y) {int temp = h[x];h[x] = h[y];h[y] = temp;temp = pos[h[x]];pos[h[x]] = pos[h[y]];pos[h[y]] = temp;}private static void siftdown(int i) {int t, flag = 0;while (i * 2 <= size && flag == 0) {if (dis[h[i]] > dis[h[i*2]]) {t = i * 2;} else {t = i;}if (i * 2 + 1 <= size) {if (dis[h[t]] > dis[h[i * 2 + 1]]) {t = i * 2 + 1;}}if (t != i) {swap(t, i);i = t;} else {flag = 1;}}}private static int pop() {/*** 記錄棧頂元素* */int t = h[1];pos[t] = 0;/*** 將堆底元素賦到堆頂* */h[1] = h[size];pos[h[1]] = 1;/*** 棧元素數(shù)目減 1* */size--;siftdown(1);return t;} } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。