深度:二叉树的根节点到叶子节点的最长路径所包含的节点个数。
满二叉树:若二叉树的深度为 h h h,则包含 2 h − 1 2^h-1 2h−1 个节点的二叉树为满二叉树。
完全二叉树:若二叉树的深度为 h h h,则完全二叉树满足:
- 除了第 h h h 层以外所有层节点数均为最大值
- 第 h h h 层有 1 ∼ 2 h − 1 1 \sim 2^{h-1} 1∼2h−1 个节点,且所有节点均靠左。
二叉排序树:空树是二叉排序树,二叉排序树中根节点的左子树中每个节点的值均小于根节点的值,根节点的右子树中每个节点的值均大于根节点的值,且这两棵子树也是二叉排序树。
AVL 树:AVL 树是一种特殊的二叉排序树。空树是 AVL 树。若 AVL 树非空,则其左右子树的深度差的绝对值不超过 1,且左右子树也是 AVL 树。
二叉树的顺序存储:若某一节点下标为 i i i,则其左孩子的下标为 2 i + 1 2i+1 2i+1,右孩子的下标为 2 i + 2 2i+2 2i+2。若二叉树是完全二叉树,则顺序表中间没有空项。
前序遍历(迭代法)
图的深度优先遍历相当于二叉树的前序遍历
先将根节点入栈,之后每次迭代先将栈顶元素出栈到结果数组,再将右孩子入栈,再将左孩子入栈。迭代结束条件为栈空。
中序遍历(迭代法)
每次迭代若当前节点不为空,则将当前节点入栈,下次迭代处理左孩子;若当前节点为空,则栈顶为父节点,将栈顶节点出栈到结果数组,下次迭代处理该父节点的右孩子即可。最终若当前节点为空且栈为空,则说明整棵树已经遍历完毕。
后序遍历(迭代法)
将前序遍历的顺序改为 根->右->左,再将结果反转即可。
通过将空结点入栈实现前中后序遍历的统一思路
将栈初始化为只有根节点,每次迭代时先弹出栈顶节点作为当前节点,迭代的结束条件为栈空。
- 若当前节点非空,则按照遍历的相反顺序(如中序遍历是右->中->左)入栈,需要注意的是若左右孩子为空则不入栈,当前(中)节点入栈后还要再入栈一个空节点。
- 若当前节点为空,则先弹出一个节点(该节点必为空节点),再弹出一个节点(该节点为当前节点的父节点)到结果数组。
Morris 遍历
利用二叉树中大量的空指针实现常数空间复杂度的遍历
前序遍历
令当前节点为根节点,之后进行如下的迭代,迭代的结束条件为当前节点为空节点:
- 若当前节点的左子树为空,则将当前节点加入结果数组,再令当前节点为右孩子继续迭代。
- 若当前节点的左子树非空,则首先找到左子树的最右侧节点(中序前驱节点,须注意在指针不断右移时可能会使指针移回当前节点,要添加相应的停止条件使指针最后指向真正的前驱节点)。若前驱节点的右子树为空,说明左子树尚未遍历,将当前节点加入结果数组,并将前驱节点的右孩子改为当前节点,最后令当前节点为当前节点的左孩子继续迭代;若前驱节点的右子树非空,则其必指向当前节点,说明左子树已遍历完成,此时将前驱节点的右子树重新设为空,令当前节点为当前节点的右孩子继续迭代。
上面的第一条对应 空->中->右 的遍历顺序,第二条对应 中->左->右 的遍历顺序。须注意特殊情况:当前节点为叶子节点时,其右孩子会指向祖先节点,下一次迭代会回到该祖先节点并将右孩子重新设为空,这样便使得原二叉树不会被更改。
中序遍历
令当前节点为根节点,之后进行如下的迭代,迭代的结束条件为当前节点为空节点:
- 若当前节点的左子树为空,则将当前节点加入结果数组,再令当前节点为右孩子继续迭代。
- 若当前节点的左子树非空,则首先找到左子树的最右侧节点(中序前驱节点,须注意在指针不断右移时可能会使指针移回当前节点,要添加相应的停止条件使指针最后指向真正的前驱节点)。若前驱节点的右子树为空,说明左子树尚未遍历,将前驱节点的右孩子改为当前节点,最后令当前节点为当前节点的左孩子继续迭代;若前驱节点的右子树非空,则其必指向当前节点,说明左子树已遍历完成,此时将当前节点加入结果数组,将前驱节点的右子树重新设为空,令当前节点为当前节点的右孩子继续迭代。
上面的第一条对应 空->中->右 的遍历顺序,第二条对应 左->中->右 的遍历顺序。须注意特殊情况:当前节点为叶子节点时,其右孩子会指向祖先节点,下一次迭代会回到该祖先节点并将右孩子重新设为空,这样便使得原二叉树不会被更改。
后序遍历
令当前节点为根节点,之后进行如下的迭代,迭代的结束条件为当前节点为空节点,迭代结束后要将从根节点到最右边节点这条路径上的节点倒序加入结果数组中:
- 若当前节点的左子树为空,则令当前节点为右孩子继续迭代。
- 若当前节点的左子树非空,则首先找到左子树的最右侧节点(中序前驱节点,须注意在指针不断右移时可能会使指针移回当前节点,要添加相应的停止条件使指针最后指向真正的前驱节点)。若前驱节点的右子树为空,将前驱节点的右孩子改为当前节点,令当前节点为当前节点的左孩子继续迭代;若前驱节点的右子树非空,则其必指向当前节点,将前驱节点的右子树重新设为空,并将左孩子到前驱节点这条路径上的节点倒序加入结果数组中,令当前节点为当前节点的右孩子继续迭代。
若要理解这种遍历方式为何是后序遍历,可以这样重画二叉树的拓扑:从根节点开始,左孩子向正下方画,右孩子向正右方画,高度相同的连成一条线的节点为一层,则右侧无节点的节点会指回上一层的最左侧节点,每次输入到结果数组时从右向左输入一层的节点并将该层最右侧的回指指针删除。须注意特殊情况:当前节点为叶子节点时,其右孩子会指向祖先节点,下一次迭代会回到该祖先节点并将右孩子重新设为空,这样便使得原二叉树不会被更改。
层序遍历
使用图的广度优先遍历算法(队列迭代法)即可,若要每层单独输出一个数组,则要记录队列大小,每次出队元素数量达到这个大小时说明已经遍历完一层,重新记录队列大小开始输出下一个数组。迭代结束条件为队列空。
二叉排序树的实现
以下所有代码仅要求
ElementType
支持operator<(const ElementType&, const ElementType&)
以及默认、拷贝构造函数、拷贝赋值运算符。
节点数据结构:包括存储的具体数据、父节点指针、左右孩子指针。
template <typename ElementType>
struct TreeNode {
using Pointer = std::shared_ptr<TreeNode<ElementType>>;
TreeNode(ElementType element = ElementType(), Pointer parent = nullptr) :
element(element), parent(parent), leftChild(nullptr), rightChild(nullptr) {}
ElementType element;
Pointer parent;
Pointer leftChild;
Pointer rightChild;
};
二叉排序树数据结构:保存根节点指针即可。
成员函数:find
、insert
、erase
。
find
查找给定值的所在位置,若查找不到则返回 nullptr
。
insert
将给定值插入合适的位置,若树中已存在该值则不作更改。
erase
删除给定值对应的节点,若该值不存在则不作更改。
erase 的实现细节
定义函数 __findMin(Position)
从输入节点开始查找最左侧的节点,即 Position 对应的子树的最小值节点。
定义函数 __eraseZeroOrOneChildNonRootNode(Position)
输入参数必须是叶子节点或只有一个孩子的节点,且不能为根节点,该函数会执行具体的节点删除操作。
首先调用 this->find
查找给定值 element
,若未找到则不作处理,若为根节点则分成如下三种情况:
- 根节点有两个孩子,此时将根节点删除根节点右子树的最小节点即可。
- 根节点有一个孩子,此时将根节点设为该孩子,之后将该节点的父节点指针置为空。
完整实现代码
template <typename ElementType>
class SearchTree {
public:
using Position = typename TreeNode<ElementType>::Pointer;
SearchTree() : __root(nullptr) {}
Position find(const ElementType& element) {
Position p = __root;
while (nullptr != p) {
if (element < p->element) p = p->leftChild;
else if (p->element < element) p = p->rightChild;
else break; // p->element == element
}
return p;
}
Position insert(const ElementType& element) {
if (nullptr == __root)
__root = Position(new TreeNode<ElementType>(element));
Position p = __root;
while (true) {
if (element < p->element) {
if (nullptr == p->leftChild)
p->leftChild = Position(new TreeNode<ElementType>(element, p));
p = p->leftChild;
}
else if (p->element < element) {
if (nullptr == p->rightChild)
p->rightChild = Position(new TreeNode<ElementType>(element, p));
p = p->rightChild;
}
else break; // p->element == element
}
return p;
}
void erase(const ElementType& element) {
Position x = this->find(element);
if (nullptr == x) return;
if (__root == x) {
if (nullptr != __root->leftChild && nullptr != __root->rightChild) {
Position p = __findMin(__root->rightChild);
__root->element = p->element;
__eraseZeroOrOneChildNonRootNode(p);
}
else if (nullptr != __root->leftChild) {
__root = __root->leftChild;
__root->parent = nullptr;
}
else if (nullptr != __root->rightChild) {
__root = __root->rightChild;
__root->parent = nullptr;
}
else __root = nullptr; // __root is leaf node
}
else { // __root != x
if (nullptr != x->leftChild && nullptr != x->rightChild) {
Position p = __findMin(x->rightChild);
x->element = p->element;
__eraseZeroOrOneChildNonRootNode(p);
}
else __eraseZeroOrOneChildNonRootNode(x);
}
}
private:
static Position __findMin(Position start) {
if (nullptr == start) return nullptr;
while (nullptr != start->leftChild)
start = start->leftChild;
return start;
}
static void __eraseZeroOrOneChildNonRootNode(Position node) {
Position p = node->parent;
if (node == p->leftChild) {
if (nullptr != node->leftChild) {
p->leftChild = node->leftChild;
p->leftChild->parent = p;
}
else if (nullptr != node->rightChild) {
p->leftChild = node->rightChild;
p->leftChild->parent = p;
}
else p->leftChild = nullptr;
}
else { // node == p->rightChild
if (nullptr != node->leftChild) {
p->rightChild = node->leftChild;
p->rightChild->parent = p;
}
else if (nullptr != node->rightChild) {
p->rightChild = node->rightChild;
p->rightChild->parent = p;
}
else p->rightChild = nullptr;
}
}
Position __root;
};
AVL 树的实现
首先实现 AVL 树的节点类。
// AVLNode.hpp
#pragma once
#include <memory>
#include <tuple>
#include <string>
// annotations
template <typename Data> class AVLNode;
template <typename Data> using AVLPointer = std::shared_ptr<AVLNode<Data>>;
template <typename Data> int
getHeight(AVLPointer<Data> node);
template <typename Data> void
updateHeight(AVLPointer<Data> node);
template <typename Data> std::pair<AVLPointer<Data>, int>
updateHeightsBottomUp(AVLPointer<Data> start);
// "Data" must support operator<, default and copy constructor,
// and copy assignment.
template <typename Data>
class AVLNode {
public:
// in-class type annotations
using Pointer = AVLPointer<Data>;
// friend function annotations
friend int getHeight<Data>(AVLPointer<Data> node);
friend void updateHeight<Data>(AVLPointer<Data> node);
friend auto updateHeightsBottomUp<Data>(AVLPointer<Data> start) ->
std::pair<AVLPointer<Data>, int>;
// constructor
AVLNode(Pointer parent = nullptr, const Data& value = Data()):
__height(0), __value(value), __parent(parent),
__leftChild(nullptr), __rightChild(nullptr) {}
// get-set methods
Pointer getParent() const { return __parent; }
void setParent(Pointer node) { __parent = node; }
Pointer getLeftChild() const { return __leftChild; }
void setLeftChild(Pointer node) { __leftChild = node; }
Pointer getRightChild() const { return __rightChild; }
void setRightChild(Pointer node) { __rightChild = node; }
Data getValue() const { return __value; }
void setValue(const Data& value) { __value = value; }
/**
* serialize the AVLNode
* e.g.
* this->getValue() == 2
* this->getParent()->getValue() == 0
* this->getLeftChild() == nullptr
* this->getRightChild()->getValue() == 1
* then return "1 <- 2 ^ 0"
*/
std::string serialize() const {
std::string result;
if (nullptr != __leftChild)
result += std::to_string(__leftChild->getValue()) + " <- ";
result += std::to_string(__value);
if (nullptr != __rightChild)
result += " -> " + std::to_string(__rightChild->getValue());
if (nullptr != __parent)
result += " ^ " + std::to_string(__parent->getValue());
return result;
}
private:
Data __value; int __height;
Pointer __parent, __leftChild, __rightChild;
};
// Search a value in a tree. If input an empty tree, it will return nullptr.
template <typename Data> AVLPointer<Data>
search(const Data& value, AVLPointer<Data> tree) {
AVLPointer<Data> current = tree;
while (nullptr != current) {
const Data& currentValue = current->getValue();
if (value < currentValue) current = current->getLeftChild();
else if (currentValue < value) current = current->getRightChild();
else return current;
}
return nullptr;
}
// Check if the node exists in the tree. If input an empty node, it will return true.
// If input a non-empty node but an empty tree, it will return false.
template <typename Data> inline bool
exist(AVLPointer<Data> node, AVLPointer<Data> tree) {
return nullptr == node ?
true : nullptr != search(node->getValue(), tree);
}
// Get the height of the node. If input an empty node, it will return -1.
// If input an leaf node, it will return 0.
// If input an non-empty and non-leaf node,
// it will return the longest distance between the node and it's farest leaf.
template <typename Data> inline int
getHeight(AVLPointer<Data> node) {
return nullptr == node ?
-1 : node->__height;
}
// Update the height of the node by checking the children's height.
// In other words, let the height of the node be
// the greater height of the children's heights plus 1.
// If input an empty node, it will do nothing.
template <typename Data> inline void
updateHeight(AVLPointer<Data> node) {
if (nullptr != node)
node->__height = std::max(
getHeight<Data>(node->__leftChild),
getHeight<Data>(node->__rightChild)) + 1;
}
// Update the heights bottom-up by the path: start to root node.
// When updating, if we find the unbalanced tree for the first time,
// we will record it and it's balance factor.
// If input an empty start node or we can't find the smallest unbalanced tree,
// it will return { nullptr, 0 }.
// Else, it will return { smallest unbalaced tree, balance factor }.
// Note: A tree's balance factor is
// the height of the left child minus the height of the right child.
template <typename Data> std::pair<AVLPointer<Data>, int>
updateHeightsBottomUp(AVLPointer<Data> start) {
AVLPointer<Data> smallestUnbalacedTree(nullptr);
int balanceFactor = 0;
while (nullptr != start) {
int lh = getHeight<Data>(start->getLeftChild());
int rh = getHeight<Data>(start->getRightChild());
if (nullptr == smallestUnbalacedTree && abs(lh - rh) > 1) {
smallestUnbalacedTree = start;
balanceFactor = lh - rh;
}
start->__height = std::max(lh, rh) + 1;
start = start->__parent;
}
return { smallestUnbalacedTree, balanceFactor };
}
// Left-rotate the given tree, return the new root node of the new tree.
// After the rotation, every node in the new tree will be with the correct height.
// If input a empty tree, it will return nullptr.
// If input a tree without a right child, it will do nothing and return the old root node.
template <typename Data> AVLPointer<Data>
leftRotate(AVLPointer<Data> root) {
if (nullptr == root) return nullptr;
AVLPointer<Data> rc = root->getRightChild();
if (nullptr == rc) return root;
root->setRightChild(rc->getLeftChild());
rc->setLeftChild(root);
rc->setParent(root->getParent());
root->setParent(rc);
if (nullptr != root->getRightChild())
root->getRightChild()->setParent(root);
updateHeight<Data>(root);
updateHeight<Data>(rc);
return rc;
}
// Right-rotate the given tree, return the new root node of the new tree.
// After the rotation, every node in the new tree will be with the correct height.
// If input a empty tree, it will return nullptr.
// If input a tree without a left child, it will do nothing and return the old root node.
template <typename Data> AVLPointer<Data>
rightRotate(AVLPointer<Data> root) {
if (nullptr == root) return nullptr;
AVLPointer<Data> lc = root->getLeftChild();
if (nullptr == lc) return root;
root->setLeftChild(lc->getRightChild());
lc->setRightChild(root);
lc->setParent(root->getParent());
root->setParent(lc);
if (nullptr != root->getLeftChild())
root->getLeftChild()->setParent(root);
updateHeight<Data>(root);
updateHeight<Data>(lc);
return lc;
}
// Adjust the given unbalanced tree of which the inserted node is in the left child of the left child.
// It will return the new root node of the new tree.
// After the adjustment, every node in the new tree will be with the correct height.
template <typename Data> inline AVLPointer<Data>
adjustLL(AVLPointer<Data> root) {
return rightRotate<Data>(root);
}
// Adjust the given unbalanced tree of which the inserted node is in the right child of the left child.
// It will return the new root node of the new tree.
// After the adjustment, every node in the new tree will be with the correct height.
template <typename Data> inline AVLPointer<Data>
adjustLR(AVLPointer<Data> root) {
if (nullptr != root) {
root->setLeftChild(leftRotate<Data>(root->getLeftChild()));
// After the rotation on the left child, the real height of the root may be modified.
updateHeight(root);
return rightRotate<Data>(root);
}
return nullptr;
}
// Adjust the given unbalanced tree of which the inserted node is in the right child of the right child.
// It will return the new root node of the new tree.
// After the adjustment, every node in the new tree will be with the correct height.
template <typename Data> inline AVLPointer<Data>
adjustRR(AVLPointer<Data> root) {
return leftRotate<Data>(root);
}
// Adjust the given unbalanced tree of which the inserted node is in the left child of the right child.
// It will return the new root node of the new tree.
// After the adjustment, every node in the new tree will be with the correct height.
template <typename Data> inline AVLPointer<Data>
adjustRL(AVLPointer<Data> root) {
if (nullptr != root) {
root->setRightChild(rightRotate<Data>(root->getRightChild()));
// After the rotation on the right child, the real height of the root may be modified.
updateHeight(root);
return leftRotate<Data>(root);
}
return nullptr;
}
// Try to adjust the given unbalanced tree correctly by
// the information of the inserted node and the balance factor.
// Note that the given tree would be modified only if abs(balance factor) == 2,
// or it will do nothing.
template <typename Data> AVLPointer<Data>
rotate(
AVLPointer<Data> unbalancedTree,
AVLPointer<Data> insertedNode,
const int balanceFactor) {
// Check if an empty tree is input.
if (nullptr == unbalancedTree) return nullptr;
AVLPointer<Data> lt = unbalancedTree->getLeftChild();
AVLPointer<Data> rt = unbalancedTree->getRightChild();
// Check if the given tree satisfy the condition of adjustRX.
if (-2 == balanceFactor && nullptr != rt) {
if (exist<Data>(insertedNode, rt->getRightChild()))
return adjustRR<Data>(unbalancedTree);
else
return adjustRL<Data>(unbalancedTree);
}
// Check if the given tree satisfy the condition of adjustLX.
else if (2 == balanceFactor && nullptr != lt) {
if (exist<Data>(insertedNode, lt->getLeftChild()))
return adjustLL<Data>(unbalancedTree);
else
return adjustLR<Data>(unbalancedTree);
}
else return unbalancedTree;
}
// Serialize the given tree in pre-order.
template <typename Data> std::string
serialize(AVLPointer<Data> tree) {
if (nullptr == tree) return "";
return tree->serialize() + '\n' +
serialize<Data>(tree->getLeftChild()) +
serialize<Data>(tree->getRightChild());
}
基于如上代码,我们实现了 AVL 树的序列化、插入节点的四种调节、节点高度的判断和自下而上更新节点高度并获取最小不平衡子树的方法,这些方法将用于 AVL 树的插入和删除,接下来是 AVL 树的实现。
// AVLTree.hpp
#pragma once
#include "AVLNode.hpp"
// annotations
template <typename Data> class AVLTree;
// Check if the given tree is a balanced tree.
// If input an empty tree, it will return true.
template <typename Data> bool
check(AVLPointer<Data> tree) {
if (nullptr == tree) return true;
Data val = tree->getValue();
AVLPointer<Data> lc = tree->getLeftChild();
AVLPointer<Data> rc = tree->getRightChild();
// Check if the heights of the root and the two children are valid.
if (getHeight<Data>(tree) != std::max(getHeight<Data>(lc), getHeight<Data>(rc)) + 1)
return false;
if (abs(getHeight<Data>(lc) - getHeight<Data>(rc)) > 1)
return false;
// Check if the left child and the right child are both balanced trees,
// and if the value of the two child is valid.
bool lcIsValid = true, rcIsValid = true;
if (nullptr != lc) {
if (!(lc->getValue() < val)) return false;
lcIsValid = check<Data>(lc);
}
if (nullptr != rc) {
if (!(val < rc->getValue())) return false;
rcIsValid = check<Data>(rc);
}
return lcIsValid && rcIsValid;
}
// Check if the given tree is a balanced tree.
template <typename Data> bool
check(AVLTree<Data>& tree) {
return check(tree.__root);
}
template <typename Data>
class AVLTree {
// friend function annotations
friend bool check<Data>(AVLTree<Data>& tree);
public:
// constructor
AVLTree() : __root(nullptr) {}
// Insert the given value and adjust the tree.
// If the value is already exist, then it will do nothing.
void insert(const Data& value) {
// Special case that it is an empty tree.
if (nullptr == __root) {
__root.reset(new AVLNode<Data>(nullptr, value));
return;
}
// Pointer to search the insert position, which is initialized by the root node.
AVLPointer<Data> current = __root;
// Search the insert position, insert the node and adjust the tree.
while (true) {
const Data& currentValue = current->getValue();
// The given value will be in the left child.
if (value < currentValue) {
AVLPointer<Data> lc = current->getLeftChild();
// The given value will be the left child.
if (nullptr == lc) {
// Build the new node and connect the pointer.
AVLPointer<Data> newLc(new AVLNode<Data>(current, value));
current->setLeftChild(newLc);
__adjustAfterInsertion(newLc);
break;
}
// The left child is existed, so we recursively search the left child.
else current = lc;
} // if (value < currentValue)
// The given value will be in the right child.
else if (currentValue < value) {
AVLPointer<Data> rc = current->getRightChild();
// The given value will be the left child.
if (nullptr == rc) {
// Build the new node and connect the pointer.
AVLPointer<Data> newRc(new AVLNode<Data>(current, value));
current->setRightChild(newRc);
__adjustAfterInsertion(newRc);
break;
}
// The right child is existed, so we recursively search the right child.
else current = rc;
} // else if (currentValue < value)
// The given value is already exist.
else return;
} // while (true)
}
// Erase the given value and adjust the tree.
// If the value is not existed, then it will do nothing.
void erase(const Data& value) {
// The positon of the node will be erased.
AVLPointer<Data> pos = search<Data>(value, __root);
if (nullptr == pos) return;
AVLPointer<Data> par = pos->getParent();
// The node will be erased is not the root node.
if (nullptr != par) {
// If the node is the left child of its parent.
bool isLeft = pos->getParent()->getLeftChild() == pos;
// The condition that the node only has the right child (maybe empty tree).
// If the right child is empty, then the node is a leaf node.
if (nullptr == pos->getLeftChild()) {
// Reconnect the right child to the parent.
if (isLeft) {
par->setLeftChild(pos->getRightChild());
if (nullptr != par->getLeftChild())
par->getLeftChild()->setParent(par);
}
else {
par->setRightChild(pos->getRightChild());
if (nullptr != par->getRightChild())
par->getRightChild()->setParent(par);
}
__adjustAfterErase(par);
} // if (nullptr == pos->getLeftChild())
// The condition that the node only has the left child (impossible to be empty tree).
else if (nullptr == pos->getRightChild()) {
// Reconnect the left child to the parent.
if (isLeft) {
par->setLeftChild(pos->getLeftChild());
if (nullptr != par->getLeftChild())
par->getLeftChild()->setParent(par);
}
else {
par->setRightChild(pos->getLeftChild());
if (nullptr != par->getRightChild())
par->getRightChild()->setParent(par);
}
__adjustAfterErase(par);
} // else if (nullptr == pos->getRightChild())
// The condition that the node has the both children (each child is not empty).
else {
// The minimum value(node) in the right subtree.
AVLPointer<Data> minimumRight = pos->getRightChild();
while (nullptr != minimumRight->getLeftChild())
minimumRight = minimumRight->getLeftChild();
// Move the minimumRight to pos and erase the minimumRight.
pos->setValue(minimumRight->getValue());
AVLPointer<Data> mrPar = minimumRight->getParent();
if (mrPar == pos) mrPar->setRightChild(nullptr);
else mrPar->setLeftChild(nullptr);
__adjustAfterErase(mrPar);
} // else
} // if (nullptr != par)
// The node will be erased is the root node.
else {
// The conditions that the root only has zero one child.
if (nullptr == __root->getLeftChild()) {
__root = __root->getRightChild();
if (nullptr != __root) __root->setParent(nullptr);
__adjustAfterErase(__root);
}
else if (nullptr == __root->getRightChild()) {
__root = __root->getLeftChild();
__root->setParent(nullptr);
__adjustAfterErase(__root);
}
// The condition that the root has the both children (each child is not empty).
else {
// The minimum value(node) in the right subtree.
AVLPointer<Data> minimumRight = __root->getRightChild();
while (nullptr != minimumRight->getLeftChild())
minimumRight = minimumRight->getLeftChild();
// Move the minimumRight to pos and erase the minimumRight.
pos->setValue(minimumRight->getValue());
AVLPointer<Data> mrPar = minimumRight->getParent();
if (mrPar == __root) mrPar->setRightChild(nullptr);
else mrPar->setLeftChild(nullptr);
__adjustAfterErase(mrPar);
} // else
} // else
}
std::string serialize() {
return ::serialize(__root);
}
private:
void __adjustAfterInsertion(AVLPointer<Data> insertedNode) {
// condition that the input is invalid
if (nullptr == insertedNode || nullptr == insertedNode->getParent()) return;
// After insertion, the real heights of the nodes between (includes)
// the inserted node's parent and the root node will be modified.
// So we call the "updateHeightsBottomUp" to make these heights correct.
// Excepts, we will get the smallest unbalanced tree and
// the balanced factor caused by the inserted node.
AVLPointer<Data> smallestUnbalacedTree(nullptr);
int balancedFactor = 0;
std::tie(smallestUnbalacedTree, balancedFactor) =
updateHeightsBottomUp<Data>(insertedNode->getParent());
// The condition that the inserted node make the tree unbalanced.
if (nullptr != smallestUnbalacedTree) {
// Adjust the smallest unbalanced tree.
// If the tree to be adjusted is not the whole tree,
// it will reconnect the adjusted tree to the parent of it.
AVLPointer<Data> parent = smallestUnbalacedTree->getParent();
AVLPointer<Data> rotatedTree = rotate<Data>(smallestUnbalacedTree, insertedNode, balancedFactor);
if (nullptr == parent) __root = rotatedTree;
else if (parent->getLeftChild() == smallestUnbalacedTree) parent->setLeftChild(rotatedTree);
else parent->setRightChild(rotatedTree);
// After the adjustment, the real heights of the nodes between (includes)
// the parent node and the root node will be modified.
// So we call the "updateHeightsBottomUp" to make these heights correct.
updateHeightsBottomUp(parent);
}
}
// Another adjustment method of the AVL tree, which is used after the erase operation.
// In this function, we will adjust the subtree from the start node to the root node recursively.
// For the current subtree, we will check the heights of the two children, and do the simple rotation to fix the unbalance.
// Then we will update the height of the parent of the current subtree if current subtree is not the whole tree.
void __adjustAfterErase(AVLPointer<Data> start) {
if (nullptr == start) return;
int lh = getHeight<Data>(start->getLeftChild());
int rh = getHeight<Data>(start->getRightChild());
if (__root == start) {
if (lh - rh > 1) __root = rightRotate<Data>(__root);
else if (rh - lh > 1) __root = leftRotate<Data>(__root);
else {
AVLPointer<Data> parent = start->getParent();
if (parent->getLeftChild() == start) {
if (lh - rh > 1) parent->setLeftChild(rightRotate<Data>(start));
else if (rh - lh > 1) parent->setLeftChild(leftRotate<Data>(start));
}
}
}
else {
if (lh - rh > 1) parent->setRightChild(rightRotate<Data>(start));
else if (rh - lh > 1) parent->setRightChild(leftRotate<Data>(start));
updateHeight(parent);
__adjustAfterErase(parent);
}
}
AVLPointer<Data> __root;
};