#CS225

AVL

Motivation

AVL 属性:定义高度平衡

关键实现细节:存储高度

恢复平衡:AVL 旋转 (AVL Rotations)

[[ Data Structure-2#核心旋转操作 ]]

AVL 插入 (AVL Insertion)

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#include <algorithm> // for std::max
#include <iostream>  // for potential debugging output

// --- 1. 节点结构 (Node Structure) ---
template <typename T>
struct TreeNode {
    T key;              // 节点存储的键值 (Key)
    int height;         // 以此节点为根的子树的高度 (Height)
    TreeNode *left;     // 指向左子节点的指针 (Left child pointer)
    TreeNode *right;    // 指向右子节点的指针 (Right child pointer)

    // 构造函数 (Constructor)
    TreeNode(T k, TreeNode* l = nullptr, TreeNode* r = nullptr)
        : key(k), height(0), left(l), right(r) {} // 新节点高度初始为 0
};

// --- 2. 辅助函数 (Helper Functions) ---

// 获取节点高度 (安全地处理空指针)
// Get the height of a node (handles NULL safely)
template <typename T>
int height(TreeNode<T>* node) {
    if (node == nullptr) {
        return -1; // 空树的高度定义为 -1 (Height of NULL node is -1)
    }
    return node->height;
}

// 更新节点高度
// Update the height of a node based on its children's heights
template <typename T>
void updateHeight(TreeNode<T>* node) {
    if (node != nullptr) {
        node->height = 1 + std::max(height(node->left), height(node->right));
    }
}

// 获取平衡因子
// Get the balance factor of a node
template <typename T>
int getBalance(TreeNode<T>* node) {
    if (node == nullptr) {
        return 0; // 空节点的平衡因子为 0 (Balance factor of NULL node is 0)
    }
    // Balance factor = height(right subtree) - height(left subtree)
    return height(node->right) - height(node->left);
}


// --- 3. 旋转操作 (Rotation Operations) ---
// 右旋 (LL Case) -围绕 z 进行
// Right Rotation (for LL Imbalance) - Pivot is z
template <typename T>
TreeNode<T>* rightRotate(TreeNode<T>* z) {
    TreeNode<T>* y = z->left;       // y 是 z 的左孩子
    TreeNode<T>* T3 = y->right;     // T3 是 y 的右子树

    // 执行旋转
    y->right = z;
    z->left = T3;

    // 更新高度 (必须先更新 z, 再更新 y)
    // Update heights (must update z first, then y)
    updateHeight(z);
    updateHeight(y);

    // 返回旋转后子树的新根 (y)
    // Return the new root of the subtree (y)
    return y;
}

// 左旋 (RR Case) - 围绕 z 进行
// Left Rotation (for RR Imbalance) - Pivot is z
template <typename T>
TreeNode<T>* leftRotate(TreeNode<T>* z) {
    TreeNode<T>* y = z->right;      // y 是 z 的右孩子
    TreeNode<T>* T2 = y->left;      // T2 是 y 的左子树

    // 执行旋转
    y->left = z;
    z->right = T2;

    // 更新高度 (必须先更新 z, 再更新 y)
    // Update heights (must update z first, then y)
    updateHeight(z);
    updateHeight(y);

    // 返回旋转后子树的新根 (y)
    // Return the new root of the subtree (y)
    return y;
}

// 左右旋 (LR Case) - 先对 y 左旋,再对 z 右旋
// Left-Right Rotation (for LR Imbalance) - Left rotate y, then right rotate z
template <typename T>
TreeNode<T>* leftRightRotate(TreeNode<T>* z) {
    // 对 z 的左孩子 y 进行左旋
    // Left rotate the left child y
    z->left = leftRotate(z->left);
    // 对 z 进行右旋,并返回新根
    // Right rotate z and return the new root
    return rightRotate(z);
}

// 右左旋 (RL Case) - 先对 y 右旋,再对 z 左旋
// Right-Left Rotation (for RL Imbalance) - Right rotate y, then left rotate z
template <typename T>
TreeNode<T>* rightLeftRotate(TreeNode<T>* z) {
    // 对 z 的右孩子 y 进行右旋
    // Right rotate the right child y
    z->right = rightRotate(z->right);
    // 对 z 进行左旋,并返回新根
    // Left rotate z and return the new root
    return leftRotate(z);
}

// --- 4. 插入操作 (Insertion Operation) ---
// (基于幻灯片中的 insert_ 递归辅助函数模板)
// (Based on the recursive helper function template `insert_` from the slides)
template <class T>
class AVLTree { // 假设这些函数在一个 AVLTree 类中
public:
    TreeNode<T>* root = nullptr;

    void insert(const T & x) {
        root = insert_(x, root);
    }

private:
    TreeNode<T>* insert_(const T & x, TreeNode<T>* t) {
        // 1. 执行标准 BST 插入 (Perform standard BST insert)
        if (t == nullptr) {
            return new TreeNode<T>(x); // 创建新节点,高度为 0
        }

        if (x < t->key) {
            t->left = insert_(x, t->left);
        } else if (x > t->key) {
            t->right = insert_(x, t->right);
        } else {
            // 不允许重复键 (Duplicate keys not allowed or handle as needed)
            return t;
        }

        // 2. 更新当前节点的高度 (Update height of the current node)
        updateHeight(t);

        // 3. 获取平衡因子并检查是否失衡 (Get balance factor and check for imbalance)
        int balance = getBalance(t);

        // 4. 如果失衡,执行相应的旋转 (If unbalanced, perform rotations)

        // LL Case (左子树过高,且插入发生在左子树的左侧)
        if (balance < -1 && x < t->left->key) { // 或者检查 getBalance(t->left) <= -1 (标准通常用-1)
             return rightRotate(t);
        }

        // RR Case (右子树过高,且插入发生在右子树的右侧)
        if (balance > 1 && x > t->right->key) { // 或者检查 getBalance(t->right) >= 1 (标准通常用 1)
            return leftRotate(t);
        }

        // LR Case (左子树过高,但插入发生在左子树的右侧)
        if (balance < -1 && x > t->left->key) { // 或者检查 getBalance(t->left) == 1
            return leftRightRotate(t);
        }

        // RL Case (右子树过高,但插入发生在右子树的左侧)
        if (balance > 1 && x < t->right->key) { // 或者检查 getBalance(t->right) == -1
            return rightLeftRotate(t);
        }

        // 5. 返回 (可能已旋转或未旋转的) 子树根节点
        // Return the (possibly rotated) subtree root
        return t;
    }

    // --- 5. 删除操作 (Deletion Operation - 伪代码/概念) ---
    /*
    TreeNode<T>* remove_(const T& x, TreeNode<T>* t) {
        // 1. 执行标准 BST 删除 (Perform standard BST delete)
        if (t == nullptr) { return t; } // 未找到 (Not found)

        if (x < t->key) {
            t->left = remove_(x, t->left);
        } else if (x > t->key) {
            t->right = remove_(x, t->right);
        } else {
            // 找到节点 (Node found)
            if (t->left == nullptr || t->right == nullptr) {
                // 0 或 1 个子节点 (0 or 1 child)
                TreeNode<T>* temp = t->left ? t->left : t->right;
                if (temp == nullptr) { // 无子节点 (No child)
                    temp = t;
                    t = nullptr;
                } else { // 一个子节点 (One child)
                    *t = *temp; // 拷贝内容 (Copy contents)
                }
                delete temp; // 删除旧节点或临时节点 (Delete old node or temp)
                // 注意: 如果使用拷贝内容的方式,需要确保正确处理指针和内存
            } else {
                // 2 个子节点 (2 children)
                // 找到中序后继 (或前驱) (Find inorder successor (or predecessor))
                TreeNode<T>* temp = findMin(t->right); // 假设有 findMin 函数
                // 拷贝后继的值到当前节点 (Copy successor's key)
                t->key = temp->key;
                // 从右子树中删除该后继 (Delete the successor from right subtree)
                t->right = remove_(temp->key, t->right);
            }
        }

        // 如果树在删除后变为空 (If tree became empty after deletion)
        if (t == nullptr) {
          return t;
        }

        // (关键步骤) 从删除点向上回溯,更新高度并检查平衡
        // (Key Step) Backtrack from deletion point, update height & check balance

        // 2. 更新高度 (Update height)
        updateHeight(t);

        // 3. 获取平衡因子 (Get balance factor)
        int balance = getBalance(t);

        // 4. 检查并执行旋转 (Check and perform rotations)
        //    (需要检查左右子节点的平衡因子来确定是单旋还是双旋)
        // LL Case (可能由右侧删除引起左侧过高)
        if (balance < -1 && getBalance(t->left) <= 0) { // 可能为 -1 或 0
            return rightRotate(t);
        }
        // LR Case
        if (balance < -1 && getBalance(t->left) > 0) { // 必须为 1
            return leftRightRotate(t);
        }
        // RR Case (可能由左侧删除引起右侧过高)
        if (balance > 1 && getBalance(t->right) >= 0) { // 可能为 1 或 0
            return leftRotate(t);
        }
        // RL Case
        if (balance > 1 && getBalance(t->right) < 0) { // 必须为 -1
            return rightLeftRotate(t);
        }

        // 5. 返回节点 (Return node)
        return t;
    }

    // (需要 findMin 辅助函数)
    TreeNode<T>* findMin(TreeNode<T>* node) {
        TreeNode<T>* current = node;
        while (current->left != nullptr) {
            current = current->left;
        }
        return current;
    }
    */
}; // end of AVLTree class definition

AVL 删除 (AVL Deletion)

注意AVL的删除可能存在多次旋转,在每一次向下调用结束以后都要注意Retain Balance

总结要点:

  1. 节点高度: TreeNode 结构中包含 height 成员。
  2. 辅助函数: height() 用于安全获取高度,updateHeight() 用于更新高度,getBalance() 用于计算平衡因子。
  3. 旋转: 四种旋转 (rightRotate, leftRotate, leftRightRotate, rightLeftRotate) 是核心,它们在 $O(1)$ 时间内完成指针调整并更新受影响节点的高度。
  4. 插入: 递归地执行 BST 插入,然后在回溯时更新高度、检查平衡,并在必要时调用适当的旋转函数。插入最多只需要一次旋转 (单或双)。
  5. 删除: 同样递归地执行 BST 删除 (可能涉及查找前驱/后继),然后在回溯时更新高度、检查平衡。与插入不同,删除可能需要在多个层级上进行旋转才能完全恢复平衡,因此检查和旋转逻辑在每次递归返回时都要执行。删除的旋转条件判断(特别是子节点的平衡因子)比插入稍有不同,需要考虑子节点平衡因子为 0 的情况。

AVL 树高度分析

红黑树与 AVL 树的比较

特性 (Feature) 红黑树 (Red-Black Tree) AVL 树 (AVL Tree)
最大高度 (Max Height) $2 \cdot lg(n)$ $1.44 \cdot lg(n)$
插入旋转 (Insert Rotations) 常数次 (Constant) 最多 1 次 (单旋或双旋) (Max 1)
删除旋转 (Remove Rotations) 常数次 (Constant) $O(log n)$
查找旋转 (Find Rotations) 0 0
平衡性 (Balance) 弱平衡 (Weaker) 强平衡 (Stronger)
适用性 (Applicability) 更适用于频繁的插入和删除操作 (Frequent Insert/Delete) 更适用于查找操作较多的场景 (Frequent Search)

要点:

数据结构复杂度比较

操作 (Operation) 未排序数组 (Unsorted Array) 排序数组 (Sorted Array) 未排序链表 (Unsorted List) 排序链表 (Sorted List) 一般二叉树 (Binary Tree) BST (二叉搜索树) AVL 树 (AVL Tree)
查找 (Find) $O(n)$ $O(\log n)$ $O(n)$ $O(n)$ $O(n)$ $O(h)$ $O(\log n)$
插入 (Insert) $O(1)$ (摊销 Amortized) $O(n)$ $O(1)$ (头插 Head Insert) $O(n)$ $O(n)$ $O(h)$ $O(\log n)$
删除 (Remove) $O(n)$ (查找+移动/交换) $O(n)$ (查找+移动) $O(n)$ (查找+删除) $O(n)$ (查找+删除) $O(n)$ $O(h)$ $O(\log n)$
遍历 (Traverse) $O(n)$ $O(n)$ $O(n)$ $O(n)$ $O(n)$ $O(n)$ $O(n)$

复杂度解释:

  1. 未排序数组 (Unsorted Array):
  2. 排序数组 (Sorted Array):
  3. 未排序链表 (Unsorted Linked List):
  4. 排序链表 (Sorted Linked List):
  5. 一般二叉树 (Binary Tree, 不一定是 BST):
  6. BST (二叉搜索树 Binary Search Tree):
  7. AVL 树 (AVL Tree, 一种平衡 BST):

结论: 对于搜索密集型应用,AVL 树提供了有保证的对数时间 ($O(\log n)$) 查找、插入和删除性能,相比普通 BST (最坏情况 $O(n)$) 和线性结构具有显著优势。

Application

一维范围搜索 (1D Range-based Searches)

这部分内容将平衡 BST (如 AVL 树) 的概念应用于解决查找特定范围内所有数据点的问题。

问题: 给定一组一维 (1D) 点 $p = {p_1, p_2, …, p_n}$,找出所有满足 $low \le p_i \le high$ 的点 $p_i$。例如:找出在区间 $[11, 42]$ 内的点。

方法一:排序数组 (Sorted Array)

  1. 预处理 (Preprocessing): 创建点的排序数组。成本: $O(n \log n)$。
  2. 查询 (Query):
  3. 总查询时间 (假设数组已排序): $O(\log n + k)$。
  4. 总时间 (包括排序): $O(n \log n + k)$。幻灯片上的 $O(n \log n + k)$ 指的是从头开始的这个总时间。

方法二:平衡 BST (例如 AVL 树)

  1. 预处理: 从点构建一个平衡 BST。成本: $O(n \log n)$ (插入 $n$ 个元素,每个 $O(\log n)$)。幻灯片演示了(隐式平衡的)构建过程。Slide 35 展示了示例点 {3, 6, 11, 33, 41, 44, 55} 的一个可能的平衡 BST。
  2. 查询 (查找 $[low, high]$ 内的点):

技术比喻: 在 BST 中进行一维范围搜索,就像在图书馆的特定区域找书。你使用目录 (树结构) 快速定位到大致的起点和终点 ($O(\log n)$),然后在书架的那个特定区域走动,收集所有符合条件的书 ($O(k)$)。

二维范围搜索与 kD 树 (2D Range-based Searches & kD-Trees)

这部分将思想扩展到二维 (2D) 数据。

问题:

  1. 给定二维点 $p = {(x_1, y_1), …, (x_n, y_n)}$,找出矩形区域 $[(x_1, y_1), (x_2, y_2)]$ 内的所有点。
  2. 找到距离查询点 $(x_q, y_q)$ 最近的点 (Nearest Neighbor Search)。

挑战: 标准的 BST 只能按一个维度排序。我们如何有效地在二维 (或更高维度) 中搜索?

解决方案思路:kD 树 (k-dimensional Tree)

技术比喻: 构建 kD 树就像递归地切蛋糕。首先,你沿着中间垂直切一刀 (x 轴分割)。然后,你把得到的两半分别水平切开 (y 轴分割)。接着,你把得到的四个象限再分别垂直切开,以此类推,将空间分割成越来越小的矩形区域。

核心要点: kD 树是一种将二叉搜索树概念应用于处理多维数据 (multi-dimensional data) 的方法,它通过在不同树层级交替使用不同维度来进行空间分割。这使得在多维空间中进行高效的范围搜索和最近邻搜索成为可能 (尽管实际的搜索算法比一维情况更复杂,可能在后续课程中讲解)。