圖——最小生成樹實現(Kruskal算法,prime算法)

目錄

預備知識:

最小生成樹概念:

Kruskal算法:

代碼實現如下:

測試:

Prime算法 :

代碼實現如下:

測試:

結語:


預備知識:

連通圖:在無向圖中,若從頂點v1到頂點v2有路徑,則稱頂點v1與頂點v2是連通的。如果圖中任意一 對頂點 都是連通的,則稱此圖為連通圖。

生成樹:一個連通圖的生成樹是指一個連通子圖,它含有圖中全部n個頂點,但只有足以構成一棵樹的n-1條邊。一顆有n個頂點的生成樹有且僅有n-1條邊,如果生成樹中再添加一條邊,則必定成環。

并查集:

由于本文章重點不在講述并查集,故下面我簡單描述并查集的作用,各種方法,源碼如下。

并查集的作用:可以將一個數組中的元素分為多個小組的數據結構。

方法:

findRoot(x):查找x的根。

union(int x1, int x2):合并x1和x2。

isSameSet(int x1, int x2):判斷兩個數字 是不是在同一個集合當中。

import java.util.Arrays;public class UnionFindSet {private int[] elem;//底層是數組public UnionFindSet(int n){this.elem = new int[n];Arrays.fill(elem,-1);//整體初始化為-1:代表根}/*** 查找x的根* @param x* @return*/public int findRoot(int x){if(x < 0){throw new IndexOutOfBoundsException("數據不合法");}while(elem[x] >= 0){x = elem[x];}return x;}/*** 合并x1和x2* @param x1* @param x2*/public void union(int x1,int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index1 == index2){//說明x1和x2的根是相同的,不需要進行合并return;}elem[index1] = elem[index1] + elem[index2];elem[index2] = index1;//將x2合并到x1}/*** 判斷兩個數字是不是在同一個集合當中* @param x1* @param x2* @return*/public boolean isSameSet(int x1,int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index1 == index2){return true;}else{return false;}} 
}

最小生成樹概念:

連通圖中的每一棵生成樹,都是原圖的一個極大無環子圖,即:從其中刪去任何一條邊,生成樹 就不在連通;反之,在其中引入任何一條新邊,都會形成一條回路。

若連通圖由n個頂點組成,則其生成樹必含n個頂點和n-1條邊。因此構造最小生成樹的準則有三 條:

(1) 只能使用圖中的邊來構造最小生成樹。

(2)?只能使用恰好n-1條邊來連接圖中的n個頂點。

(3)?選用的n-1條邊不能構成回路。

構造最小生成樹的方法:Kruskal算法和Prim算法。這兩個算法都采用了逐步求解的貪心策略。

貪心算法:是指在問題求解時,總是做出當前看起來最好的選擇。也就是說貪心算法做出的不是整體最優的選擇,而是某種意義上的局部最優解。貪心算法不是對所有的問題都能得到整體最優解。

Kruskal算法:

Kruskal算法采用全局貪心的策略,其步驟如下:

任給一個有n個頂點的連通網絡N={V,E}。

(1)首先構造一個由這n個頂點組成、不含任何邊的圖G={V,NULL},其中每個頂點自成一個連通分量。

(2)其次不斷從E中取出權值最小的一條邊(若有多條任取其一),若該邊的兩個頂點來自不同的連通分量(若相同則不加因為會生成環),則將此邊加入到G中。

(3)如此重復,直到所有頂點在同一個連通分量上為止。

核心:每次迭代時,選出一條具有最小權值,且兩端點不在同一連通分量上的邊,加入生成樹。

?具體過程如下圖所示:按照abc..的循序,箭頭為當前要操作的位置(不一定能添加,黑色為可添加)。

??

代碼實現如下:

先構造關于Edge的小根堆,由于是自定義類,故要自己實現一個比較器Comparator。

1. 定義優先級隊列存儲邊構建小根堆 跟進權重進行比較。

2. 把矩陣當中的邊全部入隊列。

3. 定義并查集判斷將來兩條邊是不是在一個集合(避免構成環)。

4. 由于篇幅有限matrix之類的前文實現過這里不在實現有需要的友友可以前往圖的概念

static class Edge{public int srcIndex;public int destIndex;public int weight;public Edge(int srcIndex,int destIndex,int weight){this.srcIndex = srcIndex;this.destIndex = destIndex;this.weight = weight;}}public int kruskal(GraphByMatrix minTree){//1. 定義優先級隊列 存儲邊 構建小根堆 跟進權重進行比較PriorityQueue<Edge> minHeap = new PriorityQueue<>(new Comparator<Edge>(){@Overridepublic int compare(Edge o1,Edge o2){return o1.weight - o2.weight;}});int n = matrix.length;//2. 把矩陣當中的邊全部入隊列for(int i = 0;i < n;i++){for(int j = 0;j < n;j++){//因為是無向圖,所以只入一半就可以 i < j 即可if(i < j && matrix[i][j] != Integer.MAX_VALUE){Edge edge = new Edge(i,j,matrix[i][j]);minHeap.offer(edge);}}}//3、最后整個的權重int totalWeight = 0;int size= 0;//4.定義并查集 判斷將來兩條邊 是不是在一個集合UnionFindSet ufs = new UnionFindSet(n);//5. 出優先級隊列的n-1條邊while(size < n-1 &&!minHeap.isEmpty()){Edge min  = minHeap.poll();int srcIndex = min.srcIndex;int destIndex = min.destIndex;//判斷是不在在同一個集合當中,在一個集合 就不能添加if(!ufs.isSameSet(srcIndex,destIndex)){//打印選出的邊System.out.println("選擇的邊: "+ arrayV[srcIndex] + "-> "+ arrayV[destIndex] + ":"+matrix[srcIndex][destIndex]);?minTree.addEdgeUseIndex(srcIndex,destIndex,min.weight);totalWeight += min.weight;//添加完成之后,說明 可以 合并到同一個集合ufs.union(srcIndex,destIndex);size++;}}//如果是 選出n-1條邊,否則就說明不是連通圖if(size == n-1){return totalWeight;}//不是連通圖, 可能選不出n-1條邊  假設一個圖中,有其他的頂點獨立著return -1;}private void addEdgeUseIndex(int srcIndex,int destIndex,int weight) {matrix[srcIndex][destIndex] = weight;//如果是無向圖 那么相反的位置 也同樣需要置為空if(!isDirect) {matrix[destIndex][srcIndex] = weight;}}

測試:

測試代碼對應的圖:

測試代碼 :

public static void main(String[] args) {testGraphMinTreeKruskal();}public static void testGraphMinTreeKruskal() {String str = "abcdefghi";char[] array =str.toCharArray();GraphByMatrix g = new GraphByMatrix(str.length(),false);g.initArrayV(array);g.addEdge('a', 'b', 4);g.addEdge('a', 'h', 8);//g.addEdge('a', 'h', 9);g.addEdge('b', 'c', 8);g.addEdge('b', 'h', 11);g.addEdge('c', 'i', 2);g.addEdge('c', 'f', 4);g.addEdge('c', 'd', 7);g.addEdge('d', 'f', 14);g.addEdge('d', 'e', 9);g.addEdge('e', 'f', 10);g.addEdge('f', 'g', 2);g.addEdge('g', 'h', 1);g.addEdge('g', 'i', 6);g.addEdge('h', 'i', 7);GraphByMatrix  kminTree = new GraphByMatrix(str.length(),false);System.out.println(g.kruskal(kminTree));kminTree.printGraph();}

效果:

顯然正確💯

Prime算法 :

Primel算法采用局部貪心的策略,其步驟如下:

按照字母順序abc....看。

代碼實現如下:

由于是局部貪心用兩個Set,那么天然就不會有環,故prime可以不用并查集。

1. 先獲取當前頂點的下標。

2. 定義一個X集合,把當前的起點下標存進去。

3. 定義一個Y集合,存儲目標頂點的元素。

4. 除了剛剛的起點,其他的頂點需要放到Y。

5. 從X集合中的點到Y集合的點中,連接的邊中找出最小值放到優先級隊列。

6. 把當前頂點連接出去的所有的邊放入隊列。

7.把這次的目標點,添加到X集合,變成了起點記得把之前的目標點,從Y集合刪除掉。

8.遍歷剛剛添加的新起點destIndex,連接出去的所有邊,再次添加到優先級隊列。

public int prim(GraphByMatrix minTree,char chV){//1. 先獲取當前頂點的下標int srcIndex = getIndexOfV(chV);int n = arrayV.length;//2. 定義一個X集合,把當前的起點下標存進去Set<Integer> setX = new HashSet<>();//3. 定義一個Y集合,存儲目標頂點的元素Set<Integer> setY = new HashSet<>();setX.add(srcIndex);//4. 除了剛剛的起點,其他的頂點需要放到Y集合for(int i = 0;i < n;i++){if(i != srcIndex){setY.add(i);}}//5. 從X集合中的點到Y集合的點中,連接的邊中找出最小值放到優先級隊列PriorityQueue<Edge> minHeap = new PriorityQueue<>(new Comparator<Edge>(){@Overridepublic int compare(Edge o1,Edge o2){return o1.weight - o2.weight;}});//6. 把當前頂點連接出去的所有的邊放入隊列for(int i = 0;i < n;i++){if(matrix[srcIndex][i] != Integer.MAX_VALUE){minHeap.offer(new Edge(srcIndex,i,matrix[srcIndex][i]));}}int size = 0;int totalWeight = 0;while(size < n - 1 && !minHeap.isEmpty()){//7. 取出隊列中的第一條邊Edge min = minHeap.poll();int srcI = min.srcIndex;int destI = min.destIndex;//起始點本身就在X集合,所以這里只需要判斷目標點即可if(setX.contains(destI)){//包含}else{//8. 直接將該邊 放入最小生成樹minTree.addEdgeUseIndex(srcI,destI,min.weight);//9. 每選一條邊 就打印一條語句System.out.println("選擇的邊: "+ arrayV[srcI] + "-> "+ arrayV[destI] + ":"+matrix[srcI][destI]);size++;totalWeight += min.weight;//10.把這次的目標點,添加到X集合,變成了起點setX.add(destI);//11.記得把之前的目標點,從Y集合刪除掉setY.remove(destI);//12. 遍歷剛剛添加的新起點destIndex,連接出去的所有邊,再次添加到優先級隊列for(int i = 0;i < n;i++){// 13. !setX.contains(i) 判斷目標點不能再X這個集合 例如: a->b 就包含了b->aif(matrix[destI][i] != Integer.MAX_VALUE && !setX.contains(i)){minHeap.offer(new Edge(destI,i,matrix[destI][i]));}}}}if(size == n-1){return totalWeight;}else{return -1;}}

測試:

測試對應的圖:

測試代碼 :

public static void main(String[] args) {testGraphMinTreePrime();}public static void testGraphMinTreePrime() {String str = "abcdefghi";char[] array = str.toCharArray();GraphByMatrix g = new GraphByMatrix(str.length(), false);g.initArrayV(array);g.addEdge('a', 'b', 4);g.addEdge('a', 'h', 8);//g.addEdge('a', 'h', 9);g.addEdge('b', 'c', 8);g.addEdge('b', 'h', 11);g.addEdge('c', 'i', 2);g.addEdge('c', 'f', 4);g.addEdge('c', 'd', 7);g.addEdge('d', 'f', 14);g.addEdge('d', 'e', 9);g.addEdge('e', 'f', 10);g.addEdge('f', 'g', 2);g.addEdge('g', 'h', 1);g.addEdge('g', 'i', 6);g.addEdge('h', 'i', 7);GraphByMatrix primTree = new GraphByMatrix(str.length(), false);System.out.println(g.prim(primTree, 'a'));primTree.printGraph();}

效果:

結語:

其實寫博客不僅僅是為了教大家,同時這也有利于我鞏固自己的知識點,和一個學習的總結,由于作者水平有限,對文章有任何問題的還請指出,接受大家的批評,讓我改進,如果大家有所收獲的話還請不要吝嗇你們的點贊收藏和關注,這可以激勵我寫出更加優秀的文章。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696257.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696257.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696257.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Sora的第一波受害者出現了。

不知道大家最近除了被Sora刷屏之外&#xff0c;有沒有被這張圖刷屏 我只能說網友太強大了 說實話&#xff0c;我進入舟老師的直播間&#xff0c;每次都是還有3分鐘下播&#xff0c;還有6單就拍完 但是10分鐘后還在激情逼單&#xff0c;6單之后還有6單 也許在營銷學上&#x…

深入理解nginx的動態變量機制【上】

目錄 1. 概述2. 動態變量的分類2.1 按照變量名的確定性來分類2.2 按照變量聲明的來源分類2.3 按照是否可以變更分類2.4 按照是否可以緩存分類2.5 按照變量的索引方式分類 3. 變量的使用3.1 聲明一個變量3.1.1 支撐變量聲明的nginx關鍵結構體3.1.2 在配置文件中聲明3.1.3 在http…

C++筆記:OOP三大特性之多態

前言 本博客中的代碼和解釋都是在VS2019下的x86程序中進行的&#xff0c;涉及的指針都是 4 字節&#xff0c;如果要其他平臺下測試&#xff0c;部分代碼需要改動。比如&#xff1a;如果是x64程序&#xff0c;則需要考慮指針是8bytes問題等等。 文章目錄 前言一、多態的概念二、…

【C++初階】系統實現日期類

目錄 一.運算符重載實現各個接口 1.小于 (d1)<> 2.等于 (d1d2) 3.小于等于&#xff08;d1<d2&#xff09; 4.大于&#xff08;d1>d2&#xff09; 5.大于等于&#xff08;d1>d2&#xff09; 6.不等于&#xff08;d1!d2&#xff09; 7.日期天數 (1) 算…

mac圖片怎么轉換格式jpg?四種高效方法助你輕松搞定JPG格式

mac圖片怎么轉換格式jpg&#xff1f;在數字時代&#xff0c;圖片格式的轉換成為了我們日常操作中的一項基本技能。特別是在使用Mac操作系統的用戶中&#xff0c;如何將圖片轉換為JPG格式成為了一個熱門話題。本文將為你詳細介紹四種簡單實用的方法&#xff0c;幫助你在Mac上輕松…

測試基礎1:偉大航路喲呼(Linux基礎、mysql基礎)

1 測試流程和方法 軟件測試定義&#xff1a; 從方式上看&#xff1a;包含人工測試、自動化測試 從方法上看&#xff1a;運行程序或系統和測定程序或系統的過程 從目的上看&#xff1a;包括找bug和找bug出現的原因 軟件測試的原則&#xff1a;功能性、可靠性、易用性、效率性…

一、網絡基礎知識

1、IP地址和端口號 1.1、IP地址 定義&#xff1a;用于在網絡中唯一標識設備的地址。格式&#xff1a;通常由四個數字組成&#xff0c;以點分十進制表示&#xff0c;例如&#xff1a;192.168.0.1。(IPv4)作用&#xff1a;允許網絡中的設備相互通信&#xff0c;通過IP地址可以定…

Python 數據可視化之密度散點圖 Density Scatter Plot

&#x1f349; CSDN 葉庭云&#xff1a;https://yetingyun.blog.csdn.net/ 密度散點圖&#xff08;Density Scatter Plot&#xff09;&#xff0c;也稱為密度點圖或核密度估計散點圖&#xff0c;是一種數據可視化技術&#xff0c;主要用于展示大量數據點在二維平面上的分布情況…

Swift基礎知識:24.Swift可選鏈

在 Swift 中&#xff0c;可選鏈&#xff08;Optional Chaining&#xff09;是一種用于調用可選類型屬性、方法或下標的安全方式。可選鏈允許我們在調用鏈中的任何一個屬性、方法或下標返回 nil 時&#xff0c;整個調用鏈仍然可以繼續執行&#xff0c;而不會因為其中的任何一個可…

一樣的代碼不同項目跳轉頁面報404的解決辦法

今天收到實施反饋的一個問題&#xff0c;點項目名稱跳轉項目詳情頁面時&#xff0c;有的頁面跳轉顯示正常&#xff0c;有的頁面跳轉報404錯誤。錯誤如下&#xff1a; 發現報錯的項目都有一個共性就是有特殊字符“[ ]” , 解決的辦法就是把帶有特殊字符的字段 用 encodeURI()…

Java SE 入門到精通—4.抽象類與接口【Java】

抽象類 同接口一樣&#xff0c;用來約束子類&#xff0c;限制子類必須擁有某些方法&#xff0c;比普通類多了個抽象方法&#xff0c;用抽象方法該類必為抽象類 概念 沒有具體的對象&#xff0c;具體的方法的一個類 abstract關鍵字聲明為抽象類/方法 一個類中有抽象方法則該…

統計前端傳過來的Req的非空屬性個數的工具類

背景 日常開發中&#xff0c;我們通常會根據前端傳過來的實體類的屬性個數去做邏輯判斷&#xff0c;下面的是判斷屬性個數的工具類。 工具類 public static Integer nonNullFieldCount(Req req) {if (req null) {return 0;}int nonNullFieldCount 0;Field[] fields req.ge…

【Django】Django自定義后臺表單——對一個關聯外鍵對象同時添加多個內容

以官方文檔為例&#xff1a; 一個投票問題包含多個選項&#xff0c;基本的表單設計只能一個選項一個選項添加&#xff0c;效率較低&#xff0c;如何在表單設計中一次性添加多個關聯選項&#xff1f; 示例代碼&#xff1a; from django.contrib import adminfrom .models impo…

Java中的關鍵字有哪些?它們各自的作用是什么?請詳細說明?Java中的訪問修飾符有哪些?它們的訪問權限是怎樣的?

1、Java中的關鍵字有哪些&#xff1f;它們各自的作用是什么&#xff1f;請詳細說明&#xff1f; Java中的關鍵字是預先定義好的&#xff0c;具有特殊含義的標識符&#xff0c;用于表示數據類型、程序結構或控制流程等。以下是Java中的一些常用關鍵字及其作用&#xff1a; abs…

【軟件架構】02-復雜度來源

1、性能 1&#xff09;單機 受限于主機的CPU、網絡、磁盤讀寫速度等影響 在多線程的互斥性、并發中的同步數據狀態等&#xff1b; 擴展&#xff1a;硬件資源、增大線程池 2&#xff09;集群 微服務化拆分&#xff0c;導致調用鏈過長&#xff0c;網絡傳輸的消耗過多。 集…

嵌入式Qt 計算器核心算法_3

一.后綴表達式實現算數運算思路 二.算法實現 #include "QCalculatorDec.h"QCalculatorDec::QCalculatorDec() {m_exp "";m_result ""; }QCalculatorDec::~QCalculatorDec() {}bool QCalculatorDec::isDigitOrDot(QChar c) {return ((0 < c)…

基于SpringBoot的景區旅游管理系統

項目介紹 本期給大家介紹一個 景區旅游管理 系統.。主要模塊有首頁&#xff0c;旅游路線&#xff0c;旅行攻略&#xff0c;在線預定。管理員可以登錄管理后臺對用戶進行管理&#xff0c;可以添加酒店&#xff0c;景區&#xff0c;攻略&#xff0c;路線等信息。整體完成度比較高…

一文搞懂match、match_phrase與match_phrase_prefix的檢索過程

一、在開始之前&#xff0c;完成數據準備&#xff1a; # 創建映射 PUT /tehero_index {"settings": {"index": {"number_of_shards": 1,"number_of_replicas": 1}},"mappings": {"_doc": {"dynamic": …

探索氣膜球幕影院:未來的電影體驗

氣膜球幕影院作為一種新興的電影放映方式&#xff0c;正逐漸成為人們關注的焦點。它采用了充氣式膜結構&#xff0c;可以為觀眾帶來 360 度全景的觀影體驗&#xff0c;讓人仿佛置身于電影之中。本文將介紹氣膜球幕影院的特點、技術原理以及未來的發展前景。 傳說在古代&#x…

Linux系統運維命令:使用 tail,grep組合命令(包括wc,sort,awk,sed等),可以方便的查閱和操作正在改變的日志文件的具體內容

一、命令介紹 1、tail命令 tail命令是Linux系統中常用的命令之一&#xff0c;用于查看文件的末尾內容。它具有許多有用的選項&#xff0c;可以幫助用戶輕松地查找并顯示文件中的信息。 它默認顯示文件的最后10行&#xff0c;但可以通過各種選項來定制輸出的行數、字節數等。ta…