图的深度优先遍历类似于二叉树

一、概述

图是计算机科学中的一类非常重要的数据结构,用于描述物体间的关系。在图中,节点(vertex)代表物体,边(edge)则代表物体间的关系。通过遍历图,我们可以对图中的节点进行访问,同时也可以找出图中节点之间的关系。

深度优先遍历是图遍历中重要的一种方法,它类似于二叉树的前序遍历,按照深度优先的方式访问图中的节点。具体而言,深度优先遍历会首先访问某个节点,接着访问该节点的邻居节点,并进一步访问邻居的邻居……直到访问到所有相关的节点。

本文主要介绍如何实现图的深度优先遍历,以及为什么它类似于二叉树。

二、图的深度优先遍历

在实现图的深度优先遍历时,我们需要考虑如何遍历每个节点,并标记哪些节点已经被遍历过。

下面是一个示例图,展示了深度优先遍历中的遍历顺序:

     1 - 2 - 3
     |   |   |
     4 - 5 - 6

上图中,我们从节点1开始遍历,并首先访问它的邻居节点2。接着,我们访问节点2的邻居节点3,再访问节点3的邻居节点6……直到访问了所有相关的节点。

下面给出实现图的深度优先遍历的代码示例:

// 图的邻接表表示法
class Graph {
    constructor() {
        // 存储图的节点
        this.vertices = [];
        // 存储图的边
        this.edges = new Map();
    }
    // 添加节点
    addVertex(v) {
        this.vertices.push(v);
        this.edges.set(v, []);
    }
    // 添加边
    addEdge(v, w) {
        this.edges.get(v).push(w);
        this.edges.get(w).push(v);
    }
    // 深度优先遍历
    dfs(startingNode) {
        const visited = new Set();
        this._dfs(startingNode, visited);
    }
    _dfs(vertex, visited) {
        visited.add(vertex);
        console.log(vertex);
        const neighbors = this.edges.get(vertex);
        for (let i = 0; i < neighbors.length; i++) {
            const neighbor = neighbors[i];
            if (!visited.has(neighbor)) {
                this._dfs(neighbor, visited);
            }
        }
    }
}

三、图的深度优先遍历类似于二叉树的前序遍历

虽然图和二叉树是两种不同的数据结构,但它们之间的遍历方式非常相似。图的深度优先遍历类似于二叉树的前序遍历,都是首先访问本节点,接着访问左子节点(或者邻居节点),最后访问右子节点(或者邻居节点)。下面是一个对比图和二叉树前序遍历的示意图:

     图的深度优先遍历:           二叉树的前序遍历:
         1                           A
        / \                         / \
       2   4                       B   D
      / \   \                     / \   \
     3   5   6                   C   E   F
                                / \     / \
                               G   H   I   J

从上面的示意图中可以看出,图的深度优先遍历和二叉树的前序遍历本质上是相同的遍历方式。唯一的区别在于,图没有一个指定的根节点,而是从任一节点开始遍历。

下面给出实现图的深度优先遍历类似于二叉树前序遍历的代码示例:

class Graph {
    // ...
    // 深度优先遍历类似于二叉树前序遍历
    dfs(node) {
        const visited = new Set();
        this._dfs(node, visited);
    }
    _dfs(node, visited) {
        visited.add(node);
        console.log(node);
        const neighbors = this.edges.get(node);
        for (const neighbor of neighbors) {
            if (!visited.has(neighbor)) {
                this._dfs(neighbor, visited);
            }
        }
    }
}

四、图的深度优先遍历与回溯

在实现图的深度优先遍历时,我们需要考虑如何在访问之后回到原来的节点。相比于二叉树的遍历,图的遍历涉及到多个路径,因此需要更加小心地考虑回溯的问题。

在深度优先遍历过程中,我们使用递归函数来遍历每个节点。如果我们走到一个死路,即当前节点没有相邻节点可访问,那么我们需要回到之前的节点(回溯),查找还有没有相邻节点可访问。如果回到原来的节点后,还有未访问的相邻节点,那么我们将继续访问,直到访问完所有相关的节点。

下面给出实现遍历时的回溯代码示例:

class Graph {
    // ...
    // 深度优先遍历
    dfs(startingNode) {
        const visited = new Set();
        this._dfs(startingNode, visited);
    }
    _dfs(node, visited) {
        visited.add(node);
        console.log(node);
        const neighbors = this.edges.get(node);
        for (const neighbor of neighbors) {
            if (!visited.has(neighbor)) {
                this._dfs(neighbor, visited);
            }
        }
    }
}

