Java數(shù)據(jù)結(jié)構(gòu)之圖的兩種搜索算法詳解
前言
在很多情況下,我們需要遍歷圖,得到圖的一些性質(zhì),例如,找出圖中與指定的頂點(diǎn)相連的所有頂點(diǎn),或者判定某個頂點(diǎn)與指定頂點(diǎn)是否相通,是非常常見的需求。
有關(guān)圖的搜索,最經(jīng)典的算法有深度優(yōu)先搜索和廣度優(yōu)先搜索,接下來我們分別講解這兩種搜索算法。
學(xué)習(xí)本文前請先閱讀這篇文章 【數(shù)據(jù)結(jié)構(gòu)與算法】圖的基礎(chǔ)概念和數(shù)據(jù)模型。
深度優(yōu)先搜索算法
所謂的深度優(yōu)先搜索,指的是在搜索時,如果遇到一個結(jié)點(diǎn)既有子結(jié)點(diǎn),又有兄弟結(jié)點(diǎn),那么先找子結(jié)點(diǎn),然后找兄弟結(jié)點(diǎn)。

如上圖所示:
由于邊是沒有方向的,所以,如果4和5頂點(diǎn)相連,那么4會出現(xiàn)在5的相鄰鏈表中,5也會出現(xiàn)在4的相鄰鏈表中。
為了不對頂點(diǎn)進(jìn)行重復(fù)搜索,應(yīng)該要有相應(yīng)的標(biāo)記來表示當(dāng)前頂點(diǎn)有沒有搜索過,可以使用一個布爾類型的數(shù)組boolean[V] marked,索引代表頂點(diǎn),值代表當(dāng)前頂點(diǎn)是否已經(jīng)搜索,如果已經(jīng)搜索,標(biāo)記為true,
如果沒有搜索,標(biāo)記為false;
API設(shè)計
| 類名 | DepthFirstSearch |
|---|---|
| 成員變量 | 1.private boolean[] marked: 索引代表頂點(diǎn),值表示當(dāng)前頂點(diǎn)是否已經(jīng)被搜索2.private int count:記錄有多少個頂點(diǎn)與s頂點(diǎn)相通 |
| 構(gòu)造方法 | DepthFirstSearch(Graph G,int s):構(gòu)造深度優(yōu)先搜索對象,使用深度優(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;
//記錄有多少個頂點(diǎn)與s頂點(diǎn)相通
private int count;
//構(gòu)造深度優(yōu)先搜索對象,使用深度優(yōu)先搜索找出G圖中s頂點(diǎn)的所有相鄰頂點(diǎn)
public DepthFirstSearch(Graph G, int s) {
//創(chuàng)建一個和圖的頂點(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)的鄰接表,得到每一個頂點(diǎn)w
for (Integer w : G.adj(v)) {
//遍歷v頂點(diǎn)的鄰接表,得到每一個頂點(diǎn)w
if (!marked[w]) {
//如果當(dāng)前頂點(diǎn)w沒有被搜索過,則遞歸搜索與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;
}
}測試:
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);
//測試與某個頂點(diǎn)相通的頂點(diǎn)數(shù)量
int count = search.count();
System.out.println("與起點(diǎn)0相通的頂點(diǎn)的數(shù)量為:"+count);
//測試某個頂點(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)先搜索,指的是在搜索時,如果遇到一個結(jié)點(diǎn)既有子結(jié)點(diǎn),又有兄弟結(jié)點(diǎn),那么先找兄弟結(jié)點(diǎn),然后找子結(jié)點(diǎn)。

- 可以通過借助一個輔助隊(duì)列實(shí)現(xiàn),先將1加入到隊(duì)列中
- 然后取出1,將1的相鄰頂點(diǎn)加入到隊(duì)列中
- 依次遞歸,如下圖所示:

API設(shè)計
| 類名 | BreadthFirstSearch |
|---|---|
| 成員變量 | 1.private boolean[] marked: 索引代表頂點(diǎn),值表示當(dāng)前頂點(diǎn)是否已經(jīng)被搜索2.private int count:記錄有多少個頂點(diǎn)與s頂點(diǎn)相通3.private Queue waitSearch: 用來存儲待搜索鄰接表的點(diǎn) |
| 構(gòu)造方法 | BreadthFirstSearch(Graph G,int s):構(gòu)造廣度優(yōu)先搜索對象,使用廣度優(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;
//記錄有多少個頂點(diǎn)與s頂點(diǎn)相通
private int count;
//用來存儲待搜索鄰接表的點(diǎn)
private Queue<Integer> waitSearch;
//構(gòu)造廣度優(yōu)先搜索對象,使用廣度優(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)識為已搜索
marked[v] = true;
//讓頂點(diǎn)v進(jìn)入隊(duì)列,待搜索
waitSearch.add(v);
//通過循環(huán),如果隊(duì)列不為空,則從隊(duì)列中彈出一個待搜索的頂點(diǎn)進(jìn)行搜索
while (!waitSearch.isEmpty()) {
//彈出一個待搜索的頂點(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;
}
}測試代碼:
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);
//測試與某個頂點(diǎn)相通的頂點(diǎn)數(shù)量
int count = search.count();
System.out.println("與起點(diǎn)0相通的頂點(diǎn)的數(shù)量為:" + count);
//測試某個頂點(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)計表,表中列出了每條道路直接連通的城鎮(zhèn)。“暢通工程”的目標(biāo)是使全省任何兩個城鎮(zhèn)間都可以實(shí)現(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對象和頂點(diǎn)9,構(gòu)建
DepthFirstSearch對象或BreadthFirstSearch對象; - 調(diào)用搜索對象的
marked(10)方法和marked(8)方法,即可得到9和城市與10號城市以及9號城市與8號城市是否相通。
代碼實(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)建一個深度優(yōu)先搜索對象,起點(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圖搜索算法的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java數(shù)據(jù)結(jié)構(gòu)之圖(動力節(jié)點(diǎn)Java學(xué)院整理)
- Java編程實(shí)現(xiàn)基于圖的深度優(yōu)先搜索和廣度優(yōu)先搜索完整代碼
- java圖搜索算法之DFS與BFS詳解
- java圖搜索算法之圖的對象化描述示例詳解
- 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-11
mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟
這篇文章主要給大家介紹了關(guān)于mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
解決Maven 項(xiàng)目報錯 java.httpservlet和synchronized使用方法
下面小編就為大家?guī)硪黄鉀QMaven 項(xiàng)目報錯 java.httpservlet和synchronized使用方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
Java中LinkedHashSet的實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java中LinkedHasSet的實(shí)現(xiàn)原理詳解,LinkedHashSet?是具有可預(yù)知迭代順序的?Set?接口的哈希表和鏈接列表實(shí)現(xiàn),此實(shí)現(xiàn)與HashSet?的不同之處在于,后者維護(hù)著一個運(yùn)行于所有條目的雙重鏈接列表,需要的朋友可以參考下2023-09-09
解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題
這篇文章主要介紹了解決SpringBoot返回結(jié)果如果為null或空值不顯示處理問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java Stream中自定義Collector實(shí)現(xiàn)復(fù)雜數(shù)據(jù)收集的方法
Java Stream API中的Collector接口是一個強(qiáng)大的工具,它允許我們自定義數(shù)據(jù)收集、轉(zhuǎn)換和聚合的過程,,本文介紹了Java Stream中自定義Collector實(shí)現(xiàn)復(fù)雜數(shù)據(jù)收集方法,需要的朋友可以參考下2024-08-08
Springboot mybais配置多數(shù)據(jù)源過程解析
這篇文章主要介紹了Springboot+mybais配置多數(shù)據(jù)源過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03
Java項(xiàng)目Guava包?HashMultimap使用及注意事項(xiàng)
guava基本上可以說是java開發(fā)項(xiàng)目中,大概率會引入的包,今天介紹的主角是一個特殊的容器HashMultmap,可以簡單的將它的數(shù)據(jù)結(jié)構(gòu)理解為Map<K,?Set<V>>,今天主要介紹下基礎(chǔ)的知識點(diǎn)?HashMultmap級使用,感興趣的朋友一起看看吧2022-05-05
idea安裝jerbel及文件上傳下載的實(shí)現(xiàn)示例
JRebel是一個Java開發(fā)工具,它是一款用于實(shí)時代碼重載的插件,本文主要介紹了idea安裝jerbel及文件上傳下載的實(shí)現(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)化點(diǎn),需要的朋友可以參考下2016-05-05

