树结构存储方案深度解析:从理论到工业实践
树结构存储方案对比全景图
一、双亲表示法(Parent-Only Representation)
🛠 存储结构实现(C++优化版)
#include <vector>
using namespace std;
struct ParentTreeNode {
int data;
int parent = -1;
};
class ParentTree {
private:
vector<ParentTreeNode> nodes;
int root = -1;
public:
ParentTree(int rootData) {
nodes.emplace_back({rootData, -1});
root = 0;
}
void addChild(int parentIdx, int data) {
if(parentIdx >= nodes.size()) return;
nodes.emplace_back({data, parentIdx});
}
int getParent(int idx) const {
return (idx < nodes.size()) ? nodes[idx].parent : -1;
}
};
性能特征
| 操作 | 时间复杂度 | 空间复杂度 |
|---|
| 找父节点 | O(1) | O(n) |
| 找子节点 | O(n) | O(n) |
| 添加节点 | O(1) | O(n) |
典型应用场景
- 文件系统目录结构(快速定位上级目录)
- 组织架构树(快速获取直接上级)
- 并查集(Union-Find)数据结构
二、孩子表示法(Children List Representation)
🛠 存储结构实现(C++智能指针版)
#include <vector>
#include <memory>
using namespace std;
struct ChildNode {
int index;
shared_ptr<ChildNode> next = nullptr;
};
struct TreeNode {
int data;
shared_ptr<ChildNode> firstChild = nullptr;
};
class ChildrenTree {
private:
vector<TreeNode> nodes;
int root = 0;
void clearChildren(shared_ptr<ChildNode>& node) {
if(node) {
clearChildren(node->next);
node.reset();
}
}
public:
ChildrenTree(int rootData) {
nodes.emplace_back(TreeNode{rootData});
}
~ChildrenTree() {
for(auto& node : nodes) {
clearChildren(node.firstChild);
}
}
void addChild(int parentIdx, int data) {
if(parentIdx >= nodes.size()) return;
int newIdx = nodes.size();
nodes.emplace_back(TreeNode{data});
auto newChild = make_shared<ChildNode>(ChildNode{newIdx});
newChild->next = nodes[parentIdx].firstChild;
nodes[parentIdx].firstChild = newChild;
}
};
性能特征
| 操作 | 时间复杂度 | 空间复杂度 |
|---|
| 找子节点 | O(1) | O(n+e) |
| 找父节点 | O(n) | O(n+e) |
| 添加节点 | O(1) | O(n+e) |
e表示边数,n表示节点数
典型应用场景
- DOM树结构(频繁操作子元素)
- 多级菜单系统
- 家谱树可视化
三、孩子兄弟表示法(Left-Child Right-Sibling)
存储结构实现(C++模板化实现)
template <typename T>
struct CSNode {
T data;
unique_ptr<CSNode> firstChild;
unique_ptr<CSNode> nextSibling;
CSNode(T val) : data(val), firstChild(nullptr), nextSibling(nullptr) {}
};
template <typename T>
class CSTree {
private:
unique_ptr<CSNode<T>> root;
void insertChild(CSNode<T>* parent, T value) {
auto newNode = make_unique<CSNode<T>>(value);
if(!parent->firstChild) {
parent->firstChild = move(newNode);
} else {
auto current = parent->firstChild.get();
while(current->nextSibling) {
current = current->nextSibling.get();
}
current->nextSibling = move(newNode);
}
}
public:
CSTree(T rootValue) {
root = make_unique<CSNode<T>>(rootValue);
}
void addChild(CSNode<T>* parent, T value) {
if(parent) insertChild(parent, value);
}
void levelOrder() {
queue<CSNode<T>*> q;
q.push(root.get());
while(!q.empty()) {
auto node = q.front();
q.pop();
cout << node->data << " ";
auto child = node->firstChild.get();
while(child) {
q.push(child);
child = child->nextSibling.get();
}
}
}
};
存储结构示意图
性能特征
| 操作 | 时间复杂度 | 空间复杂度 |
|---|
| 找子节点 | O(1) | O(n) |
| 找父节点 | O(n) | O(n) |
| 层次遍历 | O(n) | O(n) |
典型应用场景
- 树与二叉树相互转换
- XML文档解析
- 表达式树处理
四、工业级存储方案选择指南
1. 决策矩阵
| 评估维度 | 双亲表示法 | 孩子表示法 | 孩子兄弟法 |
|---|
| 父节点访问频率 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| 子节点操作频率 | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 内存效率 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 转换二叉树需求 | ⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
2. 混合方案实践
struct HybridNode {
int data;
int parent = -1;
unique_ptr<HybridNode> firstChild;
unique_ptr<HybridNode> nextSibling;
};
3. 内存优化技巧
五、前沿发展与扩展阅读
- 持久化树结构:研究不可变树结构的高效存储
- 分布式树存储:研究跨节点的树结构分片方案
- GPU加速遍历:利用CUDA实现大规模树结构并行处理
建议:在工程实践中,建议通过Profile工具分析实际访问模式,再选择存储方案。例如高频父访问+低频子操作可选择双亲表示法,反之则选孩子兄弟法。