」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 深度優先搜尋 (DFS)

深度優先搜尋 (DFS)

發佈於2024-08-19
瀏覽:225

图的深度优先搜索从图中的一个顶点开始,在回溯之前尽可能访问图中的所有顶点。
图的深度优先搜索类似于树遍历,树遍历中讨论的树的深度优先搜索。对于树,搜索从根开始。在图中,搜索可以从任何顶点开始。

树的深度优先搜索首先访问根,然后递归访问根的子树。类似地,图的深度优先搜索首先访问一个顶点,然后递归地访问与该顶点相邻的所有顶点。不同之处在于该图可能包含循环,这可能导致无限递归。为了避免这个问题,你需要跟踪已经访问过的顶点。

该搜索被称为深度优先,因为它尽可能在图中搜索“更深”。搜索从某个顶点 v 开始。访问完 v 后,它会访问 v 的未访问的邻居。如果 v 没有未访问的邻居,则搜索回溯到到达 v 的顶点。我们假设图是连通的,并且搜索开始从任意顶点出发都可以到达所有顶点。

深度优先搜索算法

深度优先搜索的算法在下面的代码中描述。

输入:G = (V, E) 和起始顶点 v
输出:以 v
为根的 DFS 树 1 树 dfs(顶点 v) {
2 访问 v;
3 对于 v
的每个邻居 w 4 if (w尚未被访问过) {
5 将 v 设置为树中 w 的父级;
6 dfs(w);
7 }
8 }

可以使用名为isVisited的数组来表示某个顶点是否已被访问。最初,对于每个顶点 i,isVisited[i]false。一旦访问了某个顶点(例如 v),isVisited[v] 就会设置为 true

考虑下图 (a) 中的图表。假设我们从顶点 0 开始深度优先搜索。首先访问 0,然后访问它的任何邻居,比如 1。现在访问 1,如下图 (b) 所示。顶点 1 有 3 个邻居:0、2 和 4。由于 0 已经被访问过,因此您将访问 2 或 4。让我们选择 2。现在 2 被访问,如下图 (c) 所示。顶点 2 有 3 个邻居:0、1 和 3。由于 0 和 1 已经被访问过,所以选择 3。现在访问 3,如下图 (d) 所示。至此,顶点已按以下顺序被访问过:

0123

由于3的所有邻居都被访问过,所以回溯到2。由于2的所有顶点都被访问过,所以回溯到1。4与1相邻,但4还没有被访问过。因此,访问4,如下图(e)所示。由于 4 的所有邻居都已访问过,因此回溯到 1。
由于1的所有邻居都被访问过,所以回溯到0。因为0的所有邻居都被访问过,所以搜索结束。

Depth-First Search (DFS)

由于每条边和每个顶点仅被访问一次,因此dfs方法的时间复杂度为O(|E| |V|),其中|E | 表示边数,|V| 表示顶点数。

深度优先搜索的实现

上面代码中的DFS算法使用了递归。很自然地使用递归来实现它。或者,您可以使用堆栈。

dfs(int v)方法在AbstractGraph.java的第164-193行实现。它返回 Tree 类的实例,以顶点 v 作为根。该方法将搜索到的顶点存储在列表 searchOrder(第 165 行)中,每个顶点的父级存储在数组 parent(第 166 行)中,并使用 isVisited 数组来指示是否已访问顶点(第 171 行)。它调用辅助方法 dfs(v, Parent, searchOrder, isVisited) 执行深度优先搜索(第 174 行)。

在递归辅助方法中,搜索从顶点u开始。 u 被添加到第 184 行的 searchOrder 中,并被标记为已访问(第 185 行)。对于 u 的每个未访问的邻居,递归调用该方法来执行深度优先搜索。当访问顶点 e.v 时,e.v 的父顶点存储在 parent[e.v] 中(第 189 行)。当访问连通图或连通组件中的所有顶点时,该方法返回。

Depth-First Search (DFS)

下面的代码给出了一个测试程序,该程序显示上图中从芝加哥开始的图形的 DFS。从芝加哥出发的DFS示意图如下图所示。

public class TestDFS {

    public static void main(String[] args) {
        String[] vertices = {"Seattle", "San Francisco", "Los Angeles", "Denver", "Kansas City", "Chicago", "Boston", "New York", "Atlanta", "Miami", "Dallas", "Houston"};

        int[][] edges = {
                {0, 1}, {0, 3}, {0, 5},
                {1, 0}, {1, 2}, {1, 3},
                {2, 1}, {2, 3}, {2, 4}, {2, 10},
                {3, 0}, {3, 1}, {3, 2}, {3, 4}, {3, 5},
                {4, 2}, {4, 3}, {4, 5}, {4, 7}, {4, 8}, {4, 10},
                {5, 0}, {5, 3}, {5, 4}, {5, 6}, {5, 7},
                {6, 5}, {6, 7},
                {7, 4}, {7, 5}, {7, 6}, {7, 8},
                {8, 4}, {8, 7}, {8, 9}, {8, 10}, {8, 11},
                {9, 8}, {9, 11},
                {10, 2}, {10, 4}, {10, 8}, {10, 11},
                {11, 8}, {11, 9}, {11, 10}
        };

        Graph graph = new UnweightedGraph(vertices, edges);
        AbstractGraph.Tree dfs = graph.dfs(graph.getIndex("Chicago"));

        java.util.List searchOrders = dfs.getSearchOrder();
        System.out.println(dfs.getNumberOfVerticesFound()   " vertices are searched in this DFS order:");
        for(int i = 0; i 



按此 DFS 顺序搜索 12 个顶点:
芝加哥 西雅图 旧金山 洛杉矶 丹佛
堪萨斯城 纽约 波士顿 亚特兰大 迈阿密 休斯顿 达拉斯
西雅图的父级是芝加哥
旧金山的父级是西雅图
洛杉矶的父级是旧金山
丹佛的父级是洛杉矶
堪萨斯城的母公司是丹佛
波士顿的父级是纽约
纽约的父级是堪萨斯城
亚特兰大的父级是纽约
迈阿密的父级是亚特兰大
达拉斯的父母是休斯顿
休斯顿的父母是迈阿密

Depth-First Search (DFS)

DFS的应用

深度优先搜索可以用来解决很多问题,例如:

  • 检测图是否连通。从任意顶点开始搜索图。如果搜索到的顶点数与图中的顶点数相同,则该图是连通的。否则,图形不连通。
  • 检测两个顶点之间是否存在路径。
  • 寻找两个顶点之间的路径。
  • 查找所有连接的组件。连通分量是最大连通子图,其中每对顶点都通过路径连接。
  • 检测图中是否存在环路。
  • 在图中找到一个循环。
  • 寻找哈密顿路径/循环。图中的哈密顿路径是只访问图中每个顶点一次的路径。 哈密尔顿循环访问图中的每个顶点一次并返回到起始顶点。

前六个问题可以使用AbstractGraph.java中的dfs方法轻松解决。要找到哈密顿路径/循环,您必须探索所有可能的 DFS,以找到通向最长路径的路径。哈密​​顿路径/循环有很多应用,包括解决著名的骑士之旅问题。

版本聲明 本文轉載於:https://dev.to/paulike/depth-first-search-dfs-4gid?1如有侵犯,請洽[email protected]刪除
最新教學 更多>
  • Python 變得強大:輕鬆程式設計的初學者指南
    Python 變得強大:輕鬆程式設計的初學者指南
    Python 是一門強大的程式語言,文法簡單,應用廣泛。安裝 Python 後,可以學習其基本語法,包括變數賦值、資料類型和流程控制。實戰案例中,我們透過蒙特卡羅模擬計算圓周率,展示了 Python 在數值計算中的能力。此外,Python 擁有豐富的函式庫,涵蓋機器學習、資料分析和網路開發等領域,體...
    程式設計 發佈於2024-11-06
  • 如何在沒有 jQuery 的情況下監聽動態建立的元素的事件?
    如何在沒有 jQuery 的情況下監聽動態建立的元素的事件?
    在沒有 jQuery 的情況下監聽動態創建的元素的事件使用外部頁面時,向動態生成的元素添加事件監聽器可能具有挑戰性。在這種情況下,委派事件處理至關重要。 一種方法是使用 event.target 屬性來檢查單擊或觸發的元素是否屬於所需類型。這是一個例子:document.querySelector(...
    程式設計 發佈於2024-11-06
  • 利用 Snipbyte 的高階考勤管理系統優化勞動力效率
    利用 Snipbyte 的高階考勤管理系統優化勞動力效率
    在當今的商業環境中,有效管理員工出勤、輪班和工資單可以決定組織的成功或失敗。準確的考勤追蹤不僅可以確保營運順利進行,還有助於提高生產力。在 Snipbyte,我們專注於提供一流的基於網路的解決方案來增強業務流程,包括我們的高級考勤管理系統。 為什麼選擇Snipbyte的考勤管理系統? 我們的考勤...
    程式設計 發佈於2024-11-06
  • Laravel Auth 路由教程
    Laravel Auth 路由教程
    Laravel auth routes is one of the essential features of the Laravel framework. Using middlewares you can implement different authentication strategies...
    程式設計 發佈於2024-11-06
  • 如何有效地跳到大型文字檔案中的特定行?
    如何有效地跳到大型文字檔案中的特定行?
    優化大型文字檔案中的跳行:另一種方法處理大量不同長度行的文字檔案時,通常效率很低順序讀取每一行以達到特定的行號。問題中提供的程式碼範例說明了這種方法,需要對整個文件進行可能緩慢的迭代。然而,還有一種替代方法可以透過利用計算出的偏移列表來優化跳線。 基於偏移的跳線要克服這項挑戰,需要一種更有效的方法涉...
    程式設計 發佈於2024-11-06
  • 如何在 JavaScript 中檢索 HTML 元素的 CSS 屬性值?
    如何在 JavaScript 中檢索 HTML 元素的 CSS 屬性值?
    在 JavaScript 中取得 HTML 元素的 CSS 屬性值在 Web 開發中,動態操作 CSS 屬性可以增強使用者體驗和介面。使用 JavaScript,存取這些屬性非常簡單。 在您的場景中,CSS 檔案連結到 HTML 頁面,您需要檢索類別名稱為「的 div 的特定屬性(例如顏色)」佈局。...
    程式設計 發佈於2024-11-06
  • PLSQL 中的 DBMS_OUTPUT.PUT_LINE
    PLSQL 中的 DBMS_OUTPUT.PUT_LINE
    在 Oracle PL/SQL 中,列印輸出的方法是使用 DBMS_OUTPUT.PUT_LINE 程序。此程序將文字寫入控制台或輸出緩衝區,如果啟用了 DBMS_OUTPUT,則可以在執行後查看文字。使用方法如下: 首先,在 SQL 環境(如 SQL*Plus 或 Oracle SQL Devel...
    程式設計 發佈於2024-11-06
  • 利用 Python 實現自動化:用程式碼簡化日常任務
    利用 Python 實現自動化:用程式碼簡化日常任務
    介紹 Python 已成為從 Web 開發到資料科學等各種應用程式的首選語言。 Python 真正大放異彩的領域之一是自動化。無論您是希望自動化平凡的任務、簡化工作流程,還是創建強大的腳本來節省時間和精力,Python 的簡單性和多功能性都使其成為完成這項工作的完美工具。 ...
    程式設計 發佈於2024-11-06
  • 如何在 Python 中傳遞參數來應用 Pandas 系列的函數?
    如何在 Python 中傳遞參數來應用 Pandas 系列的函數?
    Python Pandas 中向Series Apply 函數傳遞參數pandas 庫提供了'apply()' 方法將函數應用於Series 的每個元素。然而,舊版的 pandas 不允許向函數傳遞額外的參數。 舊版Pandas 的解決方案:在舊版中處理此限制pandas 中,您可以...
    程式設計 發佈於2024-11-06
  • 如何使用 Java 8 Lambda 以多個欄位有效地對集合進行排序?
    如何使用 Java 8 Lambda 以多個欄位有效地對集合進行排序?
    使用 Java 8 Lambda 對具有多個欄位的集合進行排序提供的排序程式碼似乎不完整,可能不會產生預期的排序順序。讓我們深入研究使用 Java 8 lambda 的更有效率、更全面的方法。 使用 Java 8 lambda 的Java 8 透過提供直覺的 lambda 表達式來簡化清單排序,這些...
    程式設計 發佈於2024-11-06
  • 開發人員如何使用 JavaScript 在 HTML 頁面之間安全地交換資料?
    開發人員如何使用 JavaScript 在 HTML 頁面之間安全地交換資料?
    在JavaScript 中維護HTML 頁面之間的資料完整性在網頁之間傳輸資料時,使用查詢參數的傳統方法(例如「http:/ /localhost/ project/index.html?status=exist") 可能會在URL 中暴露敏感資訊。為了解決這個問題,開發人員尋求安全交換資...
    程式設計 發佈於2024-11-06
  • 夾住! VS 程式碼擴充
    夾住! VS 程式碼擴充
    今天我發布了我的第一個 VS Code 擴展 - Clamp it!此擴充功能可以輕鬆為您的 CSS 程式碼產生固定尺寸。 我之所以這麼做是因為想要提高工作效率。我目前的流程包括訪問線上箝位產生器網站,輸入所需的尺寸,然後將其複製並貼上到我的程式碼中。 我在 ChatGPT 的幫助下做到了。 9...
    程式設計 發佈於2024-11-06
  • 掌握 Java 封裝:帶有範例的綜合指南
    掌握 Java 封裝:帶有範例的綜合指南
    Java 封装详细指南 封装是 Java 中的四个基本 OOP(面向对象编程)原则之一,其他原则包括继承、多态性和抽象。封装是指将数据(属性)和操作该数据(行为)的方法捆绑到单个单元或类中。除了捆绑之外,封装还涉及限制对对象的某些组件的直接访问,这通常是通过访问修饰符.来实现的 在...
    程式設計 發佈於2024-11-06
  • 將本機儲存 API 與 JavaScript 和 React JS 結合使用
    將本機儲存 API 與 JavaScript 和 React JS 結合使用
    JavaScript এবং React এ Local Storage API ব্যবহার সম্পর্কে বিস্তারিত আলোচনা করতে পারবেন? JavaScript এবং React এ Local Storage API ব্যবহার খুব ...
    程式設計 發佈於2024-11-06
  • SAML、OAuth 與 OpenID Connect
    SAML、OAuth 與 OpenID Connect
    这篇文章最初发布在我的博客上。使用以下链接查看原始来源: SAML、OAuth 与 OpenID Connect ...
    程式設計 發佈於2024-11-06

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3