Java數(shù)據(jù)結(jié)構(gòu)之圖的兩種搜索算法詳解
前言
在很多情況下,我們需要遍歷圖,得到圖的一些性質(zhì),例如,找出圖中與指定的頂點相連的所有頂點,或者判定某個頂點與指定頂點是否相通,是非常常見的需求。
有關(guān)圖的搜索,最經(jīng)典的算法有深度優(yōu)先搜索和廣度優(yōu)先搜索,接下來我們分別講解這兩種搜索算法。
學(xué)習(xí)本文前請先閱讀這篇文章 【數(shù)據(jù)結(jié)構(gòu)與算法】圖的基礎(chǔ)概念和數(shù)據(jù)模型。
深度優(yōu)先搜索算法
所謂的深度優(yōu)先搜索,指的是在搜索時,如果遇到一個結(jié)點既有子結(jié)點,又有兄弟結(jié)點,那么先找子結(jié)點,然后找兄弟結(jié)點。
如上圖所示:
由于邊是沒有方向的,所以,如果4和5頂點相連,那么4會出現(xiàn)在5的相鄰鏈表中,5也會出現(xiàn)在4的相鄰鏈表中。
為了不對頂點進(jìn)行重復(fù)搜索,應(yīng)該要有相應(yīng)的標(biāo)記來表示當(dāng)前頂點有沒有搜索過,可以使用一個布爾類型的數(shù)組boolean[V] marked
,索引代表頂點,值代表當(dāng)前頂點是否已經(jīng)搜索,如果已經(jīng)搜索,標(biāo)記為true,
如果沒有搜索,標(biāo)記為false;
API設(shè)計
類名 | DepthFirstSearch |
---|---|
成員變量 | 1.private boolean[] marked: 索引代表頂點,值表示當(dāng)前頂點是否已經(jīng)被搜索2.private int count:記錄有多少個頂點與s頂點相通 |
構(gòu)造方法 | DepthFirstSearch(Graph G,int s):構(gòu)造深度優(yōu)先搜索對象,使用深度優(yōu)先搜索找出G圖中s頂點的所有相通頂點 |
成員方法 | 1.private void dfs(Graph G, int v):使用深度優(yōu)先搜索找出G圖中v頂點的所有相通頂點2.public boolean marked(int w):判斷w頂點與s頂點是否相通3.public int count():獲取與頂點s相通的所有頂點的總數(shù) |
代碼實現(xiàn)
/** * 圖的深度優(yōu)先搜索算法 * * @author alvin * @date 2022/10/31 * @since 1.0 **/ public class DepthFirstSearch { //索引代表頂點,值表示當(dāng)前頂點是否已經(jīng)被搜索 private boolean[] marked; //記錄有多少個頂點與s頂點相通 private int count; //構(gòu)造深度優(yōu)先搜索對象,使用深度優(yōu)先搜索找出G圖中s頂點的所有相鄰頂點 public DepthFirstSearch(Graph G, int s) { //創(chuàng)建一個和圖的頂點數(shù)一樣大小的布爾數(shù)組 marked = new boolean[G.V()]; dfs(G, s); } //使用深度優(yōu)先搜索找出G圖中v頂點的所有相鄰頂點 private void dfs(Graph G, int v) { //把當(dāng)前頂點標(biāo)記為已搜索 marked[v] = true; //遍歷v頂點的鄰接表,得到每一個頂點w for (Integer w : G.adj(v)) { //遍歷v頂點的鄰接表,得到每一個頂點w if (!marked[w]) { //如果當(dāng)前頂點w沒有被搜索過,則遞歸搜索與w頂點相通的其他頂點 dfs(G, w); } } //相通的頂點數(shù)量+1 count++; } //判斷w頂點與s頂點是否相通 public boolean marked(int w) { return marked[w]; } //獲取與頂點s相通的所有頂點的總數(shù) public int count() { return count; } }
測試:
public class DepthFirstSearchTest { @Test public void test() { //準(zhǔn)備Graph對象 Graph G = new Graph(13); G.addEdge(0,5); G.addEdge(0,1); G.addEdge(0,2); G.addEdge(0,6); G.addEdge(5,3); G.addEdge(5,4); G.addEdge(3,4); G.addEdge(4,6); G.addEdge(7,8); G.addEdge(9,11); G.addEdge(9,10); G.addEdge(9,12); G.addEdge(11,12); //準(zhǔn)備深度優(yōu)先搜索對象 DepthFirstSearch search = new DepthFirstSearch(G, 0); //測試與某個頂點相通的頂點數(shù)量 int count = search.count(); System.out.println("與起點0相通的頂點的數(shù)量為:"+count); //測試某個頂點與起點是否相同 boolean marked1 = search.marked(5); System.out.println("頂點5和頂點0是否相通:"+marked1); boolean marked2 = search.marked(7); System.out.println("頂點7和頂點0是否相通:"+marked2); } }
廣度優(yōu)先搜素算法
所謂的廣度優(yōu)先搜索,指的是在搜索時,如果遇到一個結(jié)點既有子結(jié)點,又有兄弟結(jié)點,那么先找兄弟結(jié)點,然后找子結(jié)點。
- 可以通過借助一個輔助隊列實現(xiàn),先將1加入到隊列中
- 然后取出1,將1的相鄰頂點加入到隊列中
- 依次遞歸,如下圖所示:
API設(shè)計
類名 | BreadthFirstSearch |
---|---|
成員變量 | 1.private boolean[] marked: 索引代表頂點,值表示當(dāng)前頂點是否已經(jīng)被搜索2.private int count:記錄有多少個頂點與s頂點相通3.private Queue waitSearch: 用來存儲待搜索鄰接表的點 |
構(gòu)造方法 | BreadthFirstSearch(Graph G,int s):構(gòu)造廣度優(yōu)先搜索對象,使用廣度優(yōu)先搜索找出G圖中s頂點的所有相鄰頂點 |
成員方法 | 1.private void bfs(Graph G, int v):使用廣度優(yōu)先搜索找出G圖中v頂點的所有相鄰頂點2.public boolean marked(int w):判斷w頂點與s頂點是否相通3.public int count():獲取與頂點s相通的所有頂點的總數(shù) |
代碼實現(xiàn)
/** * 圖的廣度優(yōu)先搜索算法 * * @author alvin * @date 2022/10/31 * @since 1.0 **/ public class BreadthFirstSearch { //索引代表頂點,值表示當(dāng)前頂點是否已經(jīng)被搜索 private boolean[] marked; //記錄有多少個頂點與s頂點相通 private int count; //用來存儲待搜索鄰接表的點 private Queue<Integer> waitSearch; //構(gòu)造廣度優(yōu)先搜索對象,使用廣度優(yōu)先搜索找出G圖中s頂點的所有相鄰頂點 public BreadthFirstSearch(Graph G, int s) { this.marked = new boolean[G.V()]; this.count = 0; this.waitSearch = new ArrayDeque<>(); bfs(G, s); } //使用廣度優(yōu)先搜索找出G圖中v頂點的所有相鄰頂點 private void bfs(Graph G, int v) { //把當(dāng)前頂點v標(biāo)識為已搜索 marked[v] = true; //讓頂點v進(jìn)入隊列,待搜索 waitSearch.add(v); //通過循環(huán),如果隊列不為空,則從隊列中彈出一個待搜索的頂點進(jìn)行搜索 while (!waitSearch.isEmpty()) { //彈出一個待搜索的頂點 Integer wait = waitSearch.poll(); //遍歷wait頂點的鄰接表 for (Integer w : G.adj(wait)) { if (!marked[w]) { bfs(G, w); } } } //讓相通的頂點+1; count++; } //判斷w頂點與s頂點是否相通 public boolean marked(int w) { return marked[w]; } //獲取與頂點s相通的所有頂點的總數(shù) public int count() { return count; } }
測試代碼:
public class BreadthFirstSearchTest { @Test public void test() { //準(zhǔn)備Graph對象 Graph G = new Graph(13); G.addEdge(0, 5); G.addEdge(0, 1); G.addEdge(0, 2); G.addEdge(0, 6); G.addEdge(5, 3); G.addEdge(5, 4); G.addEdge(3, 4); G.addEdge(4, 6); G.addEdge(7, 8); G.addEdge(9, 11); G.addEdge(9, 10); G.addEdge(9, 12); G.addEdge(11, 12); //準(zhǔn)備廣度優(yōu)先搜索對象 BreadthFirstSearch search = new BreadthFirstSearch(G, 0); //測試與某個頂點相通的頂點數(shù)量 int count = search.count(); System.out.println("與起點0相通的頂點的數(shù)量為:" + count); //測試某個頂點與起點是否相同 boolean marked1 = search.marked(5); System.out.println("頂點5和頂點0是否相通:" + marked1); boolean marked2 = search.marked(7); System.out.println("頂點7和頂點0是否相通:" + marked2); } }
案例應(yīng)用
某省調(diào)查城鎮(zhèn)交通狀況,得到現(xiàn)有城鎮(zhèn)道路統(tǒng)計表,表中列出了每條道路直接連通的城鎮(zhèn)。“暢通工程”的目標(biāo)是使全省任何兩個城鎮(zhèn)間都可以實現(xiàn)交通(但不一定有直接的道路相連,只要互相間接通過道路可達(dá)即可)。目前的道路狀況,9號城市和10號城市是否相通?9號城市和8號城市是否相通?
測試數(shù)據(jù)格式如上圖所示,總共有20個城市,目前已經(jīng)修改好了7條道路,問9號城市和10號城市是否相通?9號城市和8號城市是否相通?
解題思路:
- 創(chuàng)建一個圖Graph對象,表示城市;
- 分別調(diào)用
addEdge(0,1),addEdge(6,9),addEdge(3,8),addEdge(5,11),addEdge(2,12),addEdge(6,10),addEdge(4,8)
,表示已經(jīng)修建好的道路把對應(yīng)的城市連接起來; - 通過Graph對象和頂點9,構(gòu)建
DepthFirstSearch
對象或BreadthFirstSearch
對象; - 調(diào)用搜索對象的
marked(10)
方法和marked(8)
方法,即可得到9和城市與10號城市以及9號城市與8號城市是否相通。
代碼實現(xiàn):
public class TrafficProjectGraph { public static void main(String[] args) throws Exception{ //城市數(shù)量 int totalNumber = 20; Graph G = new Graph(totalNumber); //添加城市的交通路線 G.addEdge(0,1); G.addEdge(6,9); G.addEdge(3,8); G.addEdge(5,11); G.addEdge(2,12); G.addEdge(6,10); G.addEdge(4,8); //構(gòu)建一個深度優(yōu)先搜索對象,起點設(shè)置為頂點9 DepthFirstSearch search = new DepthFirstSearch(G, 9); //調(diào)用marked方法,判斷8頂點和10頂點是否與起點9相通 System.out.println("頂點8和頂點9是否相通:"+search.marked(8)); System.out.println("頂點10和頂點9是否相通:"+search.marked(10)); } }
結(jié)果:
以上就是Java數(shù)據(jù)結(jié)構(gòu)之圖的兩種搜索算法詳解的詳細(xì)內(nèi)容,更多關(guān)于Java圖搜索算法的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java數(shù)據(jù)結(jié)構(gòu)之圖(動力節(jié)點Java學(xué)院整理)
- Java編程實現(xiàn)基于圖的深度優(yōu)先搜索和廣度優(yōu)先搜索完整代碼
- java圖搜索算法之DFS與BFS詳解
- java圖搜索算法之圖的對象化描述示例詳解
- Java數(shù)據(jù)結(jié)構(gòu)之圖的領(lǐng)接矩陣詳解
- Java數(shù)據(jù)結(jié)構(gòu)之圖的原理與實現(xiàn)
- Java數(shù)據(jù)結(jié)構(gòu)中圖的進(jìn)階詳解
- Java數(shù)據(jù)結(jié)構(gòu)之圖的基礎(chǔ)概念和數(shù)據(jù)模型詳解
相關(guān)文章
httpclient staleConnectionCheckEnabled獲取連接流程解析
這篇文章主要為大家介紹了httpclient staleConnectionCheckEnabled獲取連接流程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟
這篇文章主要給大家介紹了關(guān)于mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11解決Maven 項目報錯 java.httpservlet和synchronized使用方法
下面小編就為大家?guī)硪黄鉀QMaven 項目報錯 java.httpservlet和synchronized使用方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07Java中LinkedHashSet的實現(xiàn)原理詳解
這篇文章主要介紹了Java中LinkedHasSet的實現(xiàn)原理詳解,LinkedHashSet?是具有可預(yù)知迭代順序的?Set?接口的哈希表和鏈接列表實現(xiàn),此實現(xiàn)與HashSet?的不同之處在于,后者維護(hù)著一個運行于所有條目的雙重鏈接列表,需要的朋友可以參考下2023-09-09解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題
這篇文章主要介紹了解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Java Stream中自定義Collector實現(xiàn)復(fù)雜數(shù)據(jù)收集的方法
Java Stream API中的Collector接口是一個強(qiáng)大的工具,它允許我們自定義數(shù)據(jù)收集、轉(zhuǎn)換和聚合的過程,,本文介紹了Java Stream中自定義Collector實現(xiàn)復(fù)雜數(shù)據(jù)收集方法,需要的朋友可以參考下2024-08-08Springboot mybais配置多數(shù)據(jù)源過程解析
這篇文章主要介紹了Springboot+mybais配置多數(shù)據(jù)源過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03Java項目Guava包?HashMultimap使用及注意事項
guava基本上可以說是java開發(fā)項目中,大概率會引入的包,今天介紹的主角是一個特殊的容器HashMultmap,可以簡單的將它的數(shù)據(jù)結(jié)構(gòu)理解為Map<K,?Set<V>>,今天主要介紹下基礎(chǔ)的知識點?HashMultmap級使用,感興趣的朋友一起看看吧2022-05-05idea安裝jerbel及文件上傳下載的實現(xiàn)示例
JRebel是一個Java開發(fā)工具,它是一款用于實時代碼重載的插件,本文主要介紹了idea安裝jerbel及文件上傳下載的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解下2023-09-09剖析Java中HashMap數(shù)據(jù)結(jié)構(gòu)的源碼及其性能優(yōu)化
這篇文章主要介紹了Java中HashMap數(shù)據(jù)結(jié)構(gòu)的源碼及其性能優(yōu)化,文中以Java 8后HashMap的性能提升來討論了HashMap的一些優(yōu)化點,需要的朋友可以參考下2016-05-05