45、数据结构与稀疏矩阵基础:二叉搜索树、堆与矩阵运算

数据结构与稀疏矩阵基础:二叉搜索树、堆与矩阵运算

1. 二叉搜索树(Binary Search Trees)

二叉搜索树(BST)是一种重要的数据结构,对于同一组元素,可以构建出不同形态的 BST。不同的 BST 在搜索元素所需的时间上存在差异,这种差异可以通过树的高度来量化。

树中节点的高度定义为该节点到根节点的唯一路径长度。例如,在某 BST 中,标签为 27 的节点高度可能为 1,而在另一个 BST 中,该节点高度可能为 4。BST 的高度则定义为其所有节点高度的最大值。

搜索操作 bfs_search() 的时间复杂度为 $O(h)$,其中 $h$ 是 BST 的高度。如果 BST 的高度为 $O(log N)$($N$ 为 BST 中的元素数量),则称该 BST 是平衡的。在平衡 BST 中,访问、插入、删除和搜索操作的平均时间复杂度为 $O(log N)$。平衡 BST 非常适合存储需要频繁访问和更新的数据,并且在许多图算法中都有应用。

2. 二叉堆(Binary Heaps)

在许多应用中,需要快速找出一组元素中的最大值或最小值。例如,在 Dijkstra 算法中,每次都需要从未访问节点中选择距离源节点最近的节点。如果将未访问节点存储在数组中,每次搜索最小距离元素需要扫描整个数组,时间复杂度为 $O(N)$;若将数组按距离升序排序,每次更新距离后都需要重新排序,时间复杂度为 $O(N log N)$,这比在未排序数组上的线性搜索更糟糕。

二叉堆是解决此类问题的有效数据结构,它可以保证以 $O(1)$ 的时间复杂度访问和提取集合中的最小(或最大)元素,并以 $O(log N)$ 的操作来更新数据结构。如果一个二叉堆能在常数时间内访问集合中的最大(最小)元素,则称其为最大堆(Max-Heap)或最小堆(Min-Heap)。

2.1 最大堆(Binary Max-Heap)的性质
  • 每个节点都关联一个数值键,键不一定唯一。
  • 树有且仅有一个根节点。
  • 除根节点外,每个节点都有一个父节点,即离根节点更近的邻居。
  • 每个节点最多有两个子节点,分别称为左子节点和右子节点。
  • 树是完全的,即除最后一层外,所有层都被完全填充,最后一层从左到右填充。
  • 最大堆性质:任何节点的键都大于或等于其所有子节点的键。

最小堆的定义与最大堆类似,只是最大堆性质变为任何节点的键小于或等于其所有子节点的键。

例如,给定三个具有相同元素集合的树,只有满足上述所有性质的树才是最大堆。

2.2 堆的操作

二叉堆通常支持以下两种基本操作:
- heap_extract() :移除堆的根节点,并重新排列元素以确保剩余结构仍然是堆。
- heap_insert() :向堆中插入一个新元素。

可以使用数组来实现堆,具体规则如下:
- 数组中位置 $i$ 的节点的左子节点位于位置 $2 × i + 1$。
- 数组中位置 $i$ 的节点的右子节点位于位置 $2 × i + 2$。
- 数组中位置 $i$ 的节点的父节点位于位置 $\lfloor(i - 1)/2\rfloor$。

以下是 heap_extract() sift_down() 的伪代码:

Algorithm 5 heap_extract()
Input: H, N
Output: H, N, max_H
1: max_H ← H[0]
2: H[0] ← H[N - 1]
3: N ← N - 1
4: sift_down(H, N, 0)
5: return max_H

Algorithm 6 sift_down()
Input: H, N, current
Output: H (with max-heap property respected)
1: largest ← current
2: left ← 2 × current + 1
3: right ← 2 × current + 2
4: if left < N and H[left] > H[largest] then
5:     largest ← left
6: end if
7: if right < N and H[right] > H[largest] then
8:     largest ← right
9: end if
10: if largest ≠ current then
11:     swap (H[current], H[largest])
12:     sift_down(H, N, largest)
13:     return
14: end if

以下是 heap_insert() sift_up() 的伪代码:

Algorithm 7 heap_insert()
Input: H, N, new_key
Output: N, H (with new_key added and max-heap property respected)
1: N ← N + 1
2: H[N - 1] ← new_key
3: sift_up(H, N, N)
4: return H

