深度優(yōu)先遍歷&廣度優(yōu)先遍歷

一、定義

深度優(yōu)先遍歷(Depth-First Search,DFS)和廣度優(yōu)先遍歷(Breadth-First Search,BFS)是兩種主要的圖或樹結構的遍歷算法。
DFS優(yōu)先深入地探索一個節(jié)點的子節(jié)點,直到該節(jié)點的所有子節(jié)點都已被探索完,然后再回溯到該節(jié)點的同級節(jié)點進行探索;
BFS則優(yōu)先探索一個節(jié)點的所有同級節(jié)點,再逐級向下探索。
在前端的工作中,如果遇到樹形 DOM 結構、樹型控件、級聯(lián)選擇等等需求,都需要使用到DFS和BFS。

二、算法步驟

2.1 樹

樹是一種分層數(shù)據(jù)的抽象模型,樹可以看做是一種特殊的鏈表,只是鏈表只有一個 next 指向下一個節(jié)點,而樹的每個節(jié)點都有多個 next 指向下一個節(jié)點。

一個樹結構包含一系列存在父子關系的節(jié)點。每個節(jié)點都有一個父節(jié)點(除了頂部的第一個節(jié)點)以及零個或多個子節(jié)點:


image.png

JavaScript 中沒有樹這種數(shù)據(jù)結構,但是可以用 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 深度優(yōu)先遍歷(DFS)

深度優(yōu)先遍歷,盡可能深的搜索樹的分支。


image.png

image.png

序號表示被搜索的順序,它的算法口訣是:
訪問根節(jié)點;
對根節(jié)點的 children 挨個(遞歸)進行深度優(yōu)先遍歷。

# tree 為上述的結構

# 深度優(yōu)先代碼
const dfs = (node)=>{
  console.log(node.value);
  node.children.forEach(dfs);
}

# 調(diào)用
dfs(tree);

打印結果輸出順序: a、b、d、e、c、f、g 。

2.3 廣度優(yōu)先遍歷(BFS)

廣度優(yōu)先遍歷,先訪問離根節(jié)點最近的節(jié)點。


image.png
序號表示被搜索的順序,先把同層的節(jié)點給遍歷完,再去遍歷子節(jié)點。它的算法口訣是:
新建一個隊列,把根節(jié)點入隊;
把對頭出隊并訪問;
把對頭的 children 挨個入隊;
重復(循環(huán))第二、三步,直到隊列為空。
const bfs = (root)=>{
  # 根節(jié)點入隊
  const stack = [root];
  # 只要棧不為空就一直循環(huán)
  while (stack.length > 0){
    # 取出棧首
    const node = stack.shift();
    # 遍歷根節(jié)點,把它的子節(jié)點推入棧尾
    node.children.forEach((item)=> stack.push(item));
    # 打印節(jié)點值
    console.log(node.value);
  }
}

bfs(tree);


打印結果輸出順序: a、b、c、d、e、f、g 。

三、使用場景 & 應用案例

3.1 深度優(yōu)先遍歷

● 在圖或樹的復雜結構中搜索特定節(jié)點。
● 用于解決如迷宮搜索、尋找連通性組件等問題。
● 查找文件路徑
● 二叉樹的遍歷

3.2 廣度優(yōu)先遍歷

● 尋找最短路徑,如無權重圖的最近節(jié)點或社交網(wǎng)絡中的度數(shù)。
● 逐層遍歷,如層級結構的打印。
● 權限系統(tǒng)
● Web 爬蟲(廣度優(yōu)先搜索也被應用在互聯(lián)網(wǎng)搜索引擎的網(wǎng)頁爬蟲技術中,以盡可能廣泛地爬取頁面。)

四、優(yōu)點和缺點

4.1深度優(yōu)先遍歷 (DFS):

優(yōu)點:

  1. 路徑檢測:DFS 非常適合搜索所有可能的路徑,因為它走的“更深”。
  2. 內(nèi)存較少:相比 BFS,DFS 使用的內(nèi)存較少。因為它只需要存儲單條路徑上的節(jié)點。

缺點:

  1. 時間較多:在某些情況下,尤其是在目標節(jié)點離初始節(jié)點較近時,或者在解決最短路徑問題時, DFS 可能需要花費不必要的更多時間。
  2. 無限循環(huán):在非樹形圖結構中,由于 DFS 偏向深入,可能遇到不斷深入但找不到解的歷史循環(huán)問 題。
    深度優(yōu)先遍歷的優(yōu)點在于能快速找到解決方案,且易于實現(xiàn),但有可能陷入死循環(huán)或者掉入一些非最優(yōu)的情況。

4.2廣度優(yōu)先遍歷 (BFS):

優(yōu)點:

  1. 最短路徑:在樹形或圖形結構中,BFS 可以找到從根節(jié)點到目標節(jié)點的最短路徑。
  2. 層級遍歷:由于 BFS 是逐層遍歷,因此很適用于需要按層級遍歷的場合。

缺點:

  1. 內(nèi)存:相比 DFS,BFS 使用的內(nèi)存較多。尤其是在樹的分支很多時,因為它需要存儲整個擴展的節(jié) 點隊列。
  2. 路徑檢測:在需要找到所有可能的路徑時,BFS效率較低。
    廣度優(yōu)先遍歷可以找到最優(yōu)解,特別是在需要找到最短路徑的問題中,但可能需要更多的存儲空間。

五、時間和空間復雜度

5.1 BFS(廣度優(yōu)先搜索)

  1. 時間復雜度:是O(V+E),其中V是圖中頂點(Vertex)的數(shù)量,E是圖中邊(Edge)的數(shù)量。這是因為在算法執(zhí)行過程中,每個頂點和每條邊都會被探查一次。
  2. 空間復雜度:是O(V),其中V是圖中的頂點的數(shù)量。在最壞的情況下,即在隊列中存放了圖的所有頂點,所以空間復雜度與頂點的數(shù)量有關。

5.2 DFS(深度優(yōu)先搜索)

  1. 時間復雜度:是O(V+E),其中V是圖中頂點(Vertex)的數(shù)量,E是圖中邊(Edge)的數(shù)量。與BFS一樣,這是因為在算法執(zhí)行過程中,每個頂點和每條邊都會被訪問一次。
  2. 空間復雜度:是O(V),其中V是圖中的頂點的數(shù)量。在最壞的情況下,即在調(diào)用棧中存放了圖的所有頂點,所以空間復雜度與頂點的數(shù)量有關。
    注意:以上的復雜度分析都是基于鄰接列表的圖數(shù)據(jù)結構進行的。如果使用鄰接矩陣,由于需要遍歷每個點對應的所有邊,因此BFS和DFS的時間復雜度會變?yōu)镺(V^2)。
    所以,當選擇圖的數(shù)據(jù)結構時(例如,鄰接矩陣還是鄰接列表),需要考慮到實際應用的特點,如圖的稀疏或密集程度,以選擇最優(yōu)的數(shù)據(jù)結構,從而提高程序的效率。

六、 總結

深度優(yōu)先遍歷和廣度優(yōu)先遍歷是兩種基本且重要的圖與樹的遍歷算法,選擇使用哪一種遍歷方法取決于問題的具體需求,理解它們的運作原理及適用場景是所有算法學習基礎。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容