• Prim算法是干什么的?
Prim算法可以計算出一個無向加權圖的最小生成樹
• 什么是最小生成樹?
首先,樹兩個最重要的性質是①用一條邊連接樹中的任意兩點都會產生一個新的環②從樹中刪除一條邊將會得到兩棵獨立的樹,最小生成樹即為連接圖中所有點,且總權重最小的樹。最小生成樹的性質:將圖中的點分為兩個集合,橫跨兩個集合的邊中權重最
小的邊必在最小生成樹中(並不只有權重最小邊在樹中)
• 最小生成樹的性質的證明:
用反證法,如果權重最小的邊 e 不在樹中,那和它橫跨兩個相同的集合且權重比它大的邊 f 樹一定在樹中,如果把 e 加入樹中,再將 f 刪除,則得到一個權重更小的樹,所以 e 必在樹中
• Prim算法如何進行計算?
①選定一個點做為一個集合 a ,剩下的點為另一個集合 b
②將橫跨兩個集合且權重在其中最小的邊加入最小生成樹
③將剛剛加入最小生成樹的邊中不在集合 a 中的點加入集合 a,直到所有的點加入集合 a
具體到代碼如何計算?
1 class MST { 2 private boolean[] marked; //點是否已在樹中 3 private double[] distTo; //點到樹的距離 4 private ArrayList<Edge> mst; //最小生成樹 5 private EdgeWeightedGraph G; //要處理的圖 6 private TreeMap<Double, Edge> pq; //保存一個點到樹距離最短的邊和那個距離 按距離從小到大的優先隊列 7 8 public MST(EdgeWeightedGraph G) { 9 this.G = G; 10 int V = G.V(); 11 marked = new boolean[V]; 12 distTo = new double[V]; 13 mst = new ArrayList<>(); 14 pq = new TreeMap<>(); 15 //將所有點到樹的距離設置為正無窮 16 for (int i = 0; i < distTo.length; i++) 17 distTo[i] = Double.POSITIVE_INFINITY; 18 //0到0的距離為0 19 distTo[0] = 0; 20 visit(0); 21 Edge tEdge; 22 23 while (!pq.isEmpty()) { 24 tEdge = pq.remove(pq.firstKey()); //從優先隊列中取出距離樹最短的邊 25 mst.add(tEdge); //加入樹中 26 //更新優先隊列 27 int v = tEdge.either(); //either()返回邊的任意一個點 28 if (!marked[v]) visit(v); 29 else visit(tEdge.other(v)); //other(v)返回除v外的另一個節點 30 } 31 } 32 /* 33 * 將v的邊加入優先隊列 34 */ 35 private void visit(int v) { 36 marked[v] = true; 37 for (Edge e : G.adj(v)) { //adj(v)返回該點的所有邊 38 int w = e.other(v); 39 double weight = e.weight(); 40 //如果發現了使此點到樹的距離更小的通路,則更新優先隊列 41 if (weight < distTo[w]) { 42 //如果以前已經存過這個點w到最小生成樹的邊了 現在找到了權重更小的所以把它刪除 43 pq.remove(distTo[w]); 44 distTo[w] = weight; //更新最小距離 45 pq.put(weight, e); //插入優先隊列 46 } 47 } 48 } 49 50 public Iterable<Edge> edgs() { return mst;} 51 }
將樹所有鄰近的 點的到樹距離最短的邊 全部加入優先隊列,從隊列中拿出最短的邊,將其加入樹中
pq始終保存着樹旁邊所有的節點到樹的最短距離
①將0加入mst,將0-7,0-2,0-4,0-6加入優先隊列,其中0-7的權重最小
②將7加入mst,將7-1,7-4(因為發現了比0-4到樹距離更小的邊所以刪了0-4,加入7-4),7-5,(7-2不加入因為原來的距離更小),其中7-1權重最小