2024年王道数据结构课后代码题详解:树与图

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:数据结构课程中,树和图是核心内容,尤其在树的数据结构部分,涉及多种类型如二叉树、二叉搜索树、平衡二叉树、堆和图等。本课程后部分代码题针对这些树形结构的实现细节进行深入讲解。学生将通过编写代码来学习树的遍历、插入、删除等操作,以及堆的建立和排序。理解并实践这些操作是提高数据结构理解能力和解决实际问题的关键。学生还可以通过询问或注释来获得帮助,以便更好地准备考研或提升编程技能。 数据结构

1. 数据结构核心概念

数据结构是计算机存储、组织数据的方式,是编程和算法设计的基础。本章将引领我们进入数据结构的殿堂,从最基础的定义出发,探索它的分类和特性,并讨论它在实际应用中的重要性。

数据结构定义

数据结构是计算机存储数据的方式,它包括数据元素、数据元素之间的关系以及对数据的操作方法。正确选择和实现数据结构能够提高程序的运行效率和降低资源消耗。

分类与特性

数据结构可以分为两大类:线性结构和非线性结构。线性结构包括数组、链表、栈和队列等,而非线性结构则包含树、图等。每种数据结构都有其独特的特性和适用场景,例如,数组可以快速随机访问元素,而链表更适合动态数据操作。

数据结构与算法的关系

数据结构是算法的基础。一个高效的数据结构可以简化算法的设计,优化算法的性能。比如,栈结构通常用于实现递归算法,而哈希表则用于快速查找操作。理解数据结构能够帮助我们更好地理解算法的工作原理。

应用场景

数据结构在软件开发的各个领域中都有广泛应用,如数据库索引、网络路由、搜索引擎等。掌握数据结构对于提高开发效率和软件性能至关重要。

通过本章的学习,我们将为深入理解后续的数据结构知识打下坚实的基础。下一章,我们将探索树型数据结构,它是非线性数据结构中的一种,对理解更复杂的系统有着不可或缺的作用。

2. 树的数据结构

树的基本概念

树是由节点组成的非线性结构,它在计算机科学中有着广泛的应用。理解树的基本概念是学习更复杂数据结构的基础。树由节点构成,其中有一个特殊的节点称为根节点。除了根节点外,每个节点有零个或多个子节点,这些子节点自身也可能是树,称为子树。节点的子树数量称为该节点的度。树中的节点可以有父节点和兄弟节点,且每棵树都有一个唯一的根节点。树的高度或深度是根节点到最远叶子节点的最长路径上的边数。

树的特性

树的特性包括: - 根节点是树的第一个节点,没有父节点。 - 每个非根节点都只有一个父节点。 - 子树之间不能有交集,即节点的子树集合互不相交。

树的不同形式

  • 二叉树:每个节点最多有两个子节点,分别是左子节点和右子节点。
  • 完全二叉树:对于深度为k的,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称这棵树是完全二叉树。
  • 平衡二叉树(AVL树):任何节点的两个子树的高度最大差别为1。

树的遍历方法

树的遍历通常分为三种方式:前序遍历、中序遍历和后序遍历。

前序遍历

首先访问根节点,然后对根节点的每一棵子树进行递归的前序遍历。

中序遍历

首先对根节点的左子树进行中序遍历,然后访问根节点,最后对根节点的右子树进行中序遍历。

后序遍历

首先对根节点的每一棵子树进行递归的后序遍历,最后访问根节点。

树的应用实例

树结构广泛用于表示层级关系,如文件系统的目录结构、组织结构图等。此外,树在数据库索引、搜索算法中也有重要应用。

树结构的代码实现

下面是一个简单的二叉树节点的类实现,以及创建树和进行前序遍历的代码示例。

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

class BinaryTree:
    def __init__(self, root_value):
        self.root = TreeNode(root_value)
    def insert(self, node, value):
        if value < node.value:
            if node.left is None:
                node.left = TreeNode(value)
            else:
                self.insert(node.left, value)
        else:
            if node.right is None:
                node.right = TreeNode(value)
            else:
                self.insert(node.right, value)

    def preorder_traversal(self, node, visit):
        if node is not None:
            visit(node.value)
            self.preorder_traversal(node.left, visit)
            self.preorder_traversal(node.right, visit)

# 使用示例
bt = BinaryTree(10)
bt.insert(bt.root, 5)
bt.insert(bt.root, 15)
bt.insert(bt.root, 3)
bt.insert(bt.root, 7)
bt.insert(bt.root, 12)
bt.insert(bt.root, 18)

def visit(value):
    print(value)

bt.preorder_traversal(bt.root, visit)

在上述代码中,我们首先创建了 TreeNode 类来表示树中的节点,每个节点包含值、左子节点和右子节点三个属性。然后定义了 BinaryTree 类来管理树的创建和插入操作。 insert 方法用于按照二叉搜索树的规则添加新的节点。 preorder_traversal 方法实现了树的前序遍历,它首先访问根节点,然后递归遍历左子树和右子树。

树的操作优化

对于树的操作,如插入和遍历,优化可以考虑空间和时间复杂度。例如,使用平衡二叉搜索树(如AVL树)可以在插入和删除操作后自动平衡树结构,保持操作的时间复杂度为O(log n)。同时,非递归遍历方法可以节省栈空间。

树结构的可视化

为了更好地理解树的结构和遍历过程,可视化是一种有效的方法。Mermaid是一种基于文本的图表绘制工具,可以直接在Markdown文件中使用。下面是一个使用Mermaid绘制的简单树结构示例。

graph TD
    A[根节点] -->|左子树| B(左子节点)
    A -->|右子树| C(右子节点)
    B --> D(左孙子节点)
    C --> E(右孙子节点)

上述Mermaid代码描述了一棵树,其中包含一个根节点和四个子节点,它清晰地展示了树的层级和分支关系。

通过本章节的介绍,我们深入了解了树的数据结构,包括其基本概念、特性、特殊形式以及遍历方法。通过实例代码和Mermaid图的展示,我们不仅了解了树结构的理论知识,而且掌握了如何在编程实践中实现和操作树结构。在下一章中,我们将深入探讨二叉树及其各种操作,继续扩展我们的数据结构知识。

3. 二叉树与操作

二叉树基础

二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。二叉树的性质使得它在查找、排序和删除操作中表现优异。特别是二叉搜索树(BST),它不仅保留了二叉树的特性,还引入了排序的特性,即对于任何一个节点,其左子树中的所有元素都小于该节点,而右子树中的所有元素都大于该节点。

二叉树的类型

  • 满二叉树 :在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
  • 完全二叉树 :对于一棵具有 n 个节点的二叉树,按层次自上而下、自左至右编号,如果编号为 i (1≤i≤n)的节点与同深度的满二叉树中编号为 i 的节点在二叉树中的位置完全相同,则这棵二叉树称为完全二叉树。
  • 二叉搜索树 (BST):满足左子树上所有节点的值均小于它的根节点的值;右子树上所有节点的值均大于它的根节点的值。

二叉树的创建

二叉树的创建通常涉及到节点的定义和树结构的构建。下面是一个简单的二叉树节点定义和创建二叉树的代码示例:

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

# 创建一个简单的二叉树
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

在上述代码中,首先定义了一个 TreeNode 类,每个节点包含一个值 value 和两个指向左右子节点的指针 left right 。然后创建了一个根节点,并为其赋予左右子节点。

二叉树的遍历

遍历是二叉树操作中非常重要的一个环节,它允许我们访问树中所有的节点。二叉树的遍历分为三种基本形式:

  • 前序遍历 :先访问根节点,然后遍历左子树,最后遍历右子树。
  • 中序遍历 :先遍历左子树,然后访问根节点,最后遍历右子树。对于二叉搜索树,中序遍历的结果是有序的。
  • 后序遍历 :先遍历左子树,然后遍历右子树,最后访问根节点。

以下是前序遍历的代码实现:

def preorder_traversal(root):
    if root:
        print(root.value)
        preorder_traversal(root.left)
        preorder_traversal(root.right)

preorder_traversal(root)

在这个函数中,我们首先检查当前节点是否存在。如果存在,我们先打印根节点的值,然后递归地对左子树和右子树进行前序遍历。

二叉树节点的增删改查

在二叉树的日常操作中,增删改查是最基本的功能。每种操作都有其特定的逻辑,下面分别介绍每种操作的基本思想和代码实现。

增加节点

增加节点通常发生在树为空或者需要向树中添加叶子节点时。在二叉搜索树中,增加节点需要遵循二叉搜索树的特性:新节点总是被添加为某个叶子节点的左子节点或右子节点。

删除节点

删除节点较为复杂,因为要保持二叉树的特性。删除节点需要考虑三种情况:

  1. 被删除的节点是叶子节点。
  2. 被删除的节点只有一个子节点。
  3. 被删除的节点有两个子节点。

对于第三种情况,通常用该节点的中序遍历的后继(即右子树中的最小节点)或者前驱(即左子树中的最大节点)来替代它,然后删除那个后继或前驱节点。

修改节点

修改节点通常是查找节点并更新其值的操作。在二叉搜索树中,查找操作基于二叉树的特性,可以高效完成。

查询节点

查询节点是指根据特定的值查找二叉树中的节点。同样地,查询操作可以利用二叉树的特性高效地进行。

def find_node(root, value):
    if root is None:
        return None
    if root.value == value:
        return root
    elif value < root.value:
        return find_node(root.left, value)
    else:
        return find_node(root.right, value)

这段代码定义了一个简单的二叉搜索树查询函数。如果当前节点为空,则返回 None ;如果找到值等于目标值的节点,则返回该节点;如果目标值小于当前节点的值,则在左子树中继续查找;如果目标值大于当前节点的值,则在右子树中查找。

树的操作与应用

二叉树的应用场景
  • 表达式解析 :二叉树广泛应用于算术表达式或逻辑表达式的解析。
  • 决策树 :在机器学习中,决策树模型的构建与应用。
  • 数据库索引 :数据库系统经常使用二叉搜索树(BST)作为索引的实现方式。
实际操作示例

对于二叉树的增删改查操作,一个真实的编码练习场景是管理一个简单的员工管理系统。在这个系统中,每个员工可以被赋予子员工,形成一棵组织结构树。以下是使用二叉树对员工进行管理的代码示例:

class Employee:
    def __init__(self, name):
        self.name = name
        self.left = None
        self.right = None

# 创建组织结构树
root = Employee('CEO')
root.left = Employee('CTO')
root.right = Employee('CMO')
root.left.left = Employee('VP of Eng')
root.left.right = Employee('Lead Dev')

在这个例子中,我们定义了一个 Employee 类,每个员工节点包含员工姓名和左右子节点的指针。我们创建了一个公司的组织结构树,CEO是根节点,CTO和CMO是子节点,CTO下又有VP of Engineering和Lead Developer两个子节点。

二叉树操作的优化

优化二叉树操作通常涉及到减少不必要的树结构调整,特别是在删除节点时。在某些情况下,我们可以将被删除节点的子节点直接提升为父节点。此外,平衡二叉树(如AVL树)和红黑树等特殊类型的二叉树可以保证操作的时间复杂度保持在对数级别。

总结

二叉树作为一种基础而强大的数据结构,在计算机科学领域有着广泛的应用。通过本章的介绍,我们学习了二叉树的基本概念、操作以及应用场景。理解这些概念,并通过实际编码练习加以掌握,将有助于读者在未来遇到更复杂的树结构问题时能够游刃有余。

4. 二叉搜索树(BST)

二叉搜索树(BST,Binary Search Tree)是一种特殊的二叉树,它具有非常高的查找效率。BST的核心特性是每个节点都包含一个键值对,且对于任意一个节点而言,它的左子树上所有节点的键值都小于该节点的键值,而其右子树上所有节点的键值都大于该节点的键值。这种严格的顺序性质使得BST在进行查找、插入和删除操作时能够非常高效。

二叉搜索树的特性

4.1.1 定义与特性

二叉搜索树是有序树,这种顺序性不仅有助于快速的搜索,也支持高效的插入和删除操作。BST通常用于实现关联数组(associative array),即可以快速通过键值查找对应的值。

4.1.2 结构示意

下面是一个简单的BST结构示意图:

        8
       / \
      3   10
     / \    \
    1   6   14
       / \   /
      4   7 13

4.1.3 查找过程

在BST中查找一个值的过程与二分查找算法类似,从根节点开始,如果要查找的值小于当前节点的值,则向左子树查找;反之,则向右子树查找。这种查找方式在树结构平衡的情况下,时间复杂度为O(log n)。

4.1.4 插入和删除操作

插入操作相对简单,将新值作为叶子节点插入到BST中适当的位置即可。删除操作较为复杂,需要考虑三种情况:删除的是叶子节点、只含有一个子节点的节点、或者含有两个子节点的节点。

构造方法和算法复杂度分析

4.2.1 构造方法

BST的构造通常有两种方法:一种是通过插入节点的方式逐步构建,另一种是通过排序数组转换得到。通过插入方法构建BST会得到不同的树形结构,而通过排序数组转换得到的BST是平衡的。

4.2.2 算法复杂度

在最坏的情况下,BST可能会退化成链表,这样查找、插入和删除的时间复杂度将会是O(n)。为了保持BST的高效性,可以使用平衡二叉树(如AVL树和红黑树)来避免这种极端情况的发生。

平衡二叉搜索树的概念和实现

4.3.1 平衡概念

平衡二叉搜索树是为了保持树的平衡状态,从而保证搜索操作的高效性。一个平衡的BST需要保证任何节点的左右子树的高度差不超过1。

4.3.2 AVL树

AVL树是一种自平衡的BST,它在每个节点上增加了一个平衡因子属性,该属性是节点左子树和右子树的高度差。在插入和删除操作时,AVL树会通过旋转操作来调整树的平衡。

下面是一个简单的AVL树旋转示意图:

graph TD
    A((4)) -->|左旋转| B((3))
    A --> C((5))
    B --> D((2))
    B -->|右旋转| E((4))
    E --> F((3))
    E --> G((5))

4.3.3 红黑树

红黑树是一种特殊的自平衡二叉搜索树,它通过在每个节点上添加一个颜色属性,以及一套复杂的规则来保持树的平衡。红黑树保证了从根节点到叶子节点的最长可能路径不会超过最短可能路径的两倍长度。

红黑树的五个性质如下:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色。
  3. 所有叶子节点(NIL节点,空节点)都是黑色。
  4. 每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

代码示例

下面是一个简单的BST节点定义及插入操作的代码示例:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def insert_into_bst(root, val):
    if root is None:
        return TreeNode(val)
    if val < root.val:
        root.left = insert_into_bst(root.left, val)
    else:
        root.right = insert_into_bst(root.right, val)
    return root

逻辑分析: - 在上面的代码中,我们首先检查 root 是否为空。如果是空,就创建一个新的节点并返回。如果不为空,我们就根据 val 与当前节点值的比较结果,递归地将 val 插入到左子树或右子树中。 - 这个递归函数的退出条件是当 root 为空时,即到达了应插入新节点的位置。

参数说明: - root :当前树的根节点。 - val :需要插入到BST中的值。

理解了插入操作,删除操作的理解和实现就更为复杂,但基本的思想和逻辑是相同的,只不过是处理的场景更加多样。

在深入学习BST及其相关变种后,如AVL树和红黑树,可以有效地解决BST在数据不平衡时效率下降的问题,并保证其在各种操作下的性能稳定。

5. 堆数据结构及堆排序

堆是一种特殊的完全二叉树,其每个父节点的值都大于或等于其子节点的值(最大堆),或者小于或等于其子节点的值(最小堆)。堆数据结构在优先队列和排序算法中有广泛的应用。在本章节中,我们将深入探讨堆的结构特性、堆的操作以及堆排序算法的原理和实现步骤。

堆数据结构概念

定义及性质

堆(Heap)是一类特殊的完全二叉树,其中每个节点的值都必须大于等于(对于最大堆)或小于等于(对于最小堆)其子节点的值。堆的这种性质使得堆的根节点总能代表堆中的最大值或最小值,这使得堆结构在优先队列和排序算法中非常有用。

在最大堆中,任意节点的值都大于其子节点的值,因此根节点是最大值;在最小堆中,任意节点的值都小于其子节点的值,因此根节点是最小值。

堆的操作

堆的操作主要包括插入(插入新元素后进行堆调整以保持堆性质)、删除堆顶元素(删除后用最后一个元素填充,再进行堆调整)、以及查找最大值或最小值(直接返回根节点的值,因为堆性质保证了这一点)。

堆调整通常是指通过"上浮"和"下沉"操作,来恢复堆的性质。上浮操作是当一个节点的值大于它的父节点时,将节点与其父节点交换位置。下沉操作则相反,是将节点与其子节点中较小(或较大)的那个交换位置。

堆的实现