Algorithm 8 sift_up()
Input: H, N, current
Output: H (with max-heap property respected)
1: parent ← ⌊(current - 1)/2⌋
2: while H[parent] < H[current] do
3:     swap(H[parent], H[current])
4:     current ← parent
5:     parent ← ⌊(current - 1)/2⌋
6: end while
7: return H

heap_extract() 操作的流程如下:
1. 读取堆的最大值(存储在数组的第一个位置)并将其放入返回变量 max_H
2. 将数组的最后一个元素移到第一个位置。
3. 调用 sift_down() 函数来恢复最大堆性质。

heap_insert() 操作的流程如下:
1. 增加堆的元素数量。
2. 将新元素插入到堆的最后一个位置。
3. 调用 sift_up() 函数来确保新元素找到合适的位置,以满足最大堆性质。

heap_extract() heap_insert() 的时间复杂度均为 $O(log N)$。

3. 稀疏矩阵(Sparse Matrices)

矩阵是一种常见的数学结构,一个 $m×n$ 的矩阵 $A$ 是由 $m$ 行 $n$ 列的实数组成的表格,矩阵元素用 $a_{ij}$ 表示。矩阵的加法、数乘和乘法运算有特定的定义。矩阵的转置是通过交换行和列得到的,如果一个矩阵等于其转置,则称该矩阵为对称矩阵。

在处理与图相关的矩阵(如邻接矩阵和拉普拉斯矩阵)时,由于现实世界网络中的矩阵通常是稀疏的,即矩阵的非零元素数量 $M$ 远小于 $N^2$($N$ 为矩阵的阶数),因此需要使用特殊的表示方法来提高存储和运算效率。

3.1 ij - 形式(ij - Form)

$N × N$ 矩阵 $A$ 的 ij - 形式使用三个长度为 $M$(矩阵非零元素数量)的向量 $i$、$j$ 和 $s$。向量 $i$ 和 $j$ 分别存储矩阵非零元素的行和列索引,向量 $s$ 存储这些非零元素的值。如果 $A$ 是加权图的邻接矩阵,向量 $s$ 存储边的权重。

通过这些基础的数据结构和矩阵运算知识,我们可以更高效地处理各种算法问题,尤其是在图论和网络分析领域。二叉搜索树和二叉堆为我们提供了高效的数据存储和检索方法,而稀疏矩阵的表示则有助于优化矩阵运算的性能。在实际应用中,我们可以根据具体需求选择合适的数据结构和算法,以提高程序的效率和性能。

下面是一个简单的 mermaid 流程图,展示 heap_extract() 的操作流程:

graph TD;
    A[开始] --> B[读取最大值];
    B --> C[将最后元素移到首位];
    C --> D[调用 sift_down()];
    D --> E[结束];

同时,我们可以用表格总结二叉堆的操作复杂度:
| 操作 | 时间复杂度 |
| ---- | ---- |
| heap_extract() | $O(log N)$ |
| heap_insert() | $O(log N)$ |
| 查找最大值(最大堆) | $O(1)$ |
| 查找最小值(最小堆) | $O(1)$ |

数据结构与稀疏矩阵基础:二叉搜索树、堆与矩阵运算

4. 压缩行存储格式(Compressed Row Storage Format)

除了 ij - 形式,压缩行存储(CRS)格式也是一种常用的稀疏矩阵表示方法。在 CRS 格式中,需要三个数组: row_ptr col_idx values

  • row_ptr :长度为 $m + 1$,其中 $m$ 是矩阵的行数。 row_ptr[i] 表示第 $i$ 行的非零元素在 col_idx values 数组中的起始位置。
  • col_idx :存储所有非零元素的列索引。
  • values :存储所有非零元素的值。

例如,对于一个 $3\times3$ 的稀疏矩阵:
[
A =
\begin{bmatrix}
1 & 0 & 2 \
0 & 3 & 0 \
4 & 0 & 5
\end{bmatrix}
]
其 CRS 格式表示如下:
- row_ptr = [0, 2, 3, 5]
- col_idx = [0, 2, 1, 0, 2]
- values = [1, 2, 3, 4, 5]

通过 row_ptr 数组,我们可以方便地定位每一行的非零元素。例如,第 1 行的非零元素从 col_idx[row_ptr[0]] col_idx[row_ptr[1] - 1] ,即 col_idx[0] col_idx[1] ,对应列索引为 0 和 2,值分别为 values[0] values[1] ,即 1 和 2。