五、图的深度优先遍历与栈

在上面的示例代码中,我们使用递归实现图的深度优先遍历。但递归调用可能会导致栈溢出的问题,特别是在遍历大型图时,很容易出现栈溢出错误。因此,我们可以使用栈(stack)来实现深度优先遍历,而不是使用递归函数。

在使用栈实现深度优先遍历时,我们需要考虑如何在遍历完一个节点之后,回溯到它的前一个节点,并继续遍历。因此,我们需要维护一个栈,保存已经访问过的节点。同时,我们还需要维护一个表,记录每个节点是否已经被访问过。

下面给出使用栈实现遍历的代码示例:

class Graph {
    // ...
  iter_dfs(startingNode) {
      const visited = new Set();
      const stack = [];
      stack.push(startingNode);
      while (stack.length !== 0) {
          const node = stack.pop();
          if (!visited.has(node)) {
              visited.add(node);
              console.log(node);
              const neighbors = this.edges.get(node);
              for (const neighbor of neighbors) {
                  if (!visited.has(neighbor)) {
                      stack.push(neighbor);
                  }
              }
          }
      }
  }
}

六、总结

对于图的深度优先遍历类似于二叉树的前序遍历,我们需要遍历每个节点,并标记哪些节点已经被遍历过。在实现遍历时,我们需要考虑如何回溯到原来的节点,以及使用栈来替代递归实现深度优先遍历。

图的深度优先遍历类似于二叉树的前序遍历,但需要注意的是,图的遍历涉及到多个路径,因此需要更加小心地考虑回溯的问题。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/246023.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-12 13:12
下一篇 2024-12-12 13:12

相关推荐

  • Python遍历集合中的元素

    本文将从多个方面详细阐述Python遍历集合中的元素方法。 一、for循环遍历集合 Python中,使用for循环可以遍历集合中的每个元素,代码如下: my_set = {1, 2…

    编程 2025-04-29
  • Python如何遍历字典中的key和value

    本文将详细讲解Python中如何遍历字典中的key和value,包括多种遍历方式以及在遍历过程中的一些应用场景。 一、遍历字典中的key和value 在Python中,字典是一种无…

    编程 2025-04-29
  • 深度查询宴会的文化起源

    深度查询宴会,是指通过对一种文化或主题的深度挖掘和探究,为参与者提供一次全方位的、深度体验式的文化品尝和交流活动。本文将从多个方面探讨深度查询宴会的文化起源。 一、宴会文化的起源 …

    编程 2025-04-29
  • 使用PHP foreach遍历有相同属性的值

    本篇文章将介绍如何使用PHP foreach遍历具有相同属性的值,并给出相应的代码示例。 一、基础概念 在讲解如何使用PHP foreach遍历有相同属性的值之前,我们需要先了解几…

    编程 2025-04-28
  • Python下载深度解析

    Python作为一种强大的编程语言,在各种应用场景中都得到了广泛的应用。Python的安装和下载是使用Python的第一步,对这个过程的深入了解和掌握能够为使用Python提供更加…

    编程 2025-04-28
  • 二叉树非递归先序遍历c语言

    本文将为您详细介绍二叉树的非递归先序遍历算法,同时提供完整的C语言代码示例。通过本文,您将了解到二叉树的先序遍历算法,以及非递归实现的方式。 一、二叉树的先序遍历算法介绍 在介绍二…

    编程 2025-04-28
  • Python如何遍历列表

    在Python编程中,列表是一种常用的数据类型,它允许我们存储多个值。但是,我们如何遍历列表并对其中的每个值进行操作呢? 一、for循环遍历列表 fruits = [‘apple’…

    编程 2025-04-28
  • Python遍历字典删除元素

    本文主要介绍Python中如何遍历字典并删除元素。在实际应用中,遍历字典并删除元素是一种非常常见的操作,但需要注意的是,直接在字典中删除元素可能会改变字典中其他元素的索引顺序,因此…

    编程 2025-04-28
  • Python列表构建二叉树

    本文将从以下几个方面详细阐述如何使用Python列表构建二叉树: 一、二叉树的基本概念 二叉树是一种重要的数据结构,其每个节点最多有两个子节点,左子节点和右子节点。左子节点始终比右…

    编程 2025-04-27
  • Python递归深度用法介绍

    Python中的递归函数是一个函数调用自身的过程。在进行递归调用时,程序需要为每个函数调用开辟一定的内存空间,这就是递归深度的概念。本文将从多个方面对Python递归深度进行详细阐…

    编程 2025-04-27

发表回复

登录后才能评论