update
This commit is contained in:
parent
575b5ff4a5
commit
3638a0c290
426
senior/3.数据结构.md
426
senior/3.数据结构.md
@ -779,7 +779,7 @@ public:
|
||||
UnionFind(int size) {
|
||||
parent.resize(size); // 动态数组存储每个节点的父节点
|
||||
rank.resize(size, 1); // 动态数组存储每个节点的秩(树的高度)
|
||||
|
||||
|
||||
// 初始化每个节点的父节点为自己
|
||||
for (int i = 0; i < size; i++) {
|
||||
parent[i] = i;
|
||||
@ -1272,101 +1272,417 @@ int main() {
|
||||
## 笛卡尔树
|
||||
|
||||
### [维基百科用途解释](https://zh.wikipedia.org/wiki/%E7%AC%9B%E5%8D%A1%E5%B0%94%E6%A0%91)
|
||||
|
||||
![1729697900947](image/3.数据结构/1729697900947.png)
|
||||
|
||||
### [bilibili其它up解释](https://www.bilibili.com/video/BV1Fh411E7A4)
|
||||
|
||||
笛卡尔树是一种基于树结构的数据结构,结合了二叉搜索树和堆的特性。它通常用于动态集合的操作,比如合并和查找。笛卡尔树的基本原理是:
|
||||
|
||||
1. **树的结构**:对于每个节点,满足左子树的所有节点小于该节点,右子树的所有节点大于该节点(符合二叉搜索树的性质),同时每个节点的值也是该节点的优先级(符合堆的性质)。
|
||||
|
||||
2. **构建方法**:通过将元素依次插入,保持上述性质。插入时,如果新节点的优先级大于当前节点,则将其插入到当前节点的右子树,否则插入到左子树。
|
||||
|
||||
3. **合并操作**:可以通过递归方法实现合并两个笛卡尔树,比较根节点的优先级,并将较小的树作为左子树合并。
|
||||
|
||||
下面是一个简单的C++实现示例:
|
||||
![1729735106127](image/3.数据结构/1729735106127.png)
|
||||
### 自己的代码解释
|
||||
笛卡尔树(Cartesian Tree)是一种特殊的二叉树,它满足以下两个性质:
|
||||
|
||||
1. **堆性质**:对于每个节点,节点的值大于其子节点的值(或小于,取决于构建的方式)。
|
||||
2. **二叉搜索树性质**:对于每个节点,左子树的值小于节点的值,右子树的值大于节点的值。
|
||||
|
||||
以下是使用 C++ 构建笛卡尔树的详细代码,带有注释:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 定义笛卡尔树节点
|
||||
struct Node {
|
||||
int value; // 节点的值
|
||||
Node* left; // 左子树
|
||||
Node* right; // 右子树
|
||||
|
||||
Node(int val) : value(val), left(nullptr), right(nullptr) {} // 构造函数
|
||||
};
|
||||
|
||||
// 函数:插入节点到笛卡尔树中
|
||||
Node* insert(Node* root, Node* node) {
|
||||
// 如果树为空,直接返回新节点
|
||||
if (!root) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// 根据值的大小关系插入节点
|
||||
if (node->value > root->value) {
|
||||
// 插入到右子树
|
||||
root->right = insert(root->right, node);
|
||||
} else {
|
||||
// 插入到左子树
|
||||
root->left = insert(root->left, node);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// 函数:构建笛卡尔树
|
||||
Node* buildCartesianTree(const vector<int>& arr) {
|
||||
if (arr.empty()) return nullptr;
|
||||
|
||||
// 使用栈来帮助构建树
|
||||
stack<Node*> nodeStack;
|
||||
Node* root = nullptr;
|
||||
|
||||
for (int val : arr) {
|
||||
Node* newNode = new Node(val); // 创建新节点
|
||||
|
||||
// 确保新节点满足堆性质
|
||||
while (!nodeStack.empty() && nodeStack.top()->value < newNode->value) {
|
||||
// 当前节点小于新节点,作为新节点的左子树
|
||||
newNode->left = nodeStack.top();
|
||||
nodeStack.pop();
|
||||
}
|
||||
|
||||
// 如果栈不为空,当前节点作为新节点的右子树
|
||||
if (!nodeStack.empty()) {
|
||||
nodeStack.top()->right = newNode;
|
||||
} else {
|
||||
// 栈为空,当前节点成为根节点
|
||||
root = newNode;
|
||||
}
|
||||
|
||||
// 将新节点压入栈中
|
||||
nodeStack.push(newNode);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// 函数:中序遍历打印树节点
|
||||
void inorderTraversal(Node* root) {
|
||||
if (!root) return;
|
||||
inorderTraversal(root->left);
|
||||
cout << root->value << " ";
|
||||
inorderTraversal(root->right);
|
||||
}
|
||||
|
||||
// 主函数
|
||||
int main() {
|
||||
vector<int> arr = {3, 1, 4, 2}; // 示例数组
|
||||
Node* root = buildCartesianTree(arr);
|
||||
|
||||
// 打印中序遍历结果
|
||||
cout << "Inorder Traversal of Cartesian Tree: ";
|
||||
inorderTraversal(root);
|
||||
cout << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 代码说明:
|
||||
1. **Node 结构体**:定义了树的节点,包括值、左子树和右子树指针。
|
||||
2. **insert 函数**:负责将新节点插入到笛卡尔树中,确保树的性质。
|
||||
3. **buildCartesianTree 函数**:遍历输入数组,使用栈构建笛卡尔树。根据堆性质和二叉搜索树性质逐步插入节点。
|
||||
4. **inorderTraversal 函数**:用于打印树的中序遍历结果。
|
||||
5. **main 函数**:测试构建和遍历笛卡尔树的功能。
|
||||
### [模板题代码](https://www.luogu.com.cn/problem/P5854)
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
|
||||
#ifdef ONLINE_JUDGE
|
||||
#define DEBUG(code)
|
||||
#else
|
||||
#define DEBUG(code)code
|
||||
#endif
|
||||
|
||||
using ll = long long;
|
||||
|
||||
const ll max_n = 1e7+5;
|
||||
struct Node{
|
||||
ll left_child, right_child, val;
|
||||
friend std::ostream& operator<<(std::ostream &os, const Node &n){
|
||||
os<<"Node { val: "<<n.val<<", left_child: "<<n.left_child<<", right_child: "<<n.right_child<<" }";
|
||||
return os;
|
||||
}
|
||||
}nodes[max_n];
|
||||
ll n;
|
||||
std::stack<ll> stk;
|
||||
ll root, ans1, ans2;
|
||||
|
||||
int main(){
|
||||
std::iostream::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
|
||||
|
||||
std::cin>>n;
|
||||
for(ll i{1};i<=n;i++){
|
||||
std::cin>>nodes[i].val;
|
||||
}
|
||||
for(ll i{1};i<=n;i++){
|
||||
while(!stk.empty() && nodes[i].val < nodes[stk.top()].val){
|
||||
nodes[i].left_child = stk.top();
|
||||
stk.pop();
|
||||
}
|
||||
if(!stk.empty()){
|
||||
nodes[stk.top()].right_child = i;
|
||||
}else{
|
||||
root = i;
|
||||
}
|
||||
stk.push(i);
|
||||
}
|
||||
DEBUG(
|
||||
std::cout<<root<<'\n';
|
||||
)
|
||||
for(ll i{1};i<=n;i++){
|
||||
DEBUG(
|
||||
std::cout<<nodes[i]<<'\n';
|
||||
)
|
||||
ans1 ^= i * (nodes[i].left_child + 1);
|
||||
ans2 ^= i * (nodes[i].right_child + 1);
|
||||
}
|
||||
std::cout<<ans1<<' '<<ans2<<'\n';
|
||||
}
|
||||
```
|
||||
|
||||
## 平衡树(AVL, treap, splay)
|
||||
### 什么是平衡树
|
||||
平衡树是一种自我调整的二叉搜索树,其特点是保持树的高度相对较低,从而保证高效的查找、插入和删除操作。常见的平衡树包括:
|
||||
|
||||
1. **AVL树**:每个节点的左子树和右子树的高度差至多为1。AVL树通过旋转操作保持平衡。
|
||||
|
||||
2. **红黑树**:是一种带颜色属性的平衡树,保证任何路径上的黑色节点数量相同,并且红色节点不能连续出现。红黑树的高度相对较低,确保操作的时间复杂度为O(log n)。
|
||||
|
||||
3. **B树**:一种自平衡的多路搜索树,常用于数据库和文件系统中,适合于块存储,能够减少磁盘访问次数。
|
||||
|
||||
平衡树的优势在于能提供高效的动态集合操作,适用于需要频繁插入和删除的场景。你对平衡树的哪一方面感兴趣呢?
|
||||
### [B站动画](https://www.bilibili.com/video/BV1tZ421q72h)
|
||||
### AVL树概述
|
||||
|
||||
**AVL树**(Adelson-Velsky and Landis Tree)是一种自平衡的二叉搜索树。它通过维护每个节点的高度平衡因子来保证树的高度在 \(O(\log n)\) 范围内,从而确保高效的查找、插入和删除操作。
|
||||
|
||||
### 用途
|
||||
|
||||
- **高效查找**:AVL树提供了对动态数据集的高效查找,适合需要频繁查询的应用。
|
||||
- **动态数据结构**:适合需要频繁插入和删除的场景,例如实时数据分析。
|
||||
- **数据库系统**:作为索引结构的一部分,AVL树能够保持较好的平衡,减少访问时间。
|
||||
|
||||
### 原理
|
||||
|
||||
1. **高度平衡因子**:对于每个节点,计算其左子树和右子树的高度差(称为平衡因子),定义为:
|
||||
\[
|
||||
\text{balance\_factor} = \text{height(left\_subtree)} - \text{height(right\_subtree)}
|
||||
\]
|
||||
平衡因子的值只能为 -1、0 或 1。
|
||||
|
||||
2. **旋转操作**:当插入或删除操作导致某个节点的平衡因子超出上述范围时,需要进行旋转以恢复平衡。常见的旋转操作包括:
|
||||
- **右旋**:针对左重的情况。
|
||||
- **左旋**:针对右重的情况。
|
||||
- **左右旋**:针对左重的右子树。
|
||||
- **右左旋**:针对右重的左子树。
|
||||
|
||||
下面是带有详细注释的 AVL树 C++ 实现代码:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
// AVL树节点结构
|
||||
struct Node {
|
||||
int value;
|
||||
int priority;
|
||||
Node *left, *right;
|
||||
int key; // 节点的键值
|
||||
Node* left; // 左子树指针
|
||||
Node* right; // 右子树指针
|
||||
int height; // 节点的高度
|
||||
|
||||
Node(int v) : value(v), priority(rand()), left(nullptr), right(nullptr) {}
|
||||
// 构造函数,初始化节点
|
||||
Node(int value) : key(value), left(nullptr), right(nullptr), height(1) {}
|
||||
};
|
||||
|
||||
Node* merge(Node* left, Node* right) {
|
||||
if (!left) return right;
|
||||
if (!right) return left;
|
||||
// 获取节点的高度
|
||||
int getHeight(Node* node) {
|
||||
return node ? node->height : 0; // 如果节点为空,高度为0
|
||||
}
|
||||
|
||||
if (left->priority > right->priority) {
|
||||
left->right = merge(left->right, right);
|
||||
return left;
|
||||
// 获取节点的平衡因子
|
||||
int getBalance(Node* node) {
|
||||
return node ? getHeight(node->left) - getHeight(node->right) : 0; // 左右子树高度差
|
||||
}
|
||||
|
||||
// 右旋操作
|
||||
Node* rightRotate(Node* y) {
|
||||
Node* x = y->left; // 取左子树为新根
|
||||
Node* T2 = x->right; // 保存x的右子树
|
||||
|
||||
// 进行旋转
|
||||
x->right = y; // y变为x的右子树
|
||||
y->left = T2; // 将T2作为y的左子树
|
||||
|
||||
// 更新节点高度
|
||||
y->height = max(getHeight(y->left), getHeight(y->right)) + 1;
|
||||
x->height = max(getHeight(x->left), getHeight(x->right)) + 1;
|
||||
|
||||
return x; // 返回新的根节点
|
||||
}
|
||||
|
||||
// 左旋操作
|
||||
Node* leftRotate(Node* x) {
|
||||
Node* y = x->right; // 取右子树为新根
|
||||
Node* T2 = y->left; // 保存y的左子树
|
||||
|
||||
// 进行旋转
|
||||
y->left = x; // x变为y的左子树
|
||||
x->right = T2; // 将T2作为x的右子树
|
||||
|
||||
// 更新节点高度
|
||||
x->height = max(getHeight(x->left), getHeight(x->right)) + 1;
|
||||
y->height = max(getHeight(y->left), getHeight(y->right)) + 1;
|
||||
|
||||
return y; // 返回新的根节点
|
||||
}
|
||||
|
||||
// 插入节点
|
||||
Node* insert(Node* node, int key) {
|
||||
// 1. 正常的二叉搜索树插入
|
||||
if (!node) return new Node(key); // 如果树为空,创建新节点
|
||||
if (key < node->key) {
|
||||
node->left = insert(node->left, key); // 插入到左子树
|
||||
} else {
|
||||
right->left = merge(left, right->left);
|
||||
return right;
|
||||
node->right = insert(node->right, key); // 插入到右子树
|
||||
}
|
||||
|
||||
// 2. 更新当前节点的高度
|
||||
node->height = max(getHeight(node->left), getHeight(node->right)) + 1;
|
||||
|
||||
// 3. 获取平衡因子并进行旋转以保持树的平衡
|
||||
int balance = getBalance(node);
|
||||
|
||||
// 左左情况:右旋
|
||||
if (balance > 1 && key < node->left->key) return rightRotate(node);
|
||||
// 右右情况:左旋
|
||||
if (balance < -1 && key > node->right->key) return leftRotate(node);
|
||||
// 左右情况:先左旋再右旋
|
||||
if (balance > 1 && key > node->left->key) {
|
||||
node->left = leftRotate(node->left);
|
||||
return rightRotate(node);
|
||||
}
|
||||
// 右左情况:先右旋再左旋
|
||||
if (balance < -1 && key < node->right->key) {
|
||||
node->right = rightRotate(node->right);
|
||||
return leftRotate(node);
|
||||
}
|
||||
|
||||
return node; // 返回(可能已平衡的)当前节点
|
||||
}
|
||||
|
||||
// 中序遍历函数
|
||||
void inOrder(Node* root) {
|
||||
if (root) {
|
||||
inOrder(root->left); // 遍历左子树
|
||||
cout << root->key << " "; // 输出当前节点的键值
|
||||
inOrder(root->right); // 遍历右子树
|
||||
}
|
||||
}
|
||||
|
||||
void split(Node* root, int key, Node*& left, Node*& right) {
|
||||
if (!root) {
|
||||
left = right = nullptr;
|
||||
return;
|
||||
}
|
||||
if (root->value < key) {
|
||||
left = root;
|
||||
split(root->right, key, left->right, right);
|
||||
} else {
|
||||
right = root;
|
||||
split(root->left, key, left, right->left);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(Node*& root, int value) {
|
||||
Node *newNode = new Node(value);
|
||||
if (!root) {
|
||||
root = newNode;
|
||||
return;
|
||||
}
|
||||
if (root->priority > newNode->priority) {
|
||||
split(root, value, root->left, root->right);
|
||||
root = newNode;
|
||||
root->left = root->left;
|
||||
root->right = root->right;
|
||||
} else {
|
||||
insert(root->value < value ? root->right : root->left, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
int main() {
|
||||
Node* root = nullptr;
|
||||
insert(root, 10);
|
||||
insert(root, 20);
|
||||
insert(root, 15);
|
||||
// 可以继续实现其他操作,如查找、删除等
|
||||
Node* root = nullptr; // 初始化根节点为空
|
||||
// 插入节点
|
||||
root = insert(root, 10);
|
||||
root = insert(root, 20);
|
||||
root = insert(root, 30);
|
||||
root = insert(root, 40);
|
||||
root = insert(root, 50);
|
||||
root = insert(root, 25);
|
||||
|
||||
// 中序遍历结果
|
||||
cout << "中序遍历结果: ";
|
||||
inOrder(root); // 输出: 10 20 25 30 40 50
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
这个示例展示了如何插入节点并保持笛卡尔树的性质。可以根据需要扩展实现其他功能,比如查找和删除。
|
||||
|
||||
## 平衡树(AVL, treap, splay)
|
||||
### 代码说明
|
||||
|
||||
1. **节点结构**:定义了一个`Node`结构体,包含键值、左右子树指针和高度信息。
|
||||
2. **高度和平衡因子计算**:`getHeight`和`getBalance`函数分别用于获取节点的高度和平衡因子。
|
||||
3. **旋转操作**:实现了右旋和左旋操作,以保持树的平衡。
|
||||
4. **插入操作**:插入节点时,首先按照二叉搜索树的规则插入,然后更新高度并检查是否需要旋转来保持平衡。
|
||||
5. **中序遍历**:实现了中序遍历,输出树的节点值,确保按升序排列。
|
||||
|
||||
|
||||
# 常见图
|
||||
|
||||
## 稀疏图
|
||||
稀疏图(sparse graph)是指边的数量相对较少的图。在图论中,通常用图的顶点数量 \( V \) 和边数量 \( E \) 来描述图的稀疏性。对于稀疏图,边的数量 \( E \) 通常满足以下条件:
|
||||
|
||||
\[ E \ll V^2 \]
|
||||
|
||||
这意味着对于一个拥有 \( V \) 个顶点的图,边的数量远远小于 \( V^2 \)。例如,在一个稀疏图中,边的数量可能在 \( O(V) \) 或 \( O(V \log V) \) 的数量级,而不是 \( O(V^2) \)。
|
||||
|
||||
稀疏图的特点包括:
|
||||
|
||||
1. **存储效率**:由于边的数量较少,稀疏图通常使用邻接表(adjacency list)存储,而不是邻接矩阵(adjacency matrix),以节省存储空间。
|
||||
|
||||
2. **算法选择**:在处理稀疏图时,一些算法(如 Dijkstra 算法和 Prim 算法)在稀疏图上会有更好的性能,因为它们可以更高效地处理较少的边。
|
||||
|
||||
3. **应用场景**:稀疏图常见于实际应用中,例如社交网络、道路网络和互联网连接等,这些场景中节点(顶点)相对较多,但连接(边)通常较少。
|
||||
## 偶图(二分图)
|
||||
二分图(Bipartite Graph)是一个特殊的图结构,其顶点可以被分为两个不相交的子集 \( U \) 和 \( V \),并且图中每条边连接的两个顶点分别来自这两个子集。换句话说,二分图中的每条边都连接一个属于 \( U \) 的顶点和一个属于 \( V \) 的顶点。
|
||||
|
||||
### 特征
|
||||
1. **颜色标记**:二分图可以用两种颜色标记顶点,使得任何一条边连接的两个顶点颜色不同。
|
||||
2. **无奇环**:二分图中不存在长度为奇数的环。这是二分图的一个重要性质,能够通过深度优先搜索(DFS)或广度优先搜索(BFS)来验证。
|
||||
|
||||
### 应用
|
||||
二分图在许多实际问题中非常有用,例如:
|
||||
- **匹配问题**:如婚姻匹配、任务分配等。
|
||||
- **推荐系统**:用户和物品之间的关系。
|
||||
- **网络流问题**:如流量分配和最小割问题。
|
||||
|
||||
### 例子
|
||||
假设我们有一个二分图,顶点集 \( U = \{A, B, C\} \) 和 \( V = \{1, 2\} \),边集为 \( \{(A, 1), (A, 2), (B, 2), (C, 1)\} \)。在这个图中:
|
||||
- \( A \) 和 \( B \) 都连接到 \( 2 \),而 \( C \) 连接到 \( 1 \)。
|
||||
- 我们可以将 \( U \) 的顶点涂成一种颜色(例如红色),而 \( V \) 的顶点涂成另一种颜色(例如蓝色),确保相连的顶点颜色不同。
|
||||
## 欧拉图
|
||||
|
||||
欧拉图是指一种特殊的图,它包含一条经过图中每一条边恰好一次的闭合路径,称为欧拉回路。若图中存在这样的路径但不要求回到起点,则称为欧拉路径。对于一个连通的图,存在欧拉回路的必要条件是每个顶点的度数为偶数;而存在欧拉路径的条件是最多有两个顶点的度数为奇数。欧拉图在图论和网络分析中有广泛的应用。
|
||||
## 有向无环图
|
||||
有向无环图(Directed Acyclic Graph,简称 DAG)是一种图结构,其特点是:
|
||||
|
||||
1. **有向性**:图中的边是有方向的,即每条边都有一个起始顶点和一个终止顶点。也就是说,从一个顶点到另一个顶点的边只能单向访问。
|
||||
|
||||
2. **无环性**:在图中不存在从某个顶点出发,通过若干条边再次回到该顶点的路径。换句话说,DAG 中没有环。
|
||||
|
||||
由于这些特性,DAG 被广泛应用于多个领域,包括:
|
||||
|
||||
- **任务调度**:在多任务环境中,DAG 可以用来表示任务之间的依赖关系,确保在执行某个任务之前,所有依赖的任务都已完成。
|
||||
|
||||
- **版本控制系统**:例如 Git 使用 DAG 来管理提交历史,确保每个提交都有一个线性的历史。
|
||||
|
||||
- **数据处理管道**:在数据处理或流处理系统中,DAG 用于表示数据流的处理步骤。
|
||||
|
||||
- **拓扑排序**:DAG 可以进行拓扑排序,生成一个线性序列,使得对于每一条有向边,起始顶点在终止顶点之前。
|
||||
|
||||
## 连通图与强连通图
|
||||
在图论中,连通图和强连通图是描述图中节点连接性质的两个重要概念。
|
||||
|
||||
### 连通图
|
||||
一个无向图称为**连通图**,如果从图中的任意一个节点出发,都可以通过图中的边到达其他任意节点。这意味着图中的任意两个节点之间都有路径相连。如果一个无向图不是连通的,它会被分成多个连通分量,每个连通分量都是一个连通的子图。
|
||||
|
||||
### 强连通图
|
||||
一个有向图称为**强连通图**,如果图中的任意两个节点 \( u \) 和 \( v \) 都存在路径从 \( u \) 到 \( v \) 且从 \( v \) 到 \( u \)。也就是说,强连通图中的任意两个节点都是彼此可达的。如果一个有向图不是强连通的,它也可以分成多个强连通分量,每个强连通分量都是一个强连通的子图。
|
||||
|
||||
### 示例
|
||||
- **连通图**:考虑一个无向图,节点 A、B、C 和 D 之间的边连接形成一个连通图,因为从任意节点出发都可以访问到其他节点。
|
||||
|
||||
- **强连通图**:考虑一个有向图,节点 A、B、C 之间有边 A→B、B→C 和 C→A,这样就形成了一个强连通图,因为每对节点之间都可以找到相应的路径。
|
||||
## 双连通图
|
||||
双连通图(biconnected graph)是图论中的一个重要概念。一个无向图是双连通的,如果它是连通的,并且去掉任意一个顶点后,剩下的图仍然是连通的。换句话说,在双连通图中,任意两个顶点之间有至少两条不同的路径连接它们,这些路径不会经过同一个顶点。
|
||||
|
||||
双连通图的一个重要性质是它的桥(bridge),即边的删去会导致图不连通。双连通图没有桥,因此,任何边的删去都不会破坏图的连通性。
|
||||
|
||||
双连通分量(biconnected component)是双连通图的一个子图,它是最大双连通子图的集合。可以使用深度优先搜索(DFS)来找到图的所有双连通分量。
|
||||
# 哈希表
|
||||
|
||||
## 数值哈希函数构造
|
||||
|
BIN
senior/image/3.数据结构/1729735106127.png
Normal file
BIN
senior/image/3.数据结构/1729735106127.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 333 KiB |
Loading…
Reference in New Issue
Block a user