第1课_什么是平衡树
热度🔥:25 免费课程
授课语音
什么是平衡树
1. 引言
在计算机科学中,树是一个非常常见的数据结构。它的基本特性是节点与节点之间有层级关系,通过指针相连接。平衡树是一类特殊的二叉搜索树(BST),它在传统二叉搜索树的基础上加入了平衡的概念,确保了树的高度在插入和删除元素时保持较小的增量,从而保证了操作的高效性。平衡树的引入解决了二叉搜索树在极端情况下退化成链表的问题,保证了操作的时间复杂度。
2. 平衡树的定义
平衡树本质上是一个二叉搜索树,其中对于每个节点,它的左子树和右子树的高度差(即平衡因子)必须满足某个条件。通过这种限制,平衡树的高度始终保持在对数级别,因此能够保证较快的查询、插入和删除操作。
常见的平衡树有以下几种:
- AVL树:每个节点的左右子树的高度差不能超过1。
- 红黑树:通过定义节点的颜色以及一系列规则,保持树的平衡。
这里,我们将详细介绍其中最常见的 AVL 树。
3. AVL树的介绍
AVL树(Adelson-Velsky and Landis Tree)是一种自平衡的二叉搜索树,它要求树的每个节点的左右子树的高度差的绝对值不超过1。这个高度差称为“平衡因子”。如果某个节点的平衡因子超过了这个范围,就需要通过旋转操作来恢复平衡。
3.1 平衡因子
对于 AVL 树中的任意节点,其平衡因子定义为:
平衡因子 = 左子树高度 - 右子树高度
- 如果平衡因子为0,说明左右子树的高度相等。
- 如果平衡因子为1,说明左子树的高度比右子树大1。
- 如果平衡因子为-1,说明右子树的高度比左子树大1。
当平衡因子的绝对值大于1时,树就不再平衡,这时需要进行旋转操作来恢复平衡。
3.2 旋转操作
AVL树有四种基本的旋转操作,它们可以用来调整树的结构,使其恢复平衡:
- 右旋转(Right Rotation):当左子树太高时,进行右旋转。
- 左旋转(Left Rotation):当右子树太高时,进行左旋转。
- 左右旋转(Left-Right Rotation):当左子树的右子树比左子树的左子树高时,先进行左旋转,再进行右旋转。
- 右左旋转(Right-Left Rotation):当右子树的左子树比右子树的右子树高时,先进行右旋转,再进行左旋转。
4. AVL树的基本操作
4.1 插入操作
插入操作是将一个新元素插入到树中。首先,按照二叉搜索树的规则将元素插入到合适的位置,然后检查树是否平衡。如果插入后某些节点的平衡因子不符合条件,就需要通过旋转操作来恢复树的平衡。
4.2 删除操作
删除操作是从树中删除一个元素。删除元素后,可能导致树的不平衡,因此需要从删除节点开始,逐级向上检查并恢复平衡。
4.3 查询操作
查询操作是查找树中是否包含某个特定元素。AVL树的查询操作和普通的二叉搜索树一样,时间复杂度为O(log n),因为树的高度始终保持在对数级别。
5. 复杂度分析
- 查询操作时间复杂度:由于 AVL树的高度始终保持在O(log n),因此查询操作的时间复杂度为O(log n)。
- 插入操作时间复杂度:插入操作包括查找位置和可能的旋转操作,时间复杂度为O(log n)。
- 删除操作时间复杂度:删除操作同样包括查找位置和旋转操作,时间复杂度为O(log n)。
6. 平衡树的优势
- 保证了操作的时间复杂度:通过保持树的平衡,平衡树确保了查找、插入、删除操作的时间复杂度不会退化为O(n),即使在最坏情况下。
- 自动维护平衡:与普通的二叉搜索树相比,平衡树能够自动维护平衡,不需要开发者手动干预。
7. 平衡树的缺点
- 结构复杂:相比普通的二叉搜索树,平衡树的插入和删除操作相对复杂,需要进行旋转操作。
- 额外的空间开销:为了记录节点的高度或平衡因子,平衡树需要额外的空间来存储这些信息。
8. Java代码示例:AVL树的基本操作
class AVLTree {
class Node {
int key, height;
Node left, right;
Node(int d) {
key = d;
height = 1;
}
}
private Node root;
// 获取节点的高度
private int height(Node N) {
if (N == null)
return 0;
return N.height;
}
// 获取平衡因子
private int getBalance(Node N) {
if (N == null)
return 0;
return height(N.left) - height(N.right);
}
// 右旋操作
private Node rightRotate(Node y) {
Node x = y.left;
Node T2 = x.right;
// 旋转
x.right = y;
y.left = T2;
// 更新高度
y.height = Math.max(height(y.left), height(y.right)) + 1;
x.height = Math.max(height(x.left), height(x.right)) + 1;
return x;
}
// 左旋操作
private Node leftRotate(Node x) {
Node y = x.right;
Node T2 = y.left;
// 旋转
y.left = x;
x.right = T2;
// 更新高度
x.height = Math.max(height(x.left), height(x.right)) + 1;
y.height = Math.max(height(y.left), height(y.right)) + 1;
return y;
}
// 插入节点
public Node insert(Node node, int key) {
if (node == null)
return new Node(key);
// 二叉搜索树插入
if (key < node.key)
node.left = insert(node.left, key);
else if (key > node.key)
node.right = insert(node.right, key);
else // 不允许重复元素
return node;
// 更新节点高度
node.height = 1 + Math.max(height(node.left), height(node.right));
// 检查平衡因子,是否需要旋转
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;
}
// 打印树(中序遍历)
public void inOrder(Node root) {
if (root != null) {
inOrder(root.left);
System.out.print(root.key + " ");
inOrder(root.right);
}
}
}
9. 总结
在今天的课程中,我们学习了平衡树的基本概念以及其中一种常见的实现——AVL树。AVL树通过维护每个节点的平衡因子来确保树的高度保持在对数级别,从而保证了查询、插入和删除操作的高效性。虽然AVL树的实现较为复杂,但它在处理大规模数据时具有不可替代的优势。希望大家通过今天的学习,能够掌握平衡树的基本原理,并能够在实际问题中有效应用它。