一、定義
深度優先遍歷(Depth-First Search,DFS)和廣度優先遍歷(Breadth-First Search,BFS)是兩種主要的圖或樹結構的遍歷算法。
DFS優先深入地探索一個節點的子節點,直到該節點的所有子節點都已被探索完,然后再回溯到該節點的同級節點進行探索;
BFS則優先探索一個節點的所有同級節點,再逐級向下探索。
在前端的工作中,如果遇到樹形 DOM 結構、樹型控件、級聯選擇等等需求,都需要使用到DFS和BFS。
二、算法步驟
2.1 樹
樹是一種分層數據的抽象模型,樹可以看做是一種特殊的鏈表,只是鏈表只有一個 next 指向下一個節點,而樹的每個節點都有多個 next 指向下一個節點。
一個樹結構包含一系列存在父子關系的節點。每個節點都有一個父節點(除了頂部的第一個節點)以及零個或多個子節點:
image.png
JavaScript 中沒有樹這種數據結構,但是可以用 Object 和 Array 來模擬一顆樹。
const tree = {
value:"a",
children:[
{
value:"b",
children:[
{
value:"d",
children:[
{
value:"e",
children:[]
}
]
}
]
},
{
value:"c",
children:[
{
value:"f",
children:[]
},
{
value:"g",
children:[]
}
]
}
]
}
var data = [
{
name: "a",
children: [
{
name: "b",
children: [
{
name: "e",
},
],
},
{
name: "c",
children: [
{
name: "f",
},
],
},
{
name: "d",
children: [
{
name: "g",
},
],
},
],
},
{
name: "a2",
children: [
{
name: "b2",
children: [
{
name: "e2",
},
],
},
{
name: "c2",
children: [
{
name: "f2",
},
],
},
{
name: "d2",
children: [
{
name: "g2",
},
],
},
],
},
];
2.2 深度優先遍歷(DFS)
深度優先遍歷,盡可能深的搜索樹的分支。
image.png
image.png
序號表示被搜索的順序,它的算法口訣是:
訪問根節點;
對根節點的 children 挨個(遞歸)進行深度優先遍歷。
# tree 為上述的結構
# 深度優先代碼
const dfs = (node)=>{
console.log(node.value);
node.children.forEach(dfs);
}
# 調用
dfs(tree);
打印結果輸出順序: a、b、d、e、c、f、g 。
2.3 廣度優先遍歷(BFS)
廣度優先遍歷,先訪問離根節點最近的節點。
image.png
序號表示被搜索的順序,先把同層的節點給遍歷完,再去遍歷子節點。它的算法口訣是:
新建一個隊列,把根節點入隊;
把對頭出隊并訪問;
把對頭的 children 挨個入隊;
重復(循環)第二、三步,直到隊列為空。
const bfs = (root)=>{
# 根節點入隊
const stack = [root];
# 只要棧不為空就一直循環
while (stack.length > 0){
# 取出棧首
const node = stack.shift();
# 遍歷根節點,把它的子節點推入棧尾
node.children.forEach((item)=> stack.push(item));
# 打印節點值
console.log(node.value);
}
}
bfs(tree);
打印結果輸出順序: a、b、c、d、e、f、g 。
三、使用場景 & 應用案例
3.1 深度優先遍歷
● 在圖或樹的復雜結構中搜索特定節點。
● 用于解決如迷宮搜索、尋找連通性組件等問題。
● 查找文件路徑
● 二叉樹的遍歷
3.2 廣度優先遍歷
● 尋找最短路徑,如無權重圖的最近節點或社交網絡中的度數。
● 逐層遍歷,如層級結構的打印。
● 權限系統
● Web 爬蟲(廣度優先搜索也被應用在互聯網搜索引擎的網頁爬蟲技術中,以盡可能廣泛地爬取頁面。)
四、優點和缺點
4.1深度優先遍歷 (DFS):
優點:
- 路徑檢測:DFS 非常適合搜索所有可能的路徑,因為它走的“更深”。
- 內存較少:相比 BFS,DFS 使用的內存較少。因為它只需要存儲單條路徑上的節點。
缺點:
- 時間較多:在某些情況下,尤其是在目標節點離初始節點較近時,或者在解決最短路徑問題時, DFS 可能需要花費不必要的更多時間。
- 無限循環:在非樹形圖結構中,由于 DFS 偏向深入,可能遇到不斷深入但找不到解的歷史循環問 題。
深度優先遍歷的優點在于能快速找到解決方案,且易于實現,但有可能陷入死循環或者掉入一些非最優的情況。
4.2廣度優先遍歷 (BFS):
優點:
- 最短路徑:在樹形或圖形結構中,BFS 可以找到從根節點到目標節點的最短路徑。
- 層級遍歷:由于 BFS 是逐層遍歷,因此很適用于需要按層級遍歷的場合。
缺點:
- 內存:相比 DFS,BFS 使用的內存較多。尤其是在樹的分支很多時,因為它需要存儲整個擴展的節 點隊列。
- 路徑檢測:在需要找到所有可能的路徑時,BFS效率較低。
廣度優先遍歷可以找到最優解,特別是在需要找到最短路徑的問題中,但可能需要更多的存儲空間。
五、時間和空間復雜度
5.1 BFS(廣度優先搜索)
- 時間復雜度:是O(V+E),其中V是圖中頂點(Vertex)的數量,E是圖中邊(Edge)的數量。這是因為在算法執行過程中,每個頂點和每條邊都會被探查一次。
- 空間復雜度:是O(V),其中V是圖中的頂點的數量。在最壞的情況下,即在隊列中存放了圖的所有頂點,所以空間復雜度與頂點的數量有關。
5.2 DFS(深度優先搜索)
- 時間復雜度:是O(V+E),其中V是圖中頂點(Vertex)的數量,E是圖中邊(Edge)的數量。與BFS一樣,這是因為在算法執行過程中,每個頂點和每條邊都會被訪問一次。
- 空間復雜度:是O(V),其中V是圖中的頂點的數量。在最壞的情況下,即在調用棧中存放了圖的所有頂點,所以空間復雜度與頂點的數量有關。
注意:以上的復雜度分析都是基于鄰接列表的圖數據結構進行的。如果使用鄰接矩陣,由于需要遍歷每個點對應的所有邊,因此BFS和DFS的時間復雜度會變為O(V^2)。
所以,當選擇圖的數據結構時(例如,鄰接矩陣還是鄰接列表),需要考慮到實際應用的特點,如圖的稀疏或密集程度,以選擇最優的數據結構,從而提高程序的效率。
六、 總結
深度優先遍歷和廣度優先遍歷是兩種基本且重要的圖與樹的遍歷算法,選擇使用哪一種遍歷方法取決于問題的具體需求,理解它們的運作原理及適用場景是所有算法學習基礎。