AVL Tree refactor
This commit is contained in:
parent
be6b821a40
commit
aafecb8359
8 changed files with 511 additions and 376 deletions
|
|
@ -5,6 +5,7 @@ file(GLOB SOURCES "./private/*.cpp")
|
||||||
file(GLOB HEADERS "./public/*.hpp")
|
file(GLOB HEADERS "./public/*.hpp")
|
||||||
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ./private/)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
|
||||||
|
|
||||||
### -------------------------- Tests -------------------------- ###
|
### -------------------------- Tests -------------------------- ###
|
||||||
|
|
|
||||||
407
Containers/private/AVLTree.ipp
Normal file
407
Containers/private/AVLTree.ipp
Normal file
|
|
@ -0,0 +1,407 @@
|
||||||
|
// #pragma once
|
||||||
|
|
||||||
|
// #include "Tree.hpp"
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
tp::AvlTree<Key, Data, Allocator>::~AvlTree() {
|
||||||
|
removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::deleteNode(Node* node) {
|
||||||
|
node->~Node();
|
||||||
|
mAlloc.deallocate(node);
|
||||||
|
mSize--;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::newNode(KeyArg key, DataArg data) -> Node* {
|
||||||
|
mSize++;
|
||||||
|
auto node = new (mAlloc.allocate(sizeof(Node))) Node(key, data);
|
||||||
|
node->mLeft = &gNullNode;
|
||||||
|
node->mRight = &gNullNode;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
[[nodiscard]] tp::ualni tp::AvlTree<Key, Data, Allocator>::size() const {
|
||||||
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::head() const -> Node* {
|
||||||
|
return this->mRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::insert(KeyArg key, DataArg data) {
|
||||||
|
if (!mRoot) {
|
||||||
|
mRoot = newNode(key, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto parent = findInsertParent(mRoot, key)) {
|
||||||
|
restoreInvariants(insertNode(parent, key, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::remove(KeyArg key) {
|
||||||
|
|
||||||
|
auto node = findSubTree(mRoot, key);
|
||||||
|
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
if (node->mRight != &gNullNode && node->mLeft != &gNullNode) {
|
||||||
|
Node* min = minNode(node->mRight);
|
||||||
|
injectNodeInstead(node, min);
|
||||||
|
std::swap(node, min);
|
||||||
|
// auto const& newKey = min->key.getFindKey(head->mRight);
|
||||||
|
node = findSubTree(node->mRight, key);
|
||||||
|
DEBUG_ASSERT(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->mRight != &gNullNode) {
|
||||||
|
if (node->mParent) {
|
||||||
|
if (node->mParent->mLeft == node) node->mParent->mLeft = node->mRight;
|
||||||
|
else node->mParent->mRight = node->mRight;
|
||||||
|
}
|
||||||
|
node->mRight->mParent = node->mParent;
|
||||||
|
auto delNode = node;
|
||||||
|
node = node->mRight;
|
||||||
|
deleteNode(delNode);
|
||||||
|
|
||||||
|
} else if (node->mLeft != &gNullNode) {
|
||||||
|
if (node->mParent) {
|
||||||
|
if (node->mParent->mLeft == node) node->mParent->mLeft = node->mLeft;
|
||||||
|
else node->mParent->mRight = node->mLeft;
|
||||||
|
}
|
||||||
|
node->mLeft->mParent = node->mParent;
|
||||||
|
auto delNode = node;
|
||||||
|
node = node->mLeft;
|
||||||
|
deleteNode(delNode);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (node->mParent) {
|
||||||
|
if (node->mParent->mLeft == node) node->mParent->mLeft = &gNullNode;
|
||||||
|
else node->mParent->mRight = &gNullNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delNode = node;
|
||||||
|
node = node->mParent;
|
||||||
|
deleteNode(delNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreInvariants(node);
|
||||||
|
|
||||||
|
if (mRoot) mRoot->mParent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::maxNode(Node* head) const -> Node* {
|
||||||
|
if (!head) return nullptr;
|
||||||
|
while (head->mRight != &gNullNode) {
|
||||||
|
head = head->mRight;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::minNode(Node* head) const -> Node* {
|
||||||
|
if (!head) return nullptr;
|
||||||
|
while (head->mLeft != &gNullNode) {
|
||||||
|
head = head->mLeft;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::find(KeyArg key) const -> Node* {
|
||||||
|
return findSubTree(mRoot, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::findSubTree(Node* iter, KeyArg key) const -> Node* {
|
||||||
|
while (true) {
|
||||||
|
if (iter == &gNullNode) return nullptr;
|
||||||
|
if (iter->key.exactNode(key)) return iter;
|
||||||
|
if (iter->key.descentRight(key)) {
|
||||||
|
// key = iter->key.keyInRightSubtree(key);
|
||||||
|
iter = iter->mRight;
|
||||||
|
} else {
|
||||||
|
// key = iter->key.keyInLeftSubtree(key);
|
||||||
|
iter = iter->mLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::findLessOrEq(KeyArg key) const -> Node* {
|
||||||
|
Node* iter = mRoot;
|
||||||
|
while (true) {
|
||||||
|
if (iter == &gNullNode) return nullptr;
|
||||||
|
if (iter->key.exactNode(key)) return iter;
|
||||||
|
if (iter->key.descentRight(key)) {
|
||||||
|
if (iter->mRight) {
|
||||||
|
// key = iter->key.keyInRightSubtree(key);
|
||||||
|
iter = iter->mRight;
|
||||||
|
} else {
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (iter->mLeft) {
|
||||||
|
// key = iter->key.keyInLeftSubtree(key);
|
||||||
|
iter = iter->mLeft;
|
||||||
|
} else {
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
bool tp::AvlTree<Key, Data, Allocator>::isValid() {
|
||||||
|
return findInvalidNode(head()) == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::removeAll() {
|
||||||
|
if (!mRoot) return;
|
||||||
|
removeAllUtil(mRoot);
|
||||||
|
mRoot = nullptr;
|
||||||
|
mSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::removeAllUtil(Node* node) {
|
||||||
|
if (node->mLeft != &gNullNode) removeAllUtil(node->mLeft);
|
||||||
|
if (node->mRight != &gNullNode) removeAllUtil(node->mRight);
|
||||||
|
deleteNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::findInvalidNode(const Node* head) const -> const Node* {
|
||||||
|
// checks invariants of AVL tree
|
||||||
|
// returns first invalid node
|
||||||
|
if (!head || head == &gNullNode) return nullptr;
|
||||||
|
|
||||||
|
if (head->mLeft != &gNullNode) {
|
||||||
|
// TODO: incomplete test
|
||||||
|
if (head->key.descentRight(head->mLeft->key.getFindKey(head))) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
if (head->mLeft->mParent != head) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (head->mRight != &gNullNode) {
|
||||||
|
if (!head->key.descentRight(head->mRight->key.getFindKey(head))) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
if (head->mRight->mParent != head) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (head->mLeft != &gNullNode && head->mRight != &gNullNode) {
|
||||||
|
if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int balance = head->mRight->mHeight - head->mLeft->mHeight;
|
||||||
|
|
||||||
|
if (balance > 1 || balance < -1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Node* ret = findInvalidNode(head->mRight);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return findInvalidNode(head->mLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::rotateLeft(Node* pivot) -> Node* {
|
||||||
|
// returns new head
|
||||||
|
DEBUG_ASSERT(pivot);
|
||||||
|
|
||||||
|
Node* const head = pivot;
|
||||||
|
Node* const right = pivot->mRight;
|
||||||
|
Node* const right_left = right->mLeft;
|
||||||
|
Node* const parent = pivot->mParent;
|
||||||
|
|
||||||
|
// parents
|
||||||
|
if (right_left != &gNullNode) right_left->mParent = head;
|
||||||
|
head->mParent = right;
|
||||||
|
right->mParent = parent;
|
||||||
|
|
||||||
|
// children
|
||||||
|
head->mRight = right_left;
|
||||||
|
right->mLeft = head;
|
||||||
|
|
||||||
|
// heights
|
||||||
|
head->mHeight = 1 + max(head->mLeft->mHeight, head->mRight->mHeight);
|
||||||
|
right->mHeight = 1 + max(right->mLeft->mHeight, right->mRight->mHeight);
|
||||||
|
|
||||||
|
// cache
|
||||||
|
head->key.updateNodeCache(head);
|
||||||
|
right->key.updateNodeCache(right);
|
||||||
|
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::rotateRight(Node* pivot) -> Node* {
|
||||||
|
// returns new head
|
||||||
|
DEBUG_ASSERT(pivot);
|
||||||
|
|
||||||
|
Node* const head = pivot;
|
||||||
|
Node* const left = pivot->mLeft;
|
||||||
|
Node* const left_right = left->mRight;
|
||||||
|
Node* const parent = pivot->mParent;
|
||||||
|
|
||||||
|
// parents
|
||||||
|
if (left_right != &gNullNode) left_right->mParent = head;
|
||||||
|
head->mParent = left;
|
||||||
|
left->mParent = parent;
|
||||||
|
|
||||||
|
// children
|
||||||
|
head->mLeft = left_right;
|
||||||
|
left->mRight = head;
|
||||||
|
|
||||||
|
// heights
|
||||||
|
head->mHeight = 1 + max(head->mLeft->mHeight, head->mRight->mHeight);
|
||||||
|
left->mHeight = 1 + max(left->mLeft->mHeight, left->mRight->mHeight);
|
||||||
|
|
||||||
|
// cache
|
||||||
|
head->key.updateNodeCache(head);
|
||||||
|
left->key.updateNodeCache(left);
|
||||||
|
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::restoreInvariants(Node* head) {
|
||||||
|
if (!head) mRoot = nullptr;
|
||||||
|
|
||||||
|
while (head) {
|
||||||
|
Node* headParent = head->mParent;
|
||||||
|
Node** parentHeadLink = nullptr;
|
||||||
|
|
||||||
|
if (headParent) parentHeadLink = headParent->mLeft == head ? &headParent->mLeft : &headParent->mRight;
|
||||||
|
|
||||||
|
head->mHeight = 1 + max(head->mRight->mHeight, head->mLeft->mHeight);
|
||||||
|
const alni balance = head->mRight->mHeight - head->mLeft->mHeight;
|
||||||
|
|
||||||
|
if (balance < -1) {
|
||||||
|
if (head->mLeft->mLeft->mHeight >= head->mLeft->mRight->mHeight) {
|
||||||
|
head = rotateRight(head);
|
||||||
|
} else {
|
||||||
|
head->mLeft = rotateLeft(head->mLeft);
|
||||||
|
head = rotateRight(head);
|
||||||
|
}
|
||||||
|
} else if (balance > 1) {
|
||||||
|
if (head->mRight->mRight->mHeight >= head->mRight->mLeft->mHeight) {
|
||||||
|
head = rotateLeft(head);
|
||||||
|
} else {
|
||||||
|
head->mRight = rotateRight(head->mRight);
|
||||||
|
head = rotateLeft(head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
head->key.updateNodeCache(head);
|
||||||
|
if (headParent) *parentHeadLink = head;
|
||||||
|
|
||||||
|
mRoot = head;
|
||||||
|
head = headParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mRoot) mRoot->mParent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::insertNode(Node* head, KeyArg key, DataArg data) -> Node* {
|
||||||
|
Node* insertedNode = newNode(key, data);
|
||||||
|
|
||||||
|
if (head->key.descentRight(key)) {
|
||||||
|
head->mRight = insertedNode;
|
||||||
|
} else {
|
||||||
|
head->mLeft = insertedNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertedNode->mParent = head;
|
||||||
|
return insertedNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
auto tp::AvlTree<Key, Data, Allocator>::findInsertParent(Node* head, KeyArg key) -> Node* {
|
||||||
|
while (true) {
|
||||||
|
if (head->key.exactNode(key)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (head->key.descentRight(key)) {
|
||||||
|
if (head->mRight == &gNullNode) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
head = head->mRight;
|
||||||
|
} else {
|
||||||
|
if (head->mLeft == &gNullNode) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
head = head->mLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
void tp::AvlTree<Key, Data, Allocator>::injectNodeInstead(Node* target, Node* from) {
|
||||||
|
// swaps pointers of two nodes to preserve data location in the memory instead of swapping data directly
|
||||||
|
|
||||||
|
// 'target' always has two nodes
|
||||||
|
// 'from' has only one child on the right on no child at all
|
||||||
|
// 'from' can be right child of the 'target'
|
||||||
|
// 'target' can or can not have parent
|
||||||
|
// 'target' can be left or right child of parent
|
||||||
|
|
||||||
|
// from is not a child of target
|
||||||
|
Node* targetParent = target->mParent;
|
||||||
|
Node* targetLeft = target->mLeft;
|
||||||
|
Node* targetRight = target->mRight;
|
||||||
|
|
||||||
|
bool special = target->mRight == from;
|
||||||
|
|
||||||
|
// update parent
|
||||||
|
if (targetParent) {
|
||||||
|
if (targetParent->mRight == target) targetParent->mRight = from;
|
||||||
|
else targetParent->mLeft = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone 'from' to 'target'
|
||||||
|
target->mParent = special ? from : from->mParent;
|
||||||
|
target->mLeft = &gNullNode;
|
||||||
|
target->mRight = from->mRight;
|
||||||
|
if (!special) target->mParent->mLeft = target;
|
||||||
|
|
||||||
|
// if (target->mLeft) target->mLeft->mParent = target;
|
||||||
|
if (target->mRight) target->mRight->mParent = target;
|
||||||
|
|
||||||
|
// clone 'target' to 'from'
|
||||||
|
from->mParent = targetParent;
|
||||||
|
from->mLeft = targetLeft;
|
||||||
|
from->mRight = special ? target : targetRight;
|
||||||
|
from->mLeft->mParent = from;
|
||||||
|
from->mRight->mParent = from;
|
||||||
|
|
||||||
|
std::swap(from->mHeight, target->mHeight);
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
void* DefaultAllocator::allocate(ualni size) { return malloc(size); }
|
void* DefaultAllocator::allocate(ualni size) { return malloc(size); }
|
||||||
|
|
||||||
void DefaultAllocator::deallocate(void* p) { free(p); }
|
void DefaultAllocator::deallocate(void* p) { free(p); }
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +60,7 @@ namespace tp {
|
||||||
private:
|
private:
|
||||||
template <typename tFunctor>
|
template <typename tFunctor>
|
||||||
void forEachIntersectionUtil(const Node* node, tType start, tType end, tFunctor functor, ualni& debug) const {
|
void forEachIntersectionUtil(const Node* node, tType start, tType end, tFunctor functor, ualni& debug) const {
|
||||||
if (node == nullptr) return;
|
if (node->mHeight == -1) return; // sentinel node (null node)
|
||||||
|
|
||||||
debug++;
|
debug++;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,145 +38,42 @@ namespace tp {
|
||||||
class Node {
|
class Node {
|
||||||
friend AvlTree;
|
friend AvlTree;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Node() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Node(KeyArg aKey, DataArg aData) :
|
Node(KeyArg aKey, DataArg aData) :
|
||||||
key(aKey),
|
key(aKey),
|
||||||
data(aData) {}
|
data(aData) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Data data;
|
alni mHeight = -1;
|
||||||
Key key;
|
|
||||||
|
|
||||||
Node* mLeft = nullptr;
|
Node* mLeft = nullptr;
|
||||||
Node* mRight = nullptr;
|
Node* mRight = nullptr;
|
||||||
Node* mParent = nullptr;
|
Node* mParent = nullptr;
|
||||||
ualni mHeight = 0;
|
|
||||||
|
Data data;
|
||||||
|
Key key;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvlTree() = default;
|
AvlTree() = default;
|
||||||
~AvlTree() { removeAll(); }
|
~AvlTree();
|
||||||
|
|
||||||
[[nodiscard]] ualni size() const { return mSize; }
|
ualni size() const;
|
||||||
|
Node* head() const;
|
||||||
|
|
||||||
Node* head() const { return this->mRoot; }
|
void insert(KeyArg key, DataArg data);
|
||||||
|
void remove(KeyArg key);
|
||||||
|
void removeAll();
|
||||||
|
|
||||||
void insert(KeyArg key, DataArg data) {
|
Node* maxNode(Node* head) const;
|
||||||
mRoot = insertUtil(mRoot, key, data);
|
Node* minNode(Node* head) const;
|
||||||
mRoot->mParent = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(KeyArg key) {
|
Node* find(KeyArg key) const;
|
||||||
mRoot = removeUtil(mRoot, key);
|
Node* findSubTree(Node* iter, KeyArg key) const;
|
||||||
if (mRoot) mRoot->mParent = nullptr;
|
Node* findLessOrEq(KeyArg key) const;
|
||||||
}
|
|
||||||
|
|
||||||
Node* maxNode(Node* head) const {
|
|
||||||
if (!head) return nullptr;
|
|
||||||
while (head->mRight != nullptr) {
|
|
||||||
head = head->mRight;
|
|
||||||
}
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* minNode(Node* head) const {
|
|
||||||
if (!head) return nullptr;
|
|
||||||
while (head->mLeft != nullptr) {
|
|
||||||
head = head->mLeft;
|
|
||||||
}
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* find(KeyArg key) const {
|
|
||||||
Node* iter = mRoot;
|
|
||||||
while (true) {
|
|
||||||
if (!iter) return nullptr;
|
|
||||||
if (iter->key.exactNode(key)) return iter;
|
|
||||||
if (iter->key.descentRight(key)) {
|
|
||||||
key = iter->key.keyInRightSubtree(key);
|
|
||||||
iter = iter->mRight;
|
|
||||||
} else {
|
|
||||||
key = iter->key.keyInLeftSubtree(key);
|
|
||||||
iter = iter->mLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* findLessOrEq(KeyArg key) const {
|
|
||||||
Node* iter = mRoot;
|
|
||||||
while (true) {
|
|
||||||
if (!iter) return nullptr;
|
|
||||||
if (iter->key.exactNode(key)) return iter;
|
|
||||||
if (iter->key.descentRight(key)) {
|
|
||||||
if (iter->mRight) {
|
|
||||||
key = iter->key.keyInRightSubtree(key);
|
|
||||||
iter = iter->mRight;
|
|
||||||
} else {
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (iter->mLeft) {
|
|
||||||
key = iter->key.keyInLeftSubtree(key);
|
|
||||||
iter = iter->mLeft;
|
|
||||||
} else {
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks invariants of AVL tree
|
|
||||||
// returns first invalid node
|
|
||||||
const Node* findInvalidNode(const Node* head) const {
|
|
||||||
if (head == nullptr) return nullptr;
|
|
||||||
|
|
||||||
if (head->mLeft) {
|
|
||||||
// TODO: incomplete test
|
|
||||||
if (head->key.descentRight(head->mLeft->key.getFindKey(head))) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
if (head->mLeft->mParent != head) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head->mRight) {
|
|
||||||
if (!head->key.descentRight(head->mRight->key.getFindKey(head))) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
if (head->mRight->mParent != head) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head->mLeft && head->mRight) {
|
|
||||||
if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
|
|
||||||
|
|
||||||
if (balance > 1 || balance < -1) {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Node* ret = findInvalidNode(head->mRight);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return findInvalidNode(head->mLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValid() { return findInvalidNode(head()) == nullptr; }
|
|
||||||
|
|
||||||
template <typename tFunctor>
|
template <typename tFunctor>
|
||||||
void traverse(Node* node, bool after, tFunctor functor) {
|
void traverse(Node* node, bool after, tFunctor functor) {
|
||||||
|
|
@ -186,259 +83,35 @@ namespace tp {
|
||||||
if (after) functor(node);
|
if (after) functor(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeAll() {
|
auto findInvalidNode(const Node* head) const -> const Node*;
|
||||||
if (!mRoot) return;
|
bool isValid();
|
||||||
removeUtil(mRoot);
|
|
||||||
mRoot = nullptr;
|
|
||||||
mSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeUtil(Node* node) {
|
|
||||||
if (node->mLeft) removeUtil(node->mLeft);
|
|
||||||
if (node->mRight) removeUtil(node->mRight);
|
|
||||||
deleteNode(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <class tArchiver>
|
|
||||||
void archiveWrite(tArchiver& file) const {
|
|
||||||
FAIL("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class tArchiver>
|
|
||||||
void archiveRead(tArchiver&) {
|
|
||||||
FAIL("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void deleteNode(Node* node) {
|
inline void deleteNode(Node* node);
|
||||||
node->~Node();
|
inline auto newNode(KeyArg key, DataArg data) -> Node*;
|
||||||
mAlloc.deallocate(node);
|
|
||||||
mSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); }
|
inline auto rotateLeft(Node* pivot) -> Node*;
|
||||||
|
inline auto rotateRight(Node* pivot) -> Node*;
|
||||||
|
|
||||||
// swaps pointers of two nodes to preserve data location in the memory instead of swapping data directly
|
inline void restoreInvariants(Node* head);
|
||||||
inline void injectNodeInstead(Node* target, Node* from) {
|
|
||||||
// 'target' always has two nodes
|
|
||||||
// 'from' has only one child on the right on no child at all
|
|
||||||
// 'from' can be right child of the 'target'
|
|
||||||
// 'target' can or can not have parent
|
|
||||||
// 'target' can be left or right child of parent
|
|
||||||
|
|
||||||
// from is not a child of target
|
inline auto insertNode(Node* head, KeyArg key, DataArg data) -> Node*;
|
||||||
Node* targetParent = target->mParent;
|
inline auto findInsertParent(Node* head, KeyArg key) -> Node*;
|
||||||
Node* targetLeft = target->mLeft;
|
|
||||||
Node* targetRight = target->mRight;
|
|
||||||
|
|
||||||
bool special = target->mRight == from;
|
inline void injectNodeInstead(Node* target, Node* from);
|
||||||
|
|
||||||
// update parent
|
void removeAllUtil(Node* node);
|
||||||
if (targetParent) {
|
|
||||||
if (targetParent->mRight == target) targetParent->mRight = from;
|
|
||||||
else targetParent->mLeft = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone 'from' to 'target'
|
|
||||||
target->mParent = special ? from : from->mParent;
|
|
||||||
target->mLeft = nullptr;
|
|
||||||
target->mRight = from->mRight;
|
|
||||||
if (!special) target->mParent->mLeft = target;
|
|
||||||
|
|
||||||
// if (target->mLeft) target->mLeft->mParent = target;
|
|
||||||
if (target->mRight) target->mRight->mParent = target;
|
|
||||||
|
|
||||||
// clone 'target' to 'from'
|
|
||||||
from->mParent = targetParent;
|
|
||||||
from->mLeft = targetLeft;
|
|
||||||
from->mRight = special ? target : targetRight;
|
|
||||||
from->mLeft->mParent = from;
|
|
||||||
from->mRight->mParent = from;
|
|
||||||
|
|
||||||
std::swap(from->mHeight, target->mHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; }
|
|
||||||
|
|
||||||
// returns new head
|
|
||||||
inline Node* rotateLeft(Node* pivot) {
|
|
||||||
DEBUG_ASSERT(pivot);
|
|
||||||
|
|
||||||
Node* const head = pivot;
|
|
||||||
Node* const right = pivot->mRight;
|
|
||||||
Node* const right_left = right->mLeft;
|
|
||||||
Node* const parent = pivot->mParent;
|
|
||||||
|
|
||||||
// parents
|
|
||||||
if (right_left) right_left->mParent = head;
|
|
||||||
head->mParent = right;
|
|
||||||
right->mParent = parent;
|
|
||||||
|
|
||||||
// children
|
|
||||||
head->mRight = right_left;
|
|
||||||
right->mLeft = head;
|
|
||||||
|
|
||||||
// heights
|
|
||||||
head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
|
|
||||||
right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight));
|
|
||||||
|
|
||||||
// cache
|
|
||||||
head->key.updateNodeCache(head);
|
|
||||||
right->key.updateNodeCache(right);
|
|
||||||
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Node* rotateRight(Node* pivot) {
|
|
||||||
DEBUG_ASSERT(pivot);
|
|
||||||
|
|
||||||
Node* const head = pivot;
|
|
||||||
Node* const left = pivot->mLeft;
|
|
||||||
Node* const left_right = left->mRight;
|
|
||||||
Node* const parent = pivot->mParent;
|
|
||||||
|
|
||||||
// parents
|
|
||||||
if (left_right) left_right->mParent = head;
|
|
||||||
head->mParent = left;
|
|
||||||
left->mParent = parent;
|
|
||||||
|
|
||||||
// children
|
|
||||||
head->mLeft = left_right;
|
|
||||||
left->mRight = head;
|
|
||||||
|
|
||||||
// heights
|
|
||||||
head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
|
|
||||||
left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight));
|
|
||||||
|
|
||||||
// cache
|
|
||||||
head->key.updateNodeCache(head);
|
|
||||||
left->key.updateNodeCache(left);
|
|
||||||
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursively returns valid isLeft or isRight child or root
|
|
||||||
Node* insertUtil(Node* head, KeyArg key, DataArg data) {
|
|
||||||
|
|
||||||
Node* insertedNode;
|
|
||||||
|
|
||||||
if (head == nullptr) {
|
|
||||||
mSize++;
|
|
||||||
Node* out = newNode(key, data);
|
|
||||||
out->key.updateNodeCache(out);
|
|
||||||
return out;
|
|
||||||
} else if (head->key.exactNode(key)) {
|
|
||||||
return head;
|
|
||||||
} else if (head->key.descentRight(key)) {
|
|
||||||
insertedNode = insertUtil(head->mRight, head->key.keyInRightSubtree(key), data);
|
|
||||||
head->mRight = insertedNode;
|
|
||||||
insertedNode->mParent = head;
|
|
||||||
} else {
|
|
||||||
insertedNode = insertUtil(head->mLeft, head->key.keyInLeftSubtree(key), data);
|
|
||||||
head->mLeft = insertedNode;
|
|
||||||
insertedNode->mParent = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update height
|
|
||||||
head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
|
|
||||||
|
|
||||||
alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft));
|
|
||||||
|
|
||||||
if (balance > 1) {
|
|
||||||
if (head->mRight->key.descentRight(head->key.keyInRightSubtree(key))) {
|
|
||||||
return rotateLeft(head);
|
|
||||||
} else {
|
|
||||||
head->mRight = rotateRight(head->mRight);
|
|
||||||
return rotateLeft(head);
|
|
||||||
}
|
|
||||||
} else if (balance < -1) {
|
|
||||||
if (!head->mLeft->key.descentRight(head->key.keyInLeftSubtree(key))) {
|
|
||||||
return rotateRight(head);
|
|
||||||
} else {
|
|
||||||
head->mLeft = rotateLeft(head->mLeft);
|
|
||||||
return rotateRight(head);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
head->key.updateNodeCache(head);
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* removeUtil(Node* head, KeyArg key) {
|
|
||||||
if (head == nullptr) return head;
|
|
||||||
|
|
||||||
if (head->key.exactNode(key)) {
|
|
||||||
if (head->mRight && head->mLeft) {
|
|
||||||
Node* min = minNode(head->mRight);
|
|
||||||
injectNodeInstead(head, min);
|
|
||||||
std::swap(head, min);
|
|
||||||
// auto const& newKey = min->key.getFindKey(head->mRight);
|
|
||||||
head->mRight = removeUtil(head->mRight, key);
|
|
||||||
} else if (head->mRight) {
|
|
||||||
|
|
||||||
if (head->mParent) {
|
|
||||||
if (head->mParent->mLeft == head) head->mParent->mLeft = head->mRight;
|
|
||||||
else head->mParent->mRight = head->mRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
head->mRight->mParent = head->mParent;
|
|
||||||
|
|
||||||
auto delNode = head;
|
|
||||||
head = head->mRight;
|
|
||||||
deleteNode(delNode);
|
|
||||||
} else if (head->mLeft) {
|
|
||||||
|
|
||||||
if (head->mParent) {
|
|
||||||
if (head->mParent->mLeft == head) head->mParent->mLeft = head->mLeft;
|
|
||||||
else head->mParent->mRight = head->mLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
head->mLeft->mParent = head->mParent;
|
|
||||||
|
|
||||||
auto delNode = head;
|
|
||||||
head = head->mLeft;
|
|
||||||
deleteNode(delNode);
|
|
||||||
} else {
|
|
||||||
deleteNode(head);
|
|
||||||
head = nullptr;
|
|
||||||
}
|
|
||||||
} else if (head->key.descentRight(key)) {
|
|
||||||
head->mRight = removeUtil(head->mRight, head->key.keyInRightSubtree(key));
|
|
||||||
} else {
|
|
||||||
head->mLeft = removeUtil(head->mLeft, head->key.keyInLeftSubtree(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head == nullptr) return head;
|
|
||||||
|
|
||||||
head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
|
|
||||||
alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
|
|
||||||
|
|
||||||
if (balance < -1) {
|
|
||||||
if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) {
|
|
||||||
return rotateRight(head);
|
|
||||||
} else {
|
|
||||||
head->mLeft = rotateLeft(head->mLeft);
|
|
||||||
return rotateRight(head);
|
|
||||||
}
|
|
||||||
} else if (balance > 1) {
|
|
||||||
if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) {
|
|
||||||
return rotateLeft(head);
|
|
||||||
} else {
|
|
||||||
head->mRight = rotateRight(head->mRight);
|
|
||||||
return rotateLeft(head);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
head->key.updateNodeCache(head);
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Node* mRoot = nullptr;
|
Node* mRoot = nullptr;
|
||||||
ualni mSize = 0;
|
ualni mSize = 0;
|
||||||
Allocator mAlloc;
|
Allocator mAlloc;
|
||||||
|
|
||||||
|
static Node gNullNode;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
template <typename Key, typename Data, class Allocator>
|
||||||
|
AvlTree<Key, Data, Allocator>::Node AvlTree<Key, Data, Allocator>::gNullNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "AVLTree.ipp"
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ SUITE(IntervalTree) {
|
||||||
TEST(FunctionalityScale) {
|
TEST(FunctionalityScale) {
|
||||||
|
|
||||||
const int NUM_TEST_INTERVALS = 1000;
|
const int NUM_TEST_INTERVALS = 1000;
|
||||||
const halnf SPAN = 100;
|
const halnf SPAN = 1000;
|
||||||
|
|
||||||
Buffer<Interval> pool;
|
Buffer<Interval> pool;
|
||||||
IntervalTree<halnf, ualni> intervalTree;
|
IntervalTree<halnf, ualni> intervalTree;
|
||||||
|
|
@ -72,9 +72,9 @@ SUITE(IntervalTree) {
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
intervalTree.forEachIntersection(testInterval->start, testInterval->end, [&](alni start, ualni end, ualni data) {
|
intervalTree.forEachIntersection(
|
||||||
result.append(data);
|
testInterval->start, testInterval->end, [&](alni start, ualni end, ualni data) { result.append(data); }
|
||||||
});
|
);
|
||||||
|
|
||||||
CHECK(correct.size() == result.size());
|
CHECK(correct.size() == result.size());
|
||||||
|
|
||||||
|
|
@ -270,5 +270,4 @@ SUITE(IntervalTree) {
|
||||||
|
|
||||||
CHECK(makeQuery(0, 1).found(2));
|
CHECK(makeQuery(0, 1).found(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
#include <iostream>
|
||||||
#include "Tests.hpp"
|
#include "Tests.hpp"
|
||||||
#include "Tree.hpp"
|
#include "Tree.hpp"
|
||||||
|
#include "Timing.hpp"
|
||||||
|
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
||||||
|
|
@ -24,7 +26,7 @@ SUITE(AvlTree) {
|
||||||
TEST(Persistance) {
|
TEST(Persistance) {
|
||||||
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
|
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
|
||||||
|
|
||||||
const auto size = 10;
|
const auto size = 1000;
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
Item() :
|
Item() :
|
||||||
|
|
@ -122,4 +124,62 @@ SUITE(AvlTree) {
|
||||||
CHECK(tree.maxNode(tree.head()) == nullptr);
|
CHECK(tree.maxNode(tree.head()) == nullptr);
|
||||||
CHECK(tree.minNode(tree.head()) == nullptr);
|
CHECK(tree.minNode(tree.head()) == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Speed) {
|
||||||
|
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
|
||||||
|
|
||||||
|
const auto size = 1000000;
|
||||||
|
|
||||||
|
struct Item {
|
||||||
|
Item() :
|
||||||
|
data(0) {}
|
||||||
|
bool presents = false;
|
||||||
|
TestClass data;
|
||||||
|
};
|
||||||
|
|
||||||
|
Item* buff = new Item[size];
|
||||||
|
|
||||||
|
for (auto i : Range(size)) {
|
||||||
|
buff[i].data.setVal(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer insertTime;
|
||||||
|
|
||||||
|
ualni loadSize = 0;
|
||||||
|
while (loadSize < size / 2) {
|
||||||
|
auto idx = ualni(randomFloat() * (size - 1));
|
||||||
|
if (!buff[idx].presents) {
|
||||||
|
tree.insert((alni) buff[idx].data.getVal(), buff[idx].data);
|
||||||
|
loadSize++;
|
||||||
|
buff[idx].presents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "AVL Tree Insert Speed " << (double) loadSize / (double) insertTime.timePassed() << "\n";
|
||||||
|
|
||||||
|
Timer removeTime;
|
||||||
|
|
||||||
|
ualni unloadSize = 0;
|
||||||
|
while (unloadSize < size / 2) {
|
||||||
|
auto idx = ualni(randomFloat() * (size - 1));
|
||||||
|
if (buff[idx].presents) {
|
||||||
|
tree.remove((alni) buff[idx].data.getVal());
|
||||||
|
unloadSize++;
|
||||||
|
buff[idx].presents = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto idx = 0; idx < size; idx++) {
|
||||||
|
auto& item = buff[idx];
|
||||||
|
if (item.presents) {
|
||||||
|
tree.remove((alni) item.data.getVal());
|
||||||
|
unloadSize++;
|
||||||
|
item.presents = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "AVL Tree Remove Speed " << (double) unloadSize / (double) removeTime.timePassed() << "\n";
|
||||||
|
|
||||||
|
delete[] buff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
3
TODO
3
TODO
|
|
@ -13,13 +13,10 @@ Modules:
|
||||||
Remove all static variable into module's data
|
Remove all static variable into module's data
|
||||||
|
|
||||||
Containers:
|
Containers:
|
||||||
Avl tree add sentenels
|
|
||||||
Add variant size buffer
|
Add variant size buffer
|
||||||
Buffer add resize function
|
Buffer add resize function
|
||||||
Buffer improvements
|
Buffer improvements
|
||||||
Write Traverse Avl test
|
|
||||||
Add check copy times
|
Add check copy times
|
||||||
AVL dont copy data just swap
|
|
||||||
|
|
||||||
Storage:
|
Storage:
|
||||||
Reconsider
|
Reconsider
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue