前言
最近在看数据结构,提升自己编码能力
代码实现
AVL树是一种自平衡二叉搜索树。它的特点是:每个节点的两个子树的高度差(平衡因子)最多为1。
AVL树通过在必要时进行树旋转来保持平衡,包括LL(左左)旋转、RR(右右)旋转、LR(左右)旋转和RL(右左)旋转。这可以保证查找、插入和删除在AVERAGE和WORST情况下的时间复杂度都是O(logN)。
下面是AVL树的Java实现:
// AVL树节点类
class AVLNode {
int key;
int height;
AVLNode left;
AVLNode right;
public AVLNode(int key) {
this.key = key;
height = 1;
}
}
// AVL树类
class AVLTree {
// 根节点
private AVLNode root;
// 获取节点的高度
private int getHeight(AVLNode node) {
if (node == null) {
return 0;
}
return node.height;
}
// 计算平衡因子
private int getBalanceFactor(AVLNode node) {
if (node == null) {
return 0;
}
return getHeight(node.left) - getHeight(node.right);
}
// LL旋转
private AVLNode LLRotation(AVLNode node) {
AVLNode temp = node.left;
node.left = temp.right;
temp.right = node;
node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
temp.height = Math.max(getHeight(temp.left), getHeight(temp.right)) + 1;
return temp;
}
// RR旋转
private AVLNode RRRotation(AVLNode node) {
// 代码与LL对称,原理相同
}
// LR旋转
private AVLNode LRRotation(AVLNode node) {
// 分两步执行
node.left = RRRotation(node.left);
return LLRotation(node);
}
// RL旋转
private AVLNode RLRotation(AVLNode node) {
// 分两步执行
node.right = LLRotation(node.right);
return RRRotation(node);
}
// 插入节点
public void insert(int key) {
root = insert(root, key);
}
private AVLNode insert(AVLNode node, int key) {
if (node == null) {
return new AVLNode(key);
}
if (key < node.key) {
node.left = insert(node.left, key);
} else {
node.right = insert(node.right, key);
}
// 更新节点高度
node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
// 检查平衡性
int balance = getBalanceFactor(node);
// 不平衡则进行旋转
if (balance > 1) {
if (key < node.left.key) {
return LLRotation(node);
} else {
return LRRotation(node);
}
}
if (balance < -1) {
if (key > node.right.key) {
return RRRotation(node);
} else {
return RLRotation(node);
}
}
return node;
}
// 其他函数如查找、删除,原理类似
}
这样我们就用Java实现了一个带旋转操作的AVL树,它可以在插入和删除时进行自平衡,保证查找效率。
AVL树应用场景主要有:
- 需要快速查找的数据 - AVL树的查找时间复杂度为O(logN),非常快。适用于查找频繁的应用场景。
- 排序与统计 - AVL树天然可以用于数据排序,同时也可以计算数据统计信息。
- 动态数据集 - AVL树可以快速地适应数据集的插入和删除。适用于频繁更新数据的应用。
- 索引构建 - 搜索引擎可以使用AVL树构建关键词索引,如Lucene搜索引擎就采用AVL树。
- 快速查找表 - 可用AVL树实现优先队列、缓存等需要快速查找的表结构。
- 计算几何 - 用AVL树处理计算几何问题,如nearest neighbor search最近邻查找。
- 集合与映射 - 用AVL树实现有序集合与映射,查找速度快。
- 数学运算 - 计算众数、中位数等统计信息时可用AVL树。
- 内存管理 - 一些语言的内存管理会用AVL树实现以提高效率。
- 路由表 - 网络路由表可用AVL树实现以加速IP地址查找。
总体来说,AVL树适合对数据进行快速查找和排序的应用场景。它也可以用来统计和索引数据。由于平衡性,AVL树的查找速度非常稳定可靠。案例: 如何利用 AVL 树从10T 日志数据中查找某个时间段同型号汽车的维护信息:
- 对日志数据进行预处理,提取出日志中与汽车维护相关的字段,如汽车型号、维护时间、维护项目等。
- 将提取出来的维护日志,以“型号+维护时间”为关键字存储在 AVL 树中。其中,型号作为主关键字,维护时间作为次关键字。
- AVL 树以汽车型号分层,同一型号下再以维护时间排序。这样可以快速锁定目标型号。
- 查找时,输入目标型号和时间范围,在 AVL 树中查找此型号的数据,利用 AVL 树的特性可以快速定位到给定时间范围的节点。
- 从这些节点遍历读取其维护项目即可获得该时间段内目标型号的维护信息。
- 可利用 AVL 树动态插入和删除来保证维护新日志的高效性。
AVL 树的平衡性确保了对同一型号的查找时间复杂度为 O(logn)。利用二分查找,可以快速锁定时间范围内的维护记录。相比线性查找,使用 AVL 树可以大幅提高查询效率。
当日志量进一步增大时,可以考虑结合分布式处理平台,在多服务器上存储分片的 AVL 树,并行化查询。// AVL树节点 class AVLNode { String model; // 汽车型号 Date maintainTime; // 维护时间 String maintainItem; // 维护项目 // 其他AVL树需要的字段和方法 } class AVLTree { AVLNode root; // 插入节点 void insert(AVLNode node) { // 插入逻辑以及平衡调整 } // 查询时间范围内的维护信息 List<AVLNode> query(String model, Date startTime, Date endTime) { AVLNode target = find(root, model); // 找到模型对应的子树 if (target == null) return null; List<AVLNode> result = new ArrayList<>(); // 在target子树上利用maintainTime进行二分查找 // 找到时间范围内的节点,添加到result return result; } } public class Main { public static void main(String[] args) { AVLTree tree = new AVLTree(); // 模拟插入一批日志 tree.insert(new AVLNode(" ModelA", time1, item1)); tree.insert(new AVLNode("ModelA", time2, item2)); // 查询时间范围的维护记录 List records = tree.query("ModelA", startTime, endTime); } }
AVL树,并使用Java代码实现AVL树案例