dont copy node data in tree delete operation - just swap nodes
This commit is contained in:
parent
6148ff15d2
commit
738ce1d914
3 changed files with 98 additions and 45 deletions
|
|
@ -54,7 +54,7 @@ namespace tp {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvlTree() {}
|
AvlTree() = default;
|
||||||
~AvlTree() { removeAll(); }
|
~AvlTree() { removeAll(); }
|
||||||
|
|
||||||
[[nodiscard]] ualni size() const { return mSize; }
|
[[nodiscard]] ualni size() const { return mSize; }
|
||||||
|
|
@ -125,34 +125,53 @@ namespace tp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks invariants of AVL tree
|
||||||
// returns first invalid node
|
// returns first invalid node
|
||||||
const Node* findInvalidNode(const Node* head) const {
|
const Node* findInvalidNode(const Node* head) const {
|
||||||
if (head == nullptr) return nullptr;
|
if (head == nullptr) return nullptr;
|
||||||
|
|
||||||
if (head->mLeft) {
|
if (head->mLeft) {
|
||||||
// TODO: incomplete test
|
// TODO: incomplete test
|
||||||
if (head->key.descentRight(head->mLeft->key.getFindKey(head))) return head;
|
if (head->key.descentRight(head->mLeft->key.getFindKey(head))) {
|
||||||
if (head->mLeft->mParent != head) return head;
|
return head;
|
||||||
if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) 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->mRight) {
|
||||||
if (!head->key.descentRight(head->mRight->key.getFindKey(head))) return head;
|
if (!head->key.descentRight(head->mRight->key.getFindKey(head))) {
|
||||||
if (head->mRight->mParent != head) return head;
|
return head;
|
||||||
if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) 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 (head->mLeft && head->mRight) {
|
||||||
if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) return head;
|
if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
|
int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
|
||||||
|
|
||||||
if (balance > 1 || balance < -1) return head;
|
if (balance > 1 || balance < -1) {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
const Node* ret = findInvalidNode(head->mRight);
|
const Node* ret = findInvalidNode(head->mRight);
|
||||||
|
|
||||||
if (ret) return ret;
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return findInvalidNode(head->mLeft);
|
return findInvalidNode(head->mLeft);
|
||||||
}
|
}
|
||||||
|
|
@ -195,20 +214,55 @@ namespace tp {
|
||||||
inline void deleteNode(Node* node) {
|
inline void deleteNode(Node* node) {
|
||||||
node->~Node();
|
node->~Node();
|
||||||
mAlloc.deallocate(node);
|
mAlloc.deallocate(node);
|
||||||
|
mSize--;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); }
|
inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); }
|
||||||
|
|
||||||
inline void injectNodeInstead(Node* place, Node* inject) {
|
// swaps pointers of two nodes to preserve data location in the memory instead of swapping data directly
|
||||||
// TODO : swap instead of copy
|
inline void injectNodeInstead(Node* target, Node* from) {
|
||||||
place->data = inject->data;
|
// 'target' always has two nodes
|
||||||
place->key = inject->key;
|
// '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 = 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; }
|
inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; }
|
||||||
|
|
||||||
// returns new head
|
// returns new head
|
||||||
Node* rotateLeft(Node* pivot) {
|
inline Node* rotateLeft(Node* pivot) {
|
||||||
DEBUG_ASSERT(pivot);
|
DEBUG_ASSERT(pivot);
|
||||||
|
|
||||||
Node* const head = pivot;
|
Node* const head = pivot;
|
||||||
|
|
@ -236,7 +290,7 @@ namespace tp {
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* rotateRight(Node* pivot) {
|
inline Node* rotateRight(Node* pivot) {
|
||||||
DEBUG_ASSERT(pivot);
|
DEBUG_ASSERT(pivot);
|
||||||
|
|
||||||
Node* const head = pivot;
|
Node* const head = pivot;
|
||||||
|
|
@ -318,22 +372,36 @@ namespace tp {
|
||||||
if (head->key.exactNode(key)) {
|
if (head->key.exactNode(key)) {
|
||||||
if (head->mRight && head->mLeft) {
|
if (head->mRight && head->mLeft) {
|
||||||
Node* min = minNode(head->mRight);
|
Node* min = minNode(head->mRight);
|
||||||
auto const& newKey = min->key.getFindKey(head->mRight);
|
|
||||||
injectNodeInstead(head, min);
|
injectNodeInstead(head, min);
|
||||||
head->mRight = removeUtil(head->mRight, newKey);
|
std::swap(head, min);
|
||||||
|
// auto const& newKey = min->key.getFindKey(head->mRight);
|
||||||
|
head->mRight = removeUtil(head->mRight, key);
|
||||||
} else if (head->mRight) {
|
} else if (head->mRight) {
|
||||||
injectNodeInstead(head, head->mRight);
|
|
||||||
deleteNode(head->mRight);
|
if (head->mParent) {
|
||||||
head->mRight = nullptr;
|
if (head->mParent->mLeft == head) head->mParent->mLeft = head->mRight;
|
||||||
mSize--;
|
else head->mParent->mRight = head->mRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
head->mRight->mParent = head->mParent;
|
||||||
|
|
||||||
|
auto delNode = head;
|
||||||
|
head = head->mRight;
|
||||||
|
deleteNode(delNode);
|
||||||
} else if (head->mLeft) {
|
} else if (head->mLeft) {
|
||||||
injectNodeInstead(head, head->mLeft);
|
|
||||||
deleteNode(head->mLeft);
|
if (head->mParent) {
|
||||||
head->mLeft = nullptr;
|
if (head->mParent->mLeft == head) head->mParent->mLeft = head->mLeft;
|
||||||
mSize--;
|
else head->mParent->mRight = head->mLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
head->mLeft->mParent = head->mParent;
|
||||||
|
|
||||||
|
auto delNode = head;
|
||||||
|
head = head->mLeft;
|
||||||
|
deleteNode(delNode);
|
||||||
} else {
|
} else {
|
||||||
deleteNode(head);
|
deleteNode(head);
|
||||||
mSize--;
|
|
||||||
head = nullptr;
|
head = nullptr;
|
||||||
}
|
}
|
||||||
} else if (head->key.descentRight(key)) {
|
} else if (head->key.descentRight(key)) {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
#include "Tree.hpp"
|
|
||||||
#include "Timing.hpp"
|
|
||||||
#include "UnitTest++/UnitTest++.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
TEST(AVLTreeStream) {
|
|
||||||
tp::Timer timer(1000);
|
|
||||||
|
|
||||||
tp::AvlTree<tp::AvlNumericKey<tp::ualni>> tree;
|
|
||||||
|
|
||||||
while (!timer.isTimeout()) {
|
|
||||||
tree.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,7 @@ SUITE(AvlTree) {
|
||||||
TEST(Persistance) {
|
TEST(Persistance) {
|
||||||
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
|
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
|
||||||
|
|
||||||
const auto size = 1000;
|
const auto size = 10;
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
Item() :
|
Item() :
|
||||||
|
|
@ -87,6 +87,8 @@ SUITE(AvlTree) {
|
||||||
|
|
||||||
tree.remove((alni) buff[idx].data.getVal());
|
tree.remove((alni) buff[idx].data.getVal());
|
||||||
|
|
||||||
|
CHECK(tree.isValid());
|
||||||
|
|
||||||
unloadSize++;
|
unloadSize++;
|
||||||
buff[idx].presents = false;
|
buff[idx].presents = false;
|
||||||
|
|
||||||
|
|
@ -120,5 +122,4 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue