基于树的数组(Array Based on Tree,简称ABT),是一种基本数据结构,它是基于平衡二叉树的数组,并根据其左右子树的节点数来维持平衡。不同于二叉搜索树(Binary Search Tree),ABT维持的是元素的插入顺序而非节点的键值顺序。ABT不仅具备随机访问能力,而且具备动态编辑能力,其随机访问、插入和删除操作的时间复杂度均为O(log n) 。
适用场景
数组(Array)和链表(List)是两种常见的数据结构。数组的随机访问时间复杂度为O(1),而链表的插入和删除时间复杂度为O(1)。然而,有时我们需要一种数据结构,能够结合数组和链表的优点。因此,基于树的数组(Array Based on Tree)的概念应运而生。ABT很好地平衡了随机访问、插入和删除操作的性能,适用于同时需要随机访问与动态编辑的应用场景。
特性
ABT是一种基于非排序二叉树的数据结构,具有以下特性:
(1) 左子树节点数不小于其两个侄子所在子树的节点数。
(2) 右子树节点数不小于其两个侄子所在子树的节点数。
(3) 左右子树均为ABT。
考虑以下示例,其中T是ABT的节点,L和R是其子节点,A、B、C和D是满足上述ABT特性的子树。
根据ABT的特性,节点T必须满足:
(1) size(L) >= max(size(C), size(D))
(2) size(R) >= max(size(A), size(B))
节点
ABT的节点包括一个父节点、两个子节点以及该节点所在子树中的节点数量。
节点定义的C++代码如下:
// Class template abt_node
template <class T>
struct abt_node
{
using node_type = abt_node<T>;
using node_pointer = node_type*;
using const_node_pointer = const node_type*;
using node_reference = node_type&;
using const_node_reference = const node_type&;
node_pointer parent;
node_pointer left;
node_pointer right;
size_t size;
T data;
};
旋转操作
如同其他自平衡二叉树,当插入或删除节点导致大小平衡树失去平衡时,需要通过旋转操作来恢复平衡。
常见的旋转操作包括左旋转和右旋转,这可以通过交换节点和子树的位置来实现。下面介绍左右旋转的操作过程。
左旋转
左旋转用于使节点T的右子节点R成为其父节点,并使其右子节点R的左子节点A成为其右子节点。此操作使原节点T成为其右子节点R的左子节点,从而维持二叉树的平衡。
左旋转的C++代码如下:
node_pointer rotate_left(node_pointer t)
{
node_pointer r = t->right;
t->right = r->left;
if (r->left)
r->left->parent = t;
r->parent = t->parent;
if (t == _head->parent)
_head->parent = r;
else if (t == t->parent->left)
t->parent->left = r;
else
t->parent->right = r;
r->left = t;
r->size = t->size;
t->parent = r;
t->size = (t->left ? t->left->size : 0) + (t->right ? t->right->size : 0) + 1;
return r;
}
右旋转
右旋转与左旋转类似,但方向相反。右旋转用于使节点T的左子节点L成为其父节点,并使其左子节点L的右子节点B成为其左子节点。此操作使原节点T成为其左子节点L的右子节点,从而维持二叉树的平衡。
右旋转的C++代码如下:
node_pointer rotate_right(node_pointer t)
{
node_pointer l = t->left;
t->left = l->right;
if (l->right)
l->right->parent = t;
l->parent = t->parent;
if (t == _head->parent)
_head->parent = l;
else if (t == t->parent->right)
t->parent->right = l;
else
t->parent->left = l;
l->right = t;
l->size = t->size;
t->parent = l;
t->size = (t->left ? t->left->size : 0) + (t->right ? t->right->size : 0) + 1;
return l;
}
重新平衡
当对ABT进行插入或者删除操作后,可能违反ABT的特性,需要对以T为根的ABT进行重新平衡。再平衡的前提是T的子树均满足ABT的特性。重新平衡需要考虑以下4种情况:
(1) size(T.left)<size(T.right.left)
可能发生在节点T的右子树插入节点或者左子树删除节点后。先对T的右子节点R进行右旋转,再对T进行左旋转。此时,子树A、B、D、E、F 和 L 仍然满足 ABT的特性;而左子树T和右子树R可能违反ABT的特性。对于左子树T,由于其右子树的节点减少,可能出现size(T.right)<size(T.left.child),需要重新平衡。对于右子树R,由于其左子树的节点减少,可能出现size(R.left)<size(R.right.child),需要重新平衡。最后,对节点C及其祖先节点逐一进行重新平衡,直到根节点为止。
(2) size(T.left) < size(T.right.right)
可能发生在节点T的右子树插入节点或者左子树删除节点后。对节点T进行左旋转后,子树A、B、C、D、E、F、L仍然满足ABT的特性;而左子树T的右子树的节点减少,可能出现size(T.right)<size(T.left.child),需要重新平衡。最后,对节点R及其祖先节点逐一进行重新平衡,直到根节点为止。
(3) size(T.right) < size(T.left.right)
可能发生在节点T的左子树插入节点或者右子树删除节点后。先对T的左子节点进行左旋转,再对T进行右旋转。此时,子树A、C、D、E、F、R仍然满足ABT的性质;而左子树L和右子树T可能违反ABT的特性。对于左子树L,由于其右子树的节点减少,可能出现size(L.right)<size(L.left.child),需要重新平衡。对于右子树T,由于其左子树的节点减少,可能出现size(T.left)<size(T.right.child),需要重新平衡。最后,对节点B及其祖先节点逐一进行重新平衡,直到根节点为止。
(4) size(T.right)<size(T.left.left)
可能发生在节点T的左子树插入节点或者右子树删除节点后。对节点T进行右旋转后,子树A、B、C、D、E、F、R仍然满足SBT的性质;而右子树T的左子树的节点减少,可能出现size(T.left)<size(T.right.child),需要重新平衡。最后,对节点L及其祖先节点逐一进行重新平衡,直到根节点为止。
向节点T的子节点插入节点后进行重新平衡的C++代码如下:
node_pointer insert_rebalance(node_pointer t, bool flag)
{
if (flag)
{
if (t->right)
{
size_type left_size = t->left ? t->left->size : 0;
// case 1: size(T.left) < size(T.right.left)
if (t->right->left && left_size < t->right->left->size)
{
t->right = rotate_right(t->right);
t = rotate_left(t);
t->left = insert_rebalance(t->left, false);
t->right = insert_rebalance(t->right, true);
t = insert_rebalance(t, true);
}
// case 2. size(T.left) < size(T.right.right)
else if (t->right->right && left_size < t->right->right->size)
{
t = rotate_left(t);
t->left = insert_rebalance(t->left, false);
t = insert_rebalance(t, true);
}
}
}
else
{
if (t->left)
{
size_type right_size = t->right ? t->right->size : 0;
// case 3. size(T.right) < size(T.left.right)
if (t->left->right && right_size < t->left->right->size)
{
t->left = rotate_left(t->left);
t = rotate_right(t);
t->left = insert_rebalance(t->left, false);
t->right = insert_rebalance(t->right, true);
t = insert_rebalance(t, false);
}
// case 4. size(T.right) < size(T.left.left)
else if (t->left->left && right_size < t->left->left->size)
{
t = rotate_right(t);
t->right = insert_rebalance(t->right, true);
t = insert_rebalance(t, false);
}
}
}
return t;
}
从节点T的子节点删除节点后进行重新平衡的C++代码如下:
node_pointer erase_rebalance(node_pointer t, bool flag)
{
if (!flag)
{
if (t->right)
{
size_type left_size = t->left ? t->left->size : 0;
// case 1: size(T.left) < size(T.right.left)
if (t->right->left && left_size < t->right->left->size)
{
t->right = rotate_right(t->right);
t = rotate_left(t);
t->left = erase_rebalance(t->left, true);
t->right = erase_rebalance(t->right, false);
t = erase_rebalance(t, false);
}
// case 2. size(T.left) < size(T.right.right)
else if (t->right->right && left_size < t->right->right->size)
{
t = rotate_left(t);
t->left = erase_rebalance(t->left, true);
t = erase_rebalance(t, false);
}
}
}
else
{
if (t->left)
{
size_type right_size = t->right ? t->right->size : 0;
// case 3. size(T.right) < size(T.left.right)
if (t->left->right && right_size < t->left->right->size)
{
t->left = rotate_left(t->left);
t = rotate_right(t);
t->left = erase_rebalance(t->left, true);
t->right = erase_rebalance(t->right, false);
t = erase_rebalance(t, true);
}
// case 4. size(T.right) < size(T.left.left)
else if (t->left->left && right_size < t->left->left->size)
{
t = rotate_right(t);
t->right = erase_rebalance(t->right, false);
t = erase_rebalance(t, true);
}
}
}
return t;
}
插入操作
如果ABT为空,则直接添加该节点作为根节点。否则,根据插入节点T的子节点的情况,可以分为以下两种情况:
(1) 节点T没有左子节点
在这种情况下直接插入到节点T的左子树中。其祖先节点的节点数应全部加1。可能出现上述第(3)种情况或者第(4)种情况,因此,节点T需要重新平衡。
(2) 节点T已有左子节点
在这种情况下选择其左子树中的最后一个节点作为实际插入节点X。插入到节点X的右子树中,其祖先节点的节点数应全部加1。可能会出现上述第(1)种情况或者第(2)种情况,因此,节点X需要重新平衡。
插入操作的C++代码如下:
template <class ...Args>
node_pointer insert_node(node_pointer t, Args&&... args)
{
// creates a new node
node_pointer n = this->create_node(std::forward<Args>(args)...);
n->left = nullptr;
n->right = nullptr;
n->size = 1;
if (t == _head)
{
// if the tree is not empty
if (_head->parent)
{
t = _head->right;
// inserts the node
n->parent = t;
t->right = n;
_head->right = n;
// increases the size of nodes
for (node_pointer p = t; p != _head; p = p->parent)
++p->size;
do
{
// rebalance after insertion
t = insert_rebalance(t->parent, t == t->parent->right);
} while (t->parent != _head);
}
else
{
// inserts the node
n->parent = t;
_head->parent = n;
_head->left = n;
_head->right = n;
}
}
else if (t->left)
{
t = t->left;
while (t->right)
t = t->right;
// inserts the node
n->parent = t;
t->right = n;
// increases the size of nodes
for (node_pointer p = t; p != _head; p = p->parent)
++p->size;
do
{
// rebalance after insertion
t = insert_rebalance(t->parent, t == t->parent->right);
} while (t->parent != _head);
}
else
{
// inserts the node
n->parent = t;
t->left = n;
if (t == _head->left)
_head->left = n;
// increases the size of nodes
for (node_pointer p = t; p != _head; p = p->parent)
++p->size;
do
{
// rebalance after insertion
t = insert_rebalance(t->parent, t == t->parent->right);
} while (t->parent != _head);
}
return n;
}
删除操作
假设要删除的节点是T,根据节点T的子节点数量,可以分为以下两种情况:
(1) 节点T最多有一个子节点
在这种情况下,可以直接删除节点T。其祖先节点的节点数减少1。如果节点T具有子节点L或R,则用其子节点替换节点T。最后,重新平衡节点T的父节点。
(2) 节点T有两个子节点
在这种情况下,不能直接删除节点T,否则整个树将被破坏。当节点T的左子树L的节点数小于右子树R的节点数时,选择其右子树R中值最小的节点作为实际删除节点X;否则,选择其左子树L中值最大的节点作为实际删除节点X。然后交换节点T和节点X的位置,此时,与第(1)种情况完全相同。
删除操作的C++代码如下:
void erase_node(node_pointer t)
{
bool flag;
node_pointer x;
node_pointer parent;
// if node t has two child nodes
if (t->left && t->right)
{
if (t->left->size < t->right->size)
{
x = leftmost(t->right);
// the rebalance flag
flag = (x == x->parent->right);
// reduces the size of nodes
for (node_pointer p = x->parent; p != _head; p = p->parent)
--p->size;
// replaces node t with node x and removes node t
t->left->parent = x;
x->left = t->left;
if (x != t->right)
{
x->parent->left = x->right;
if (x->right)
x->right->parent = x->parent;
t->right->parent = x;
x->right = t->right;
parent = x->parent;
}
else
parent = x;
if (t == _head->parent)
_head->parent = x;
else if (t == t->parent->left)
t->parent->left = x;
else
t->parent->right = x;
x->parent = t->parent;
x->size = t->size;
}
else
{
x = rightmost(t->left);
// the rebalance flag
flag = (x == x->parent->right);
// reduces the size of nodes
for (node_pointer p = x->parent; p != _head; p = p->parent)
--p->size;
// replaces node t with node x and removes node t
t->right->parent = x;
x->right = t->right;
if (x != t->left)
{
x->parent->right = x->left;
if (x->left)
x->left->parent = x->parent;
t->left->parent = x;
x->left = t->left;
parent = x->parent;
}
else
parent = x;
if (t == _head->parent)
_head->parent = x;
else if (t == t->parent->left)
t->parent->left = x;
else
t->parent->right = x;
x->parent = t->parent;
x->size = t->size;
}
// rebalance after deletion
node_pointer p = erase_rebalance(parent, flag);
while (p != _head)
p = erase_rebalance(p->parent, p == p->parent->right);
}
// if node t has one child node at most
else
{
x = t->left ? t->left : t->right;
// the rebalance flag
flag = (t == t->parent->right);
// removes node t
if (x)
x->parent = t->parent;
if (t == _head->parent)
_head->parent = x;
else if (t == t->parent->left)
t->parent->left = x;
else
t->parent->right = x;
if (t == _head->left)
_head->left = x ? leftmost(x) : t->parent;
if (t == _head->right)
_head->right = x ? rightmost(x) : t->parent;
// reduces the size of nodes
for (node_pointer p = t->parent; p != _head; p = p->parent)
--p->size;
if (t != _head)
{
// rebalance after deletion
node_pointer p = erase_rebalance(t->parent, flag);
while (p != _head)
p = erase_rebalance(p->parent, p == p->parent->right);
}
}
// destroy node
this->destroy_node(t);
}
选择操作
选择操作为ABT提供随机访问功能。
选择操作的C++代码如下:
node_pointer select_node(size_type k)
{
node_pointer t = header->parent;
while (t)
{
size_type left_size = t->left ? t->left->size : 0;
if (left_size < k)
{
t = t->right;
k -= (left_size + 1);
}
else if (k < left_size)
t = t->left;
else
return t;
}
return header;
}
代码下载
ab_tree 源代码-优快云博客https://blog.youkuaiyun.com/CQRuler/article/details/144518592