二叉搜索树
二叉搜索树是二叉树的的一种,只不过多了个限制条件,即左子树节点的值都小于该点,右子树节点的值都大于该点。
定义:
// 树节点
template <typename T>
class Node
{
public:
T key;
Node* parent;
Node* left;
Node* right;
Node(T k, Node* p = nullptr, Node* l = nullptr, Node* r = nullptr) :
key(k), parent(p), left(l), right(r) {}
};
// 二叉搜索树
template <typename T>
class BSTree
{
private:
Node<T>* root_;
public:
BSTree();
~BSTree();
Node<T>* Search(T key);
Node<T>* Insert(T key);
void Remove(T key);
private:
void Destroy(Node<T>* &head);
};
这里只实现了最关键的三个操作:查找、插入和删除。
查找自然不必多说,类似于二分查找,从根节点开始判断往左往右还是命中。
插入也简单,类似查找,找到要插入的位置即可,注意新插入的节点必为叶子节点。
删除节点就麻烦一些了,需要分情况进行处理。
(1)删除的节点为叶子节点,直接删除即可。
(2)删除的节点只有一个孩子,把这个孩子提上来代替自己即可。
(3)删除的节点左右孩子皆有,这种情况需要先找到右子树最左节点,然后把这个节点跟要删除的节点交换,这样就变成了删除右子树最左节点了,而该最左节点必定无左孩子,这样删除就变成上面两种情况之一了。
这里还要注意的一点是当删除的节点为根节点时,要维护好根节点的更新。
实现:
template <typename T>
BSTree<T>::BSTree() : root_(nullptr) {}
template <typename T>
BSTree<T>::~BSTree()
{
Destroy(root_);
}
template <typename T>
Node<T>* BSTree<T>::Search(T key)
{
Node<T>* cur = root_;
while (cur && cur->key != key)
{
if (key < cur->key)
cur = cur->left;
else
cur = cur->right;
}
return cur;
}
template <typename T>
Node<T>* BSTree<T>::Insert(T key)
{
Node<T>* cur = root_;
Node<T>* pre = nullptr;
// 找到插入位置
while (cur)
{
pre = cur;
if (key == cur->key)
return cur;
else if (key < cur->key)
cur = cur->left;
else
cur = cur->right;
}
Node<T>* new_node = new Node<T>(key);
new_node->parent = pre;
// 插入为根节点
if (pre == nullptr)
root_ = new_node;
// 插入为左孩子
else if (key < pre->key)
pre->left = new_node;
// 插入为右孩子
else
pre->right = new_node;
}
template <typename T>
void BSTree<T>::Remove(T key)
{
Node<T>* remove;
// 不存在该节点
if ((remove = Search(key)) == nullptr)
return;
// 为叶子节点,直接删除
if (remove->left == nullptr && remove->left == nullptr)
{
if (remove->parent == nullptr) // 删除节点为根节点的情况
root_ = nullptr;
else if (remove->parent->left == remove)
remove->parent->left = nullptr;
else
remove->parent->right = nullptr;
delete remove;
return;
}
// 左孩子为空,直接把右孩子提上来后删除自己
if (remove->left == nullptr)
{
if (remove->parent == nullptr)
{
remove->right->parent = nullptr;
root_ = remove->right;
}
else if (remove->parent->left == remove)
{
remove->right->parent = remove->parent;
remove->parent->left = remove->right;
}
else
{
remove->right->parent = remove->parent;
remove->parent->right = remove->right;
}
delete remove;
return;
}
// 右孩子为空,直接把左孩子提上来后删除自己
if (remove->right == nullptr)
{
if (remove->parent == nullptr)
{
remove->left->parent = nullptr;
root_ = remove->left;
}
else if (remove->parent->left == remove)
{
remove->left->parent = remove->parent;
remove->parent->left = remove->left;
}
else
{
remove->left->parent = remove->parent;
remove->parent->right = remove->left;
}
delete remove;
return;
}
// 左右孩子均不为空的情况,把该节点跟右子树最小节点交换后,转换为删除右子树最小节点
Node<T>* right_min = remove->right;
while (right_min->left) // 找到右子树最小节点
{
right_min = right_min->left;
}
remove->key = right_min->key; // 与要删除的节点交换
if (right_min->right) // 有右孩子,转换为第三种情况
{
right_min->right->parent = right_min->parent;
right_min->parent->left = right_min->right;
}
else // 无右孩子,转换为第二种情况
{
if (right_min->parent->left == right_min)
right_min->parent->left = nullptr;
else
right_min->parent->right = nullptr;
}
delete right_min;
return;
}
template <typename T>
void BSTree<T>::Destroy(Node<T>* &head)
{
if (head == nullptr)
return;
if (head->left)
Destroy(head->left);
if (head->right)
Destroy(head->right);
delete head;
head = nullptr;
}
AVL 自平衡二叉搜索树
AVL树在二叉搜索树的基础上加了平衡的限制条件,即对于每个节点,左右子树的高度差不能超过1,这样对于某些情况,该搜索树的性能就不至于下降。
AVL树的定义于普通的二叉搜索树大同小异,不过受限于平衡条件,插入和删除节点的操作就变得复杂起来,在每次插入或删除节点后,都要判断是否失衡并恢复平衡。
定义:
template <typename T>
class Node
{
public:
T key;
int height;
Node* left;
Node* right;
Node(T k, Node* l = nullptr, Node* r = nullptr) :
key(k), height(0), left(l), right(r) {}
};
template <typename T>
class AVLTree
{
private:
Node<T>* root_;
public:
AVLTree();
~AVLTree();
int Height(Node<T>* head);
Node<T>* Search(T key);
void Insert(T key);
void Remove(T key);
private:
Node<T>* LLRotation(Node<T>* head);
Node<T>* RRRotation(Node<T>* head);
Node<T>* LRRotation(Node<T>* head);
Node<T>* RLRotation(Node<T>* head);
Node<T>* Insert(Node<T>* &head, T key);
Node<T>* Remove(Node<T>* &head, Node<T>* r);
void Destroy(Node<T>* &head);
};
四种失衡状态及其恢复
(1)LL
(2)RR
(3)LR
(4)RL
有了四种失衡情况调整后就可以实现插入、删除了,这里插入删除都是用递归来写,比如插入,判断要往左子树还是右子树插入,递归下去,返回时就要判断左右子树是否出现了失衡,如果出现就判断是哪种情况并进行处理。
实现:
template <typename T>
AVLTree<T>::AVLTree() : root_(nullptr) {}
template <typename T>
AVLTree<T>::~AVLTree()
{
Destroy(root_);
}
template <typename T>
int AVLTree<T>::Height(Node<T>* head)
{
if (head)
return head->height;
return -1;
}
template <typename T>
Node<T>* AVLTree<T>::Search(T key)
{
Node<T>* cur = root_;
while (cur && cur->key != key)
{
if (key < cur->key)
cur = cur->left;
else
cur = cur->right;
}
return cur;
}
template <typename T>
Node<T>* AVLTree<T>::LLRotation(Node<T>* head)
{
Node<T>* tmp = head->left;
head->left = tmp->right;
tmp->right = head;
head->height = max(Height(head->left), Height(head->right)) + 1;
tmp->height = max(Height(tmp->left), Height(tmp->right)) + 1;
return tmp;
}
template <typename T>
Node<T>* AVLTree<T>::RRRotation(Node<T>* head)
{
Node<T>* tmp = head->right;
head->right = tmp->left;
tmp->left = head;
head->height = max(Height(head->left), Height(head->right)) + 1;
tmp->height = max(Height(tmp->left), Height(tmp->right)) + 1;
return tmp;
}
template <typename T>
Node<T>* AVLTree<T>::LRRotation(Node<T>* head)
{
head->left = RRRotation(head->left);
return LLRotation(head);
}
template <typename T>
Node<T>* AVLTree<T>::RLRotation(Node<T>* head)
{
head->right = LLRotation(head->right);
return RRRotation(head);
}
template <typename T>
Node<T>* AVLTree<T>::Insert(Node<T>* &head, T key)
{
// 插入为根节点
if (head == nullptr)
{
head = new Node<T>(key);
}
// 往左子树插入
else if (key < head->key)
{
head->left = Insert(head->left, key);
// 判断是否失衡
if (Height(head->left) - Height(head->right) == 2)
{
if (key < head->left->key)
head = LLRotation(head);
else
head = LRRotation(head);
}
}
// 往右子树插入
else if (key > head->key)
{
head->right = Insert(head->right, key);
if (Height(head->right) - Height(head->left) == 2)
{
if (key > head->right->key)
head = RRRotation(head);
else
head = RLRotation(head);
}
}
// 更新高度
head->height = max(Height(head->left), Height(head->right)) + 1;
// 返回插入后的根节点
return head;
}
template <typename T>
void AVLTree<T>::Insert(T key)
{
Insert(root_, key);
}
template <typename T>
Node<T>* AVLTree<T>::Remove(Node<T>* &head, Node<T>* r)
{
// 找不到要删除的节点
if (head == nullptr || r == nullptr)
return nullptr;
// 待删除的节点在左子树中
if (r->key < head->key)
{
head->left = Remove(head->left, r);
// 判断是否失衡
if (Height(head->right) - Height(head->left) == 2)
{
if (Height(head->right->left) > Height(head->right->right))
head = RLRotation(head);
else
head = RRRotation(head);
}
}
// 待删除的节点在右子树中
else if (r->key > head->key)
{
head->right = Remove(head->right, r);
if (Height(head->left) - Height(head->right) == 2)
{
if (Height(head->left->left) > Height(head->left->right))
head = LLRotation(head);
else
head = LRRotation(head);
}
}
// head对应的节点就是要删除的节点
else
{
// head的左右孩子皆非空
if (head->left != nullptr && head->right != nullptr)
{
// 在左右子树高度较高者中找到最右或最左节点,与head交换,然后转为删除该节点
// 删除策略与二叉搜索树一致
// 在高度较高的子树中删除不会破坏平衡条件
if (Height(head->left) > Height(head->right))
{
Node<T>* cur = head->left;
while (cur->right)
cur = cur->right;
head->key = cur->key;
head->left = Remove(head->left, cur);
}
else
{
Node<T>* cur = head->right;
while (cur->left)
cur = cur->left;
head->key = cur->key;
head->right = Remove(head->right, cur);
}
}
// 有一个空节点或者为叶子节点
else
{
Node<T>* tmp = head;
head = head->left != nullptr ? head->left : head->right;
delete tmp;
}
}
// 返回删除后的根节点
return head;
}
template <typename T>
void AVLTree<T>::Remove(T key)
{
Node<T>* r = Search(key);
if (r)
{
root_ = Remove(root_, r);
}
}
template <typename T>
void AVLTree<T>::Destroy(Node<T>* &head)
{
if (head == nullptr)
return;
if (head->left)
Destroy(head->left);
if (head->right)
Destroy(head->right);
delete head;
head = nullptr;
}