Search⌘ K
AI Features

Solution: Scapegoat Trees

Explore how to modify the add method in Scapegoat Trees to prevent recomputation of subtree sizes, improving insertion efficiency. Understand the depth threshold triggering rebalancing, how to find the scapegoat node without extra overhead, and the process to rebuild unbalanced subtrees to maintain tree balance.

We'll cover the following...

Task

Here is the task that modifies the add(x) method of the ScapegoatTree so that it does not waste any time recomputing the sizes of subtrees that have already been computed. This is possible because, by the time the method wants to compute size(w), it has already computed one of size(w.left) or size(w.right).

Solution

We have implemented the code that modifies the add() method of the ScapegoatTree. The add() method inserts a new element x into the ScapegoatTree and rebalances the tree if the depth exceeds a certain threshold.

C++
#ifndef SCAPEGOATTREE_H_
#define SCAPEGOATTREE_H_
#include <cmath>
#include "BinarySearchTree.h"
namespace ods {
template <class Node, class T>
class ScapegoatTree : public BinarySearchTree<Node, T> {
public:
using BinaryTree<Node>::r;
protected:
using BinaryTree<Node>::nil;
using BinarySearchTree<Node, T>::n;
int q;
static int log32(int q);
int addWithDepth(Node* u);
void rebuild(Node* u);
int packIntoArray(Node* u, Node** a, int i);
Node* buildBalanced(Node** a, int i, int ns);
public:
ScapegoatTree();
virtual ~ScapegoatTree();
virtual bool add(T x);
virtual bool remove(T x);
};
template <class T>
class ScapegoatTree1 : public ScapegoatTree<BSTNode1<T>, T> {};
template <class Node, class T>
inline int ScapegoatTree<Node, T>::log32(int q) {
static double log23 = 2.4663034623764317;
return (int)ceil(log23 * log(q));
}
template <class Node, class T>
inline int ScapegoatTree<Node, T>::addWithDepth(Node* u) {
Node* w = r;
if (w == nil) {
r = u;
n++;
q++;
return 0;
}
bool done = false;
int d = 0;
do {
int res = compare(u->x, w->x);
if (res < 0) {
if (w->left == nil) {
w->left = u;
u->parent = w;
done = true;
}
else {
w = w->left;
}
}
else if (res > 0) {
if (w->right == nil) {
w->right = u;
u->parent = w;
done = true;
}
w = w->right;
}
else {
return -1;
}
d++;
} while (!done);
n++;
q++;
return d;
}
template <class Node, class T>
inline void ScapegoatTree<Node, T>::rebuild(Node* u) {
int ns = BinaryTree<Node>::size(u);
Node* p = u->parent;
Node** a = new Node*[ns];
packIntoArray(u, a, 0);
if (p == nil) {
r = buildBalanced(a, 0, ns);
r->parent = nil;
}
else if (p->right == u) {
p->right = buildBalanced(a, 0, ns);
p->right->parent = p;
}
else {
p->left = buildBalanced(a, 0, ns);
p->left->parent = p;
}
delete[] a;
}
template <class Node, class T>
inline int ScapegoatTree<Node, T>::packIntoArray(Node* u, Node** a, int i) {
if (u == nil) {
return i;
}
i = packIntoArray(u->left, a, i);
a[i++] = u;
return packIntoArray(u->right, a, i);
}
template <class Node, class T>
inline Node* ScapegoatTree<Node, T>::buildBalanced(Node** a, int i, int ns) {
if (ns == 0) {
return nil;
}
int m = ns / 2;
a[i + m]->left = buildBalanced(a, i, m);
if (a[i + m]->left != nil)
a[i + m]->left->parent = a[i + m];
a[i + m]->right = buildBalanced(a, i + m + 1, ns - m - 1);
if (a[i + m]->right != nil)
a[i + m]->right->parent = a[i + m];
return a[i + m];
}
template <class Node, class T>
inline ScapegoatTree<Node, T>::ScapegoatTree() : BinarySearchTree<Node, T>(), q(0) {
// Constructor implementation
}
template <class Node, class T>
inline ScapegoatTree<Node, T>::~ScapegoatTree() {
// Destructor implementation
}
template <class Node, class T>
inline bool ScapegoatTree<Node, T>::add(T x) {
Node* u = new Node;
u->x = x;
u->left = u->right = u->parent = nil;
int d = addWithDepth(u);
if (d > log32(q)) {
Node* w = u->parent;
int a, b;
if (w->left == u) {
a = d - 1; // Size of the left subtree of w has already been computed as d - 1
b = BinaryTree<Node>::size(w) - a - 1; // Compute the size of the right subtree
} else {
b = d - 1; // Size of the right subtree of w has already been computed as d - 1
a = BinaryTree<Node>::size(w) - b - 1; // Compute the size of the left subtree
}
while (3 * a <= 2 * b) {
w = w->parent;
a = BinaryTree<Node>::size(w->left);
b = BinaryTree<Node>::size(w->right);
}
rebuild(w->parent);
}
return d >= 0;
}
template <class Node, class T>
inline bool ScapegoatTree<Node, T>::remove(T x) {
if (BinarySearchTree<Node, T>::remove(x)) {
if (2 * n < q) {
rebuild(r);
q = n;
}
return true;
}
return false;
}
} /* namespace ods */
#endif /* SCAPEGOATTREE_H_ */

Explanation

Let’s focus on the detailed explanation for the add(T x) ...