4.1 从 ij - 形式转换到 CRS 格式

将矩阵从 ij - 形式转换到 CRS 格式的步骤如下:
1. 初始化 row_ptr 数组, row_ptr[0] = 0
2. 遍历 i 向量(ij - 形式中的行索引向量),统计每一行的非零元素数量。
3. 根据每一行的非零元素数量,计算 row_ptr 数组的后续元素。
4. 初始化 col_idx values 数组。
5. 再次遍历 i j s 向量,将列索引和元素值分别存储到 col_idx values 数组中。

以下是一个简单的 Python 代码示例,展示如何进行转换:

def ij_to_crs(i, j, s, m):
    row_ptr = [0] * (m + 1)
    for idx in i:
        row_ptr[idx + 1] += 1
    for k in range(1, m + 1):
        row_ptr[k] += row_ptr[k - 1]
    col_idx = [0] * len(i)
    values = [0] * len(i)
    for idx in range(len(i)):
        row = i[idx]
        pos = row_ptr[row]
        col_idx[pos] = j[idx]
        values[pos] = s[idx]
        row_ptr[row] += 1
    # 恢复 row_ptr 的原始值
    for k in range(m, 0, -1):
        row_ptr[k] = row_ptr[k - 1]
    row_ptr[0] = 0
    return row_ptr, col_idx, values
4.2 从 CRS 格式转换到 ij - 形式

将矩阵从 CRS 格式转换到 ij - 形式的步骤如下:
1. 初始化 i j s 向量。
2. 遍历 row_ptr 数组,对于每一行,根据 row_ptr 数组确定该行非零元素的范围。
3. 在该范围内,将行索引、列索引和元素值分别添加到 i j s 向量中。

以下是一个简单的 Python 代码示例,展示如何进行转换:

def crs_to_ij(row_ptr, col_idx, values, m):
    i = []
    j = []
    s = []
    for row in range(m):
        start = row_ptr[row]
        end = row_ptr[row + 1]
        for pos in range(start, end):
            i.append(row)
            j.append(col_idx[pos])
            s.append(values[pos])
    return i, j, s
5. 稀疏矩阵的矩阵 - 向量乘法

稀疏矩阵的一个重要应用是矩阵 - 向量乘法。对于一个 $m\times n$ 的矩阵 $A$ 和一个 $n$ 维向量 $x$,矩阵 - 向量乘法 $y = Ax$ 的结果是一个 $m$ 维向量 $y$。

在稀疏矩阵的情况下,使用 ij - 形式或 CRS 格式可以更高效地执行矩阵 - 向量乘法。

5.1 使用 ij - 形式进行矩阵 - 向量乘法

使用 ij - 形式进行矩阵 - 向量乘法的步骤如下:
1. 初始化结果向量 $y$ 为零向量。
2. 遍历 i j s 向量。
3. 对于每一个非零元素 $a_{ij}$(存储在 s 向量中,行索引为 i ,列索引为 j ),将 $a_{ij}x_j$ 累加到 $y_i$ 中。

以下是一个简单的 Python 代码示例:

def sparse_matrix_vector_multiply_ij(i, j, s, x, m):
    y = [0] * m
    for idx in range(len(i)):
        row = i[idx]
        col = j[idx]
        value = s[idx]
        y[row] += value * x[col]
    return y
5.2 使用 CRS 格式进行矩阵 - 向量乘法

使用 CRS 格式进行矩阵 - 向量乘法的步骤如下:
1. 初始化结果向量 $y$ 为零向量。
2. 遍历每一行。
3. 对于每一行,根据 row_ptr 数组确定该行非零元素的范围。
4. 在该范围内,将非零元素与向量 $x$ 对应元素的乘积累加到 $y$ 的相应行中。

以下是一个简单的 Python 代码示例:

def sparse_matrix_vector_multiply_crs(row_ptr, col_idx, values, x, m):
    y = [0] * m
    for row in range(m):
        start = row_ptr[row]
        end = row_ptr[row + 1]
        for pos in range(start, end):
            col = col_idx[pos]
            value = values[pos]
            y[row] += value * x[col]
    return y
6. 总结与应用场景

