查找树(BST到2-3树再到红黑树)
Posted R_Arisa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了查找树(BST到2-3树再到红黑树)相关的知识,希望对你有一定的参考价值。
二叉查找树(BST)
思路
二叉查找树保证任意一个结点的左结点都小于它,而右结点都大于它。
- 插入
插入一个结点时首先查找是否有相同的键,若有则更新即可。
否则直到查找到应该插入的点,创建结点并使得其父结点指向它。 - 查找
递归地查找根结点的子结点,直到找到。 - 删除
删除是BST最复杂的操作。
若待删除结点是叶子结点,那么只需简单地删掉即可。
若待删除结点只有一个子结点,那么只需用其子结点代替自己在树中的位置即可。
最复杂的是待删除结点有两个子结点的情况,这种情况需要使用其后继结点来代替它的位置,这样的话就可以保持BST的有序性。
所谓后继结点,就是指待删除结点右子树中的最小结点。删除的步骤可以分为以下:
- 找到待删除结点node。
- 找到其后继结点sub。
- 用sub代替node的位置。
- 在node的右子树中删除sub结点
对比自己写的C++代码和书中的Java代码,感觉C++好麻烦…
评估
因为插入和查找的行为几乎一致,因此时间复杂度也相同,取决于树的形状。
最坏的情况是有N层,就像一个数组,其时间复杂度为
O
(
N
)
O(N)
O(N)。而最好的情况则是一颗平衡二叉树,其时间复杂度为
O
(
l
o
g
N
)
O(logN)
O(logN)。
查找所需的平均比较次数为
2
l
n
N
2lnN
2lnN
代码
/*
* Key min() 返回最小键
* Key max() 返回最大键
* Key floor(Key key) 返回key向下取整的键
* Key celling(Key key) 返回key向上取整了键
* Key select(int k) 返回排名为k的键(有k个键比它小)
* int rank(Key key) 返回key的排名
* void deleteMin() 删除最小键
* void Delete(Key key) 删除任意键
* void print() 中序遍历,按升序打印键
* vector<Key> &range(Key lo, Key hi) 返回一个存储范围内键的vector
*/
template<typename Key, typename Value>
struct Node
Key key;
Value val;
Node *left;
Node *right;
int N; // 以该结点为根的树中结点的总数
Node(Key key, Value val, int N)
this->key = key;
this->val = val;
this->N = N;
this->left = nullptr;
this->right = nullptr;
;
template<typename Key, typename Value>
class BST
private:
Node<Key, Value> *root;
int size(Node<Key, Value> *root)
if (root == nullptr)
return 0;
else
return root->N;
Value get(Node<Key, Value> *node, Key key)
if (node == nullptr)
return nullptr;
if (node->key == key)
return node->val;
else if (key < node->key)
return get(node->left, key);
else
return get(node->right, key);
Node<Key, Value> *put(Node<Key, Value> *node, Key key, Value val)
// 不存在时,建立新结点
if (node == nullptr)
return new Node<Key, Value>(key, val, 1);
// 如果存在,则更新
if (node->key == key)
node->val = val;
// 否则查找
else if (key < node->key)
node->left = put(node->left, key, val);
else
node->right = put(node->right, key, val);
updateN(root);
return node;
Node<Key, Value> *select(Node<Key, Value> *node, int k)
if (k < size(node->left))
return select(node->left, k);
else if (k > size(node->left))
return select(node->right, k - size(node->left) - 1);
else
return node;
int *rank(Node<Key, Value> *node, Key key)
if (key == node->key)
return size(node->left);
else if (key < size(node->left))
return rank(node->left, key);
else
return rank(node->right, key) + size(node->left) + 1;
int updateN(Node<Key, Value> *node)
if (node == nullptr)
return 0;
else
node->N = updateN(node->left) + updateN(node->right) + 1;
void print(Node<Key, Value> *node)
if (node == nullptr)
return;
print(node->left);
std::cout << node->key << " ";
print(node->right);
void range(std::vector<Key> &arr, Node<Key, Value> *node, Key lo, Key hi)
if (node == nullptr)
return;
if (lo < node->key)
range(arr, node->left, lo, hi);
if (lo <= node->key && node->key <= hi)
arr.push_back(node->key);
if (node->key < hi)
range(arr, node->right, lo, hi);
Node<Key, Value> *Delete(Node<Key, Value> *node, Key key)
if (node == nullptr)
return nullptr;
if (key < node->key)
node->left = Delete(node->left, key);
else if (key > node->key)
node->right = Delete(node->right, key);
else if (key == node->key)
if (node->left != nullptr && node->right != nullptr)
// 左右子结点都不为空
Node<Key, Value> *DeletedRight = node->right;
// 找到后继结点sub(subsequent)
Node<Key, Value> *sub = DeletedRight;
Node<Key, Value> *pre = node;
while (sub->left != nullptr)
pre = sub;
sub = sub->left;
// 让后继结点代替deleted的位置
// sub和右结点是同一结点时需要特殊处理
if (sub == DeletedRight)
sub->left = node->left;
delete node;
return sub;
else
sub->right = DeletedRight;
sub->left = node->left;
pre->left = nullptr;
delete node;
return sub;
else if (node->left == nullptr && node->right == nullptr)
delete node;
return nullptr;
else if (node->left == nullptr)
Node<Key, Value> *newRight = node->right;
delete node;
return newRight;
else if (node->right == nullptr)
Node<Key, Value> *newLeft = node->left;
delete node;
return newLeft;
return node;
// 用于析构函数
void destructor(Node<Key, Value> *node)
if (node == nullptr)
return;
destructor(node->left);
destructor(node->right);
delete node;
public:
BST()
root = nullptr;
~BST()
destructor(root);
int size()
return size(root);
Value get(Key key)
return get(root, key);
void put(Key key, Value val)
root = put(root, key, val);
Key min()
Node<Key, Value> *cur = root;
while (cur->left != nullptr)
cur = cur->left;
return cur->key;
Key max()
Node<Key, Value> *cur = root;
while (cur->right != nullptr)
cur = cur->right;
return cur->key;
Key floor(Key key)
Node<Key, Value> *cur = root;
while (cur->left != nullptr && key < cur->key)
cur = cur->left;
if (cur->right != nullptr && cur->right->key <= key)
return cur->right->key;
else
return cur->key;
Key celling(Key key)
Node<Key, Value> *cur = root;
while (cur->right != nullptr && key > cur->key)
cur = cur->right;
if (cur->left != nullptr && cur->left->key >= key)
return cur->left->key;
else
return cur->key;
Key select(int k)
return select(root, k)->key;
int rank(Key key)
return rank(root, key);
void deleteMin()
Node<Key, Value> *pre = root;
Node<Key, Value> *cur = root->left;
if (cur == nullptr)
if (root->right != nullptr)
root = root->right;
delete pre;
else
delete root;
root = nullptr;
updateN(root);
return;
while (cur->left != nullptr)
cur = cur->left;
pre = pre->left;
pre->left = cur->right;
delete cur;
updateN(root);
void Delete(Key key)
root = Delete(root, key);
updateN(root);
void print()
print(root);
std::vector<Key> range(Key lo, Key hi)
std::vector<Key> arr;
range(arr, root, lo, hi);
return arr;
;
2-3查找树
BST的形状和输入相关,如果想要最坏情况下也只需要对数级别的查找,就需要一个平衡二叉树。下面介绍的2-3查找树就是一颗完美平衡二叉树(即所有空链接到根结点的距离都相等)。
如果说BST的结点是2-结点,即每个结点有两条链接、一个键,那么2-3查找树就是再引入了3-结点,即有三条链接和2个键的结点。它的左链接的键小于该结点,右链接的键大于该结点,中间链接的键则在该结点两个键之间。
对于2-3查找树的构造,可以分为以下几种情况:
- 向2-结点中插入新键
只需将其替换为3-结点即可。 - 向3-结点中插入新键
直接插入使其暂时成为一个4-结点,然后将中间大小的键插入到其父结点中,然后把该3-结点分成2个2-结点。如果此时其父结点变成了4-结点,就重复该操作。如果直到根结点变成了4-结点,就将其中间键提出来作为根结点。
构造结果和输入顺序有关,但都满足定义。
注意如果不把4-结点划分成2个2-结点,则构造后可能不满足“任意空链接到根结点的距离相等”,也可能构造出不符合大小顺序的2-3查找树。如下例:
给该2-3查找树插入13得: