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