通过上述内容,我们了解了二叉搜索树、二叉堆和稀疏矩阵的相关知识。这些数据结构和算法在不同的场景中有着重要的应用。

  • 二叉搜索树 :适用于需要频繁进行插入、删除和搜索操作的场景,尤其是当数据需要有序存储时。平衡二叉搜索树在处理大规模数据时能保证较好的时间复杂度。
  • 二叉堆 :在需要快速找到最大值或最小值的场景中表现出色,如 Dijkstra 算法、优先队列等。堆操作的时间复杂度较低,能有效提高算法效率。
  • 稀疏矩阵 :在处理大规模稀疏矩阵时,ij - 形式和 CRS 格式能显著减少存储空间,并提高矩阵运算的效率。在图论、机器学习等领域,稀疏矩阵的应用非常广泛。

下面是一个 mermaid 流程图,展示稀疏矩阵从 ij - 形式到 CRS 格式的转换流程:

graph TD;
    A[开始] --> B[初始化 row_ptr];
    B --> C[统计每行非零元素数量];
    C --> D[计算 row_ptr 后续元素];
    D --> E[初始化 col_idx 和 values];
    E --> F[遍历 i, j, s 向量];
    F --> G[填充 col_idx 和 values];
    G --> H[恢复 row_ptr 原始值];
    H --> I[结束];

同时,我们可以用表格总结稀疏矩阵不同表示方法的特点:
| 表示方法 | 优点 | 缺点 | 适用场景 |
| ---- | ---- | ---- | ---- |
| ij - 形式 | 简单直观,易于实现 | 存储效率相对较低 | 小规模稀疏矩阵或需要快速构建矩阵的场景 |
| 压缩行存储格式 | 存储效率高,适合矩阵 - 向量乘法 | 构建过程相对复杂 | 大规模稀疏矩阵和需要频繁进行矩阵 - 向量乘法的场景 |

综上所述,掌握这些数据结构和算法对于解决实际问题,特别是在图论、网络分析和机器学习等领域,具有重要的意义。我们可以根据具体的应用场景选择合适的数据结构和算法,以达到最佳的性能和效率。

该数据集通过合成方式模拟了多种发动机在运行过程中的传感器监测数据,旨在构建一个用于机械系统故障检测的基准资源,特别适用于汽车领域的诊断分析。数据按固定时间间隔采集,涵盖了发动机性能指标、异常状态以及工作模式等多维度信息。 时间戳:数据类型为日期时间,记录了每个数据点的采集时刻。序列起始于2024年12月24日10:00,并以5分钟为间隔持续生成,体现了对发动机运行状态的连续监测。 温度(摄氏度):以浮点数形式记录发动机的温度读数。其数值范围通常处于60至120摄氏度之间,反映了发动机在常规工况下的典型温度区间。 转速(转/分钟):以浮点数表示发动机曲轴的旋转速度。该参数在1000至4000转/分钟的范围内随机生成,符合多数发动机在正常运转时的转速特征。 燃油效率(公里/升):浮点型变量,用于衡量发动机的燃料利用效能,即每升燃料所能支持的行驶里程。其取值范围设定在15至30公里/升之间。 振动_X、振动_Y、振动_Z:这三个浮点数列分别记录了发动机在三维空间坐标系中各轴向的振动强度。测量值标准化至0到1的标度,较高的数值通常暗示存在异常振动,可能潜在的机械故障相关。 扭矩(牛·米):以浮点数表征发动机输出的旋转力矩,数值区间为50至200牛·米,体现了发动机的负载能力。 功率输出(千瓦):浮点型变量,描述发动机单位时间内做功的速率,取值范围为20至100千瓦。 故障状态:整型分类变量,用于标识发动机的异常程度,共分为四个等级:0代表正常状态,1表示轻微故障,2对应中等故障,3指示严重故障。该列作为分类任务的目标变量,支持基于传感器数据预测故障等级。 运行模式:字符串类型变量,描述发动机当前的工作状态,主要包括:怠速(发动机运转但无负载)、巡航(发动机在常规负载下平稳运行)、重载(发动机承受高负荷或高压工况)。 数据集整体包含1000条记录,每条记录对应特定时刻的发动机性能快照。其中故障状态涵盖从正常到严重故障的四级分类,有助于训练模型实现故障预测诊断。所有数据均为合成生成,旨在模拟真实的发动机性能变化典型故障场景,所包含的温度、转速、燃油效率、振动、扭矩及功率输出等关键传感指标,均为影响发动机故障判定的重要因素。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值