在编程语言中,堆通常可以用数组来实现。对于数组中的任意位置i,其左子节点位置为2i + 1,右子节点位置为2i + 2,父节点位置为(i - 1) / 2。利用这个性质,我们可以快速访问节点的子节点和父节点,从而进行上浮和下沉操作。

最大堆和最小堆

最大堆和最小堆是堆的两种基本形式。最大堆通常用于实现优先队列,使得每次都能取出优先级最高的元素。最小堆常用于求中位数等场景,以及堆排序算法。

堆排序算法原理

算法步骤

堆排序(Heap Sort)是一种选择排序,其基本思想是通过构建最大堆(或最小堆)来实现排序。堆排序算法主要分为两个步骤:

  1. 构建堆:首先,将待排序的数据构建成一个最大堆。在这个过程中,从最后一个非叶子节点开始,进行下沉操作,保证每个节点都满足堆性质。

  2. 排序过程:接着,重复进行"将堆顶元素与堆中最后一个元素交换,然后调整剩余元素形成最大堆"的操作。每次执行完这个操作后,都将最大的元素放到正确的位置,即数组的末尾,逐渐形成有序序列。

时间复杂度

堆排序的时间复杂度为O(nlogn),因为构建堆的操作时间复杂度为O(n),而n次的下沉操作(每次下沉操作的时间复杂度为O(logn))构成了主要的计算量。

空间复杂度

堆排序的空间复杂度为O(1),因为堆排序是原地排序算法,不需要额外的空间。

堆排序代码实现

下面是一个简单的最大堆排序实现代码示例,使用Python编写:

def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2
    # 如果左子节点大于根节点
    if left < n and arr[i] < arr[left]:
        largest = left
    # 如果右子节点比最大的还大
    if right < n and arr[largest] < arr[right]:
        largest = right
    # 如果最大的不是根节点
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]  # 交换
        heapify(arr, n, largest)

def heapSort(arr):
    n = len(arr)
    # 构建最大堆
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    # 一个个从堆顶取出元素
    for i in range(n - 1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]  # 交换
        heapify(arr, i, 0)

# 测试代码
arr = [12, 11, 13, 5, 6, 7]
heapSort(arr)
n = len(arr)
print("Sorted array is")
for i in range(n):
    print("%d" % arr[i], end=" ")

代码逻辑分析

上述代码中, heapify 函数负责调整一个以给定索引为根的子树,以保证这个子树满足最大堆的性质。 heapSort 函数首先调用 heapify 来构建一个最大堆,然后逐步将堆顶元素(即最大元素)与数组的最后一个元素交换,并调整剩余的数组使其仍然满足最大堆的性质。

参数说明

  • arr : 待排序的数组
  • n : 数组的长度
  • i : 当前需要调整的子树的根节点索引

堆排序优化方法

原地构建堆的优化

堆排序的一个优化点在于原地构建堆。在初始构建堆的过程中,可以逐步扩大堆的范围,从最后一个非叶子节点开始,逐个进行下沉操作,直到根节点。这样做可以减少不必要的数组操作,提高算法效率。

排序过程的优化

在排序过程中,每次从堆顶取下元素后,只需要对新的根节点进行一次下沉操作即可。这是因为堆顶元素被移至数组的尾部后,除了根节点外,其他的堆性质都未被破坏。

堆排序与其它排序算法的比较

堆排序与快速排序、归并排序等算法相比,在最坏情况下的时间复杂度都是O(nlogn),但是堆排序不是稳定的排序算法,因为它在排序过程中可能会改变相等元素的相对顺序。此外,堆排序算法通常比快速排序需要更多的内存访问,因此在实际应用中,快速排序更为常用。

堆排序的优点在于它是一种原地排序算法,不需要额外的空间,适用于内存受限的环境。同时,堆排序在处理大数据量时,其稳定的O(nlogn)时间复杂度也保证了良好的性能。

结论

堆数据结构及堆排序是一个在算法面试和实际编程中非常重要且常见的内容。理解和掌握堆的性质、操作和堆排序算法对于任何IT专业人员都是一项必备的技能。通过上述章节的学习,希望读者能够对堆及其排序原理有更深入的了解,并能在实际问题中灵活应用。

在本章节中,我们详细学习了堆数据结构的定义、性质、实现方法、操作和堆排序算法的原理。通过代码示例和逻辑分析,我们了解了堆排序的实际操作过程,并且指出了堆排序算法的时间复杂度和空间复杂度。此外,我们还探讨了堆排序的优化方法和与其他排序算法的比较,以帮助读者更全面地掌握堆排序的知识。在接下来的章节中,我们将继续探讨树结构的其他重要主题,如树的遍历方法等。

6. 树的遍历方法(DFS、BFS)

树的遍历是树结构操作中不可或缺的一部分,它允许我们访问树中的每个节点,通常用于搜索、排序和其他算法操作。本章将详细介绍树的两种基本遍历方法:深度优先搜索(DFS)和广度优先搜索(BFS)。通过本章的学习,读者不仅能够掌握DFS和BFS的具体算法实现,还能够了解它们在解决实际问题中的应用。

深度优先搜索(DFS)

深度优先搜索是一种沿着树的分支进行搜索直到末端,然后再回溯到其他分支的遍历方法。它的核心思想是尽可能深地搜索树的分支,当节点v的所有邻接节点都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。

算法步骤: 1. 访问起始节点,标记为已访问。 2. 选取一个邻接节点,进行DFS。 3. 如果当前节点没有未被访问的邻接节点,则回溯到上一个节点,重复此步骤。 4. 重复步骤2和3,直到所有的节点都被访问过。

代码示例:

def DFS(graph, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    print(start, end=' ')
    for next in graph[start] - visited:
        DFS(graph, next, visited)
    return visited

# 示例图的表示
graph = {
    'A': {'B', 'C'},
    'B': {'A', 'D', 'E'},
    'C': {'A', 'F'},
    'D': {'B'},
    'E': {'B', 'F'},
    'F': {'C', 'E'}
}

DFS(graph, 'A')

在上述代码中,我们首先定义了DFS函数,该函数接受图 graph 、起始节点 start 和一个可选的 visited 集合。我们使用递归实现DFS,首先访问起始节点并将其打印出来,然后递归地对未访问的邻接节点进行DFS。

广度优先搜索(BFS)

广度优先搜索是一种逐层遍历树结构的方法,从根节点开始,逐个访问同一层级的所有节点,然后再向下访问下一层级的所有节点。它使用队列来追踪待访问的节点。

算法步骤: 1. 创建一个队列并把起始节点入队。 2. 当队列不为空时,执行以下步骤: a. 将队列的第一个节点出队并访问。 b. 将所有未访问的邻接节点入队。 3. 重复步骤2,直到所有节点都被访问。

代码示例:

from collections import deque

def BFS(graph, start):
    visited = set()
    queue = deque([start])
    while queue:
        vertex = queue.popleft()
        if vertex not in visited:
            print(vertex, end=' ')
            visited.add(vertex)
            queue.extend(set(graph[vertex]) - visited)
    return visited

BFS(graph, 'A')

在这个BFS实现中,我们使用了 collections.deque ,它比列表提供了更高效的队列操作。同样地,我们首先访问起始节点并将其加入到已访问集合和队列中。然后,当队列中有节点时,我们将节点出队并访问,然后将其所有未访问的邻接节点加入队列。

实际应用: - 路径查找: 在有向图或无向图中寻找两个节点之间的路径。 - 社交网络: 分析社交网络中的朋友圈或社区结构。 - 搜索引擎: 对网页进行爬取时,决定按何种顺序访问链接。

优化方法: - 避免重复访问: 在BFS和DFS中,我们使用了 visited 集合来记录已经访问过的节点,确保每个节点只访问一次。 - 空间优化: 对于大型图,可以使用双端队列(deque)来存储待访问节点,提高空间使用效率。

通过上述介绍和代码示例,我们了解了DFS和BFS的基本概念、实现步骤和应用场景。在实际编程中,熟练掌握这两种遍历方法对于解决树和图相关的问题至关重要。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:数据结构课程中,树和图是核心内容,尤其在树的数据结构部分,涉及多种类型如二叉树、二叉搜索树、平衡二叉树、堆和图等。本课程后部分代码题针对这些树形结构的实现细节进行深入讲解。学生将通过编写代码来学习树的遍历、插入、删除等操作,以及堆的建立和排序。理解并实践这些操作是提高数据结构理解能力和解决实际问题的关键。学生还可以通过询问或注释来获得帮助,以便更好地准备考研或提升编程技能。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>