自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

數(shù)據(jù)結(jié)構(gòu)與算法之最小生成樹,秒懂!

開發(fā) 前端 算法
在數(shù)據(jù)結(jié)構(gòu)與算法的圖論中,(生成)最小生成樹算法是一種常用并且和生活貼切比較近的一種算法。但是可能很多人對(duì)概念不是很清楚,什么是最小生成樹?

[[426679]]

前言

在數(shù)據(jù)結(jié)構(gòu)與算法的圖論中,(生成)最小生成樹算法是一種常用并且和生活貼切比較近的一種算法。但是可能很多人對(duì)概念不是很清楚,什么是最小生成樹?

  • 一個(gè)有 n 個(gè)結(jié)點(diǎn)的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個(gè)結(jié)點(diǎn),并且有保持圖連通的最少的邊。最小生成樹可以用kruskal(克魯斯卡爾)算法或prim(普里姆)算法求出。

通俗易懂的講就是最小生成樹包含原圖的所有節(jié)點(diǎn)而只用最少的邊和最小的權(quán)值距離。因?yàn)閚個(gè)節(jié)點(diǎn)最少需要n-1個(gè)邊聯(lián)通,而距離就需要采取某種策略選擇恰當(dāng)?shù)倪叀?/p>

學(xué)習(xí)最小生成樹實(shí)現(xiàn)算法之前我們要先高清最小生成樹的結(jié)構(gòu)和意義所在。咱么首先根據(jù)一些圖更好的祝你理解。

一個(gè)故事

在城市道路規(guī)劃中,是一門很需要科學(xué)的研究(只是假設(shè)學(xué)習(xí)不必當(dāng)真)。在公路時(shí)代城市聯(lián)通的主要矛盾是時(shí)間慢,而造價(jià)相比運(yùn)輸時(shí)間是次要矛盾。所以在公路時(shí)代我們盡量使得城市能夠直接聯(lián)通,縮短城市聯(lián)系時(shí)間,而稍微考慮建路成本!隨著科技發(fā)展、高級(jí)鐵路、信息傳輸相比公路運(yùn)輸快非常非常多,從而事件的主要矛盾從運(yùn)輸時(shí)間轉(zhuǎn)變?yōu)樵靸r(jià)成本,故有時(shí)會(huì)關(guān)注聯(lián)通所有點(diǎn)的路程(最短),這就用到最小生成樹算法。

城市道路鋪設(shè)可能經(jīng)歷以下幾個(gè)階段。

  • 初始,各個(gè)城市沒有高速公路(鐵路)。
  • 政府打算各個(gè)城市鋪設(shè)公路(鐵路),每個(gè)城市都想成為交通樞紐,快速到達(dá)其他城市,但每個(gè)城市都有這種想法,如果實(shí)現(xiàn)下去造價(jià)太昂貴。并且造成巨大浪費(fèi)。
  • 最終國(guó)家選擇一些主要城市進(jìn)行聯(lián)通,有個(gè)別城市只能稍微繞道而行,而繞道太遠(yuǎn)的、人流量多的國(guó)家考慮新建公路(鐵路),適當(dāng)提高效率。

不過上面鐵路規(guī)劃上由于龐大的人口可能不能夠滿足與"有鐵路"這個(gè)需求,人們對(duì)速度、距離、直達(dá)等條件一直在追求中……

但是你可以想象這個(gè)場(chǎng)景:有些東西造假非常非常昂貴,使用效率非常高,我這里假設(shè)成黃金鑲鉆電纜 鋪設(shè),所以各個(gè)城市只要求不給自己落下,能通上就行(沒人敢跳了吧)。

要從有環(huán)圖中選取代價(jià)和最小的路線一方面代價(jià)最小(總距離最小最省黃金)另一方面聯(lián)通所有城市。

然而根據(jù)上圖我們可以得到以下最小生成樹,但是最么生成這個(gè)最小生成樹,就是下面要講的了。

而類似的還有局部區(qū)域島嶼聯(lián)通修橋,海底通道這些高成本的都多多少少會(huì)運(yùn)用。

Kruskal算法

上面介紹了最小生成樹是什么,現(xiàn)在需要掌握和理解最小生成樹如何形成。給你一個(gè)圖,用一個(gè)規(guī)則生成一個(gè)最小生成樹。而在實(shí)現(xiàn)最小生成樹方面有prim和kruskal算法,這兩種算法的策略有所區(qū)別,但是時(shí)間復(fù)雜度一致。

Kruskal算法,和前面講到的并查集關(guān)系很大,它的主要思想為:

  • 先構(gòu)造一個(gè)只含 n 個(gè)頂點(diǎn)、而邊集為空的子圖,把子圖中各個(gè)頂點(diǎn)看成各棵樹上的根結(jié)點(diǎn),之后,從網(wǎng)的邊集 E 中選取一條權(quán)值最小的邊,若該條邊的兩個(gè)頂點(diǎn)分屬不同的樹,則將其加入子圖,即把兩棵樹合成一棵樹,反之,若該條邊的兩個(gè)頂點(diǎn)已落在同一棵樹上,則不可取,而應(yīng)該取下一條權(quán)值最小的邊再試之。依次類推,直到森林中只有一棵樹,也即子圖中含有 n-1 條邊為止。

簡(jiǎn)而言之,Kruskal算法進(jìn)行調(diào)度的單位是邊,它的信仰為:所有邊能小則小,算法的實(shí)現(xiàn)方面要用到并查集判斷兩點(diǎn)是否在同一集合。

而算法的具體步驟為:

  1. 將圖中所有邊對(duì)象(邊長(zhǎng)、兩端點(diǎn))依次加入集合(優(yōu)先隊(duì)列)q1中。初始所有點(diǎn)相互獨(dú)立。
  2. 取出集合(優(yōu)先隊(duì)列)q1最小邊,判斷邊的兩點(diǎn)是否聯(lián)通。
  3. 如果聯(lián)通說明兩個(gè)點(diǎn)已經(jīng)有其它邊將兩點(diǎn)聯(lián)通了,跳過,如果不連通,則使用union(并查集合并)將兩個(gè)頂點(diǎn)合并,這條邊被使用(可以儲(chǔ)存或者計(jì)算數(shù)值)。
  4. 重復(fù)2,3操作直到集合(優(yōu)先隊(duì)列)q1為空。此時(shí)被選擇的邊構(gòu)成最小生成樹。

Prim算法除了Kruskal算法以外,普里姆算法(Prim算法)也是常用的最小生成樹算法。雖然在效率上差不多。但是貪心的方式和Kruskal完全不同。

prim算法的核心信仰是:從已知擴(kuò)散尋找最小。它的實(shí)現(xiàn)方式和Dijkstra算法相似但稍微有所區(qū)別,Dijkstra是求單源最短路徑,而每計(jì)算一個(gè)點(diǎn)需要對(duì)這個(gè)點(diǎn)重新更新距離,而prim不用更新距離。直接找已知點(diǎn)的鄰邊最小加入即可!prim和kruskal算法都是從邊入手處理。

對(duì)于具體算法具體步驟,大致為:

  1. 尋找圖中任意點(diǎn),以它為起點(diǎn),它的所有邊V加入集合(優(yōu)先隊(duì)列)q1,設(shè)置一個(gè)boolean數(shù)組bool[]標(biāo)記該位置(邊有兩個(gè)點(diǎn),每次加入沒有被標(biāo)記那個(gè)點(diǎn)的所有邊)。
  2. 從集合q1找到距離最小的那個(gè)邊v1并 判斷邊是否存在未被標(biāo)記的一點(diǎn)`p` ,如果p不存在說明已經(jīng)確定過那么跳過當(dāng)前邊處理,如果未被標(biāo)(訪問)記那么標(biāo)記該點(diǎn)p,并且與p相連的未知點(diǎn)(未被標(biāo)記)構(gòu)成的邊加入集合q1, 邊v1(可以進(jìn)行計(jì)算距離之類,該邊構(gòu)成最小生成樹) .
  3. 重復(fù)1,2直到q1為空,構(gòu)成最小生成樹 !

大體步驟圖解為:

因?yàn)閜rim從開始到結(jié)束一直是一個(gè)整體在擴(kuò)散,所以不需要考慮兩棵樹合并的問題,在這一點(diǎn)實(shí)現(xiàn)上稍微方便了一點(diǎn)。

當(dāng)然,要注意的是最小生成樹并不唯一,甚至同一種算法生成的最小生成樹都可能有所不同,但是相同的是無論生成怎樣的最小生成樹:

  • 能夠保證所有節(jié)點(diǎn)連通(能夠滿足要求和條件)
  • 能夠保證所有路徑之和最小(結(jié)果和目的相同)
  • 最小生成樹不唯一,可能多樣的

代碼實(shí)現(xiàn)

上面分析了邏輯實(shí)現(xiàn)。下面我們用代碼簡(jiǎn)單實(shí)現(xiàn)上述的算法。

prim

  1. package 圖論; 
  2.  
  3. import java.util.ArrayList; 
  4. import java.util.Arrays; 
  5. import java.util.Comparator; 
  6. import java.util.List; 
  7. import java.util.PriorityQueue; 
  8. import java.util.Queue; 
  9.  
  10. public class prim { 
  11.  
  12.     public static void main(String[] args) { 
  13.         int minlength=0;//最小生成樹的最短路徑長(zhǎng)度 
  14.         int max=66666; 
  15.         String cityname[]= {"北京","武漢","南京","上海","杭州","廣州","深圳"}; 
  16.         int city[][]= { 
  17.                 { max, 8, 7, maxmaxmaxmax }, //北京和武漢南京聯(lián)通 
  18.                 { 8, max,6, max,9, 8,max }, //武漢——北京、南京、杭州、廣州 
  19.                 { 7, 6, max, 3,4, max,max }, //南京——北京、武漢、上海、杭州 
  20.                 { maxmax,3, max,2, max,max }, //上海——南京、杭州 
  21.                 { max, 9,4, 2,maxmax,10 }, //杭州——武漢、南京、上海、深圳 
  22.                 { max, 8,maxmax,maxmax,2 }, //廣州——武漢、深圳 
  23.                 { maxmax,maxmax,10,2,max }//深圳——杭州、廣州 
  24.         };// 地圖 
  25.  
  26.         boolean istrue[]=new boolean[7]; 
  27.         //南京 
  28.         Queue<side>q1=new PriorityQueue<side>(new Comparator<side>() { 
  29.             public int compare(side o1, side o2) { 
  30.                 // TODO Auto-generated method stub 
  31.                 return o1.lenth-o2.lenth; 
  32.             } 
  33.         }); 
  34.         for(int i=0;i<7;i++) 
  35.         { 
  36.             if(city[2][i]!=max
  37.             { 
  38.                 istrue[2]=true
  39.                 q1.add(new side(city[2][i], 2, i)); 
  40.             } 
  41.         }        
  42.         while(!q1.isEmpty()) 
  43.         { 
  44.             side newside=q1.poll();//拋出 
  45.             if(istrue[newside.point1]&&istrue[newside.point2]) 
  46.             { 
  47.                 continue
  48.             } 
  49.             else { 
  50.                 if(!istrue[newside.point1]) 
  51.                 { 
  52.                     istrue[newside.point1]=true
  53.                     minlength+=city[newside.point1][newside.point2]; 
  54.                     System.out.println(cityname[newside.point1]+" "+cityname[newside.point2]+" 聯(lián)通"); 
  55.                     for(int i=0;i<7;i++) 
  56.                     { 
  57.                         if(!istrue[i]) 
  58.                         { 
  59.                             q1.add(new side(city[newside.point1][i],newside.point1,i)); 
  60.                         } 
  61.                     } 
  62.                 } 
  63.                 else { 
  64.                     istrue[newside.point2]=true
  65.                     minlength+=city[newside.point1][newside.point2]; 
  66.                     System.out.println(cityname[newside.point2]+" "+cityname[newside.point1]+" 聯(lián)通"); 
  67.                     for(int i=0;i<7;i++) 
  68.                     { 
  69.                         if(!istrue[i]) 
  70.                         { 
  71.                             q1.add(new side(city[newside.point2][i],newside.point2,i)); 
  72.                         } 
  73.                     } 
  74.                 } 
  75.             } 
  76.  
  77.         } 
  78.         System.out.println(minlength);       
  79.     } 
  80.  
  81.     static class side//邊 
  82.     { 
  83.         int lenth; 
  84.         int point1; 
  85.         int point2; 
  86.         public side(int lenth,int p1,int p2) { 
  87.             this.lenth=lenth; 
  88.             this.point1=p1; 
  89.             this.point2=p2; 
  90.         } 
  91.     } 
  92.  

輸出結(jié)果:

  • 上海 南京 聯(lián)通
  • 杭州 上海 聯(lián)通
  • 武漢 南京 聯(lián)通
  • 北京 南京 聯(lián)通
  • 廣州 武漢 聯(lián)通
  • 深圳 廣州 聯(lián)通
  • 28

Kruskal:

  1. package 圖論; 
  2.  
  3. import java.util.Comparator; 
  4. import java.util.PriorityQueue; 
  5. import java.util.Queue; 
  6.  
  7. import 圖論.prim.side; 
  8. /* 
  9.  * 作者:bigsai(公眾號(hào)) 
  10.  */ 
  11. public class kruskal { 
  12.  
  13.     static int tree[]=new int[10];//bing查集 
  14.     public static void init() { 
  15.         for(int i=0;i<10;i++)//初始 
  16.         { 
  17.             tree[i]=-1; 
  18.         } 
  19.     } 
  20.     public static int search(int a)//返回頭節(jié)點(diǎn)的數(shù)值 
  21.     { 
  22.         if(tree[a]>0)//說明是子節(jié)點(diǎn) 
  23.         { 
  24.             return tree[a]=search(tree[a]);//路徑壓縮 
  25.         } 
  26.         else 
  27.             return a; 
  28.     } 
  29.     public static void union(int a,int b)//表示 a,b所在的樹合并小樹合并大樹(不重要) 
  30.     { 
  31.         int a1=search(a);//a根 
  32.         int b1=search(b);//b根 
  33.         if(a1==b1) {//System.out.println(a+"和"+b+"已經(jīng)在一棵樹上"); 
  34.         } 
  35.         else { 
  36.         if(tree[a1]<tree[b1])//這個(gè)是負(fù)數(shù),為了簡(jiǎn)單減少計(jì)算,不在調(diào)用value函數(shù) 
  37.         { 
  38.             tree[a1]+=tree[b1];//個(gè)數(shù)相加  注意是負(fù)數(shù)相加 
  39.             tree[b1]=a1;       //b樹成為a的子樹,直接指向a; 
  40.         } 
  41.         else 
  42.         { 
  43.             tree[b1]+=tree[a1];//個(gè)數(shù)相加  注意是負(fù)數(shù)相加 
  44.             tree[a1]=b1;       //b樹成為a的子樹,直接指向a; 
  45.         } 
  46.         } 
  47.     } 
  48.     public static void main(String[] args) { 
  49.         // TODO Auto-generated method stub 
  50.         init(); 
  51.         int minlength=0;//最小生成樹的最短路徑長(zhǎng)度 
  52.         int max=66666; 
  53.         String cityname[]= {"北京","武漢","南京","上海","杭州","廣州","深圳"}; 
  54.         boolean jud[][]=new boolean[7][7];//加入邊需要防止重復(fù) 比如 ba和ab等價(jià)的 
  55.         int city[][]= { 
  56.                 { max, 8, 7, maxmaxmaxmax },  
  57.                 { 8, max,6, max,9, 8,max },  
  58.                 { 7, 6, max, 3,4, max,max },  
  59.                 { maxmax,3, max,2, max,max },  
  60.                 { max, 9,4, 2,maxmax,10 },  
  61.                 { max, 8,maxmax,maxmax,2 },  
  62.                 { maxmax,maxmax,10,2,max } 
  63.         };// 地圖 
  64.         boolean istrue[]=new boolean[7]; 
  65.         //南京 
  66.         Queue<side>q1=new PriorityQueue<side>(new Comparator<side>() {//優(yōu)先隊(duì)列存邊+ 
  67.             public int compare(side o1, side o2) { 
  68.                 // TODO Auto-generated method stub 
  69.                 return o1.lenth-o2.lenth; 
  70.             } 
  71.         }); 
  72.         for(int i=0;i<7;i++) 
  73.         { 
  74.             for(int j=0;j<7;j++) 
  75.             { 
  76.                 if(!jud[i][j]&&city[i][j]!=max)//是否加入隊(duì)列 
  77.                 { 
  78.                     jud[i][j]=true;jud[j][i]=true
  79.                     q1.add(new side(city[i][j], i, j)); 
  80.                 } 
  81.             } 
  82.         } 
  83.         while(!q1.isEmpty())//執(zhí)行算法 
  84.         { 
  85.             side newside=q1.poll(); 
  86.             int p1=newside.point1; 
  87.             int p2=newside.point2; 
  88.             if(search(p1)!=search(p2)) 
  89.             { 
  90.                 union(p1, p2); 
  91.                 System.out.println(cityname[p1]+" "+cityname[p2]+" 聯(lián)通"); 
  92.                 minlength+=newside.lenth; 
  93.             } 
  94.         } 
  95.         System.out.println(minlength); 
  96.  
  97.  
  98.     } 
  99.     static class side//邊 
  100.     { 
  101.         int lenth; 
  102.         int point1; 
  103.         int point2; 
  104.         public side(int lenth,int p1,int p2) { 
  105.             this.lenth=lenth; 
  106.             this.point1=p1; 
  107.             this.point2=p2; 
  108.         } 
  109.     } 

輸出結(jié)果

  • 上海 杭州 聯(lián)通
  • 廣州 深圳 聯(lián)通
  • 南京 上海 聯(lián)通
  • 武漢 南京 聯(lián)通
  • 北京 南京 聯(lián)通
  • 武漢 廣州 聯(lián)通
  • 28

總結(jié)

最小生成樹算法理解起來也相對(duì)簡(jiǎn)單,實(shí)現(xiàn)起來也不是很難。Kruskal和Prim主要是貪心算法的兩種角度。一個(gè)從整體開始找最小邊,遇到關(guān)聯(lián)不斷合并,另一個(gè)從局部開始擴(kuò)散找身邊的最小不斷擴(kuò)散直到生成最小生成樹。在學(xué)習(xí)最小生成樹之前最好學(xué)習(xí)一下dijkstra算法和并查集,這樣在實(shí)現(xiàn)起來能夠快一點(diǎn),清晰一點(diǎn)。

力扣1584就是一個(gè)最小生成樹的入門題,不過哪個(gè)有點(diǎn)區(qū)別的就是默認(rèn)所有點(diǎn)是聯(lián)通的,所以需要你剪枝優(yōu)化。這里就不帶大家一起看啦,有問題下面也可交流!

最后,如果你那天真的獲得一大筆資金去修建這么一條昂貴的黃金路線,可以適當(dāng)采取此方法,另外剩下的大批,茍富貴,勿相忘。

 

責(zé)任編輯:姜華 來源: bigsai
相關(guān)推薦

2023-11-27 15:01:21

Prim算法Kruskal算法

2020-10-30 09:56:59

Trie樹之美

2022-09-21 07:57:33

二叉搜索樹排序二叉樹

2022-09-26 07:56:53

AVL算法二叉樹

2021-03-18 08:44:20

Java數(shù)據(jù)結(jié)構(gòu)算法

2017-10-10 16:59:28

Java數(shù)據(jù)結(jié)構(gòu)算法解析

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2023-03-08 08:03:09

數(shù)據(jù)結(jié)構(gòu)算法歸并排序

2023-03-31 08:24:29

數(shù)據(jù)結(jié)構(gòu)算法數(shù)目

2021-04-07 09:26:37

Java數(shù)據(jù)結(jié)構(gòu)算法

2023-10-27 07:04:20

2020-11-02 09:15:47

算法與數(shù)據(jù)結(jié)構(gòu)

2021-03-24 10:41:04

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-12-30 11:12:57

數(shù)據(jù)結(jié)構(gòu)算法爬樓梯

2023-03-02 08:15:13

2023-03-10 08:07:39

數(shù)據(jù)結(jié)構(gòu)算法計(jì)數(shù)排序

2023-03-07 08:02:07

數(shù)據(jù)結(jié)構(gòu)算法數(shù)列

2023-04-27 09:13:20

排序算法數(shù)據(jù)結(jié)構(gòu)

2019-09-09 14:33:17

開發(fā)者技能算法

2023-03-13 10:08:31

數(shù)據(jù)結(jié)構(gòu)算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)