Files
2026-02-02 04:50:13 +01:00

460 lines
11 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/misc/iavl_tree.h"
#include "ultima/nuvie/core/nuvie_defs.h"
namespace Ultima {
namespace Nuvie {
static iAVLNode *iAVLCloseSearchNode(iAVLTree const *avltree, iAVLKey key);
static void iAVLRebalanceNode(iAVLTree *avltree, iAVLNode *avlnode);
static void iAVLFreeBranch(iAVLNode *avlnode, void (freeitem)(void *item));
static void iAVLFillVacancy(iAVLTree *avltree,
iAVLNode *origparent, iAVLNode **superparent,
iAVLNode *left, iAVLNode *right);
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define L_DEPTH(n) ((n)->left ? (n)->left->depth : 0)
#define R_DEPTH(n) ((n)->right ? (n)->right->depth : 0)
#define CALC_DEPTH(n) (MAX(L_DEPTH(n), R_DEPTH(n)) + 1)
/*
* AVLAllocTree:
* Allocate memory for a new AVL tree and set the getkey function for
* that tree. The getkey function should take an item and return an
* AVLKey that is to be used for indexing this object in the AVL tree.
* On success, a pointer to the malloced AVLTree is returned. If there
* was a malloc failure, then nullptr is returned.
*/
iAVLTree *iAVLAllocTree(iAVLKey(*getkey)(void const *item)) {
iAVLTree *rc;
rc = (iAVLTree *)malloc(sizeof(iAVLTree));
if (rc == nullptr)
return nullptr;
rc->top = nullptr;
rc->count = 0;
rc->getkey = getkey;
return rc;
}
/*
* AVLFreeTree:
* Free all memory used by this AVL tree. If freeitem is not nullptr, then
* it is assumed to be a destructor for the items reference in the AVL
* tree, and they are deleted as well.
*/
void iAVLFreeTree(iAVLTree *avltree, void (freeitem)(void *item)) {
iAVLCleanTree(avltree, freeitem);
free(avltree);
}
void iAVLCleanTree(iAVLTree *avltree, void (freeitem)(void *item)) {
if (avltree->top)
iAVLFreeBranch(avltree->top, freeitem);
avltree->top = nullptr;
avltree->count = 0;
}
/*
* AVLInsert:
* Create a new node and insert an item there.
*
* Returns 0 on success,
* -1 on malloc failure,
* 3 if duplicate key.
*/
int iAVLInsert(iAVLTree *avltree, void *item) {
iAVLNode *newnode;
iAVLNode *node;
iAVLNode *balnode;
iAVLNode *nextbalnode;
newnode = (iAVLNode *)malloc(sizeof(iAVLNode));
if (newnode == nullptr)
return -1;
newnode->key = avltree->getkey(item);
newnode->item = item;
newnode->depth = 1;
newnode->left = nullptr;
newnode->right = nullptr;
newnode->parent = nullptr;
if (avltree->top != nullptr) {
node = iAVLCloseSearchNode(avltree, newnode->key);
if (!iAVLKey_cmp(avltree, node->key, newnode->key)) {
free(newnode);
return 3;
}
newnode->parent = node;
if (iAVLKey_cmp(avltree, newnode->key, node->key) < 0) {
node->left = newnode;
node->depth = CALC_DEPTH(node);
}
else {
node->right = newnode;
node->depth = CALC_DEPTH(node);
}
for (balnode = node->parent; balnode; balnode = nextbalnode) {
nextbalnode = balnode->parent;
iAVLRebalanceNode(avltree, balnode);
}
}
else {
avltree->top = newnode;
}
avltree->count++;
return 0;
}
/*
* iAVLSearch:
* Return a pointer to the item with the given key in the AVL tree. If
* no such item is in the tree, then nullptr is returned.
*/
void *iAVLSearch(iAVLTree const *avltree, iAVLKey key) {
iAVLNode *node;
node = iAVLCloseSearchNode(avltree, key);
if (node && !iAVLKey_cmp(avltree, node->key, key))
return node->item;
return nullptr;
}
/*
* iAVLDelete:
* Deletes the node with the given key. Does not delete the item at
* that key. Returns 0 on success and -1 if a node with the given key
* does not exist.
*/
int iAVLDelete(iAVLTree *avltree, iAVLKey key) {
iAVLNode *avlnode;
iAVLNode *origparent;
iAVLNode **superparent;
avlnode = iAVLCloseSearchNode(avltree, key);
if (avlnode == nullptr || iAVLKey_cmp(avltree, avlnode->key, key))
return -1;
origparent = avlnode->parent;
if (origparent) {
if (iAVLKey_cmp(avltree, avlnode->key, avlnode->parent->key) < 0)
superparent = &(avlnode->parent->left);
else
superparent = &(avlnode->parent->right);
} else
superparent = &(avltree->top);
iAVLFillVacancy(avltree, origparent, superparent,
avlnode->left, avlnode->right);
free(avlnode);
avltree->count--;
return 0;
}
/*
* iAVLFirst:
* Initializes an iAVLCursor object and returns the item with the lowest
* key in the iAVLTree.
*/
void *iAVLFirst(iAVLCursor *avlcursor, iAVLTree const *avltree) {
const iAVLNode *avlnode;
avlcursor->avltree = avltree;
if (avltree->top == nullptr) {
avlcursor->curnode = nullptr;
return nullptr;
}
for (avlnode = avltree->top;
avlnode->left != nullptr;
avlnode = avlnode->left);
avlcursor->curnode = avlnode;
return avlnode->item;
}
/*
* iAVLNext:
* Called after an iAVLFirst() call, this returns the item with the least
* key that is greater than the last item returned either by iAVLFirst()
* or a previous invokation of this function.
*/
void *iAVLNext(iAVLCursor *avlcursor) {
const iAVLNode *avlnode;
avlnode = avlcursor->curnode;
if (avlnode->right != nullptr) {
for (avlnode = avlnode->right;
avlnode->left != nullptr;
avlnode = avlnode->left);
avlcursor->curnode = avlnode;
return avlnode->item;
}
while (avlnode->parent && avlnode->parent->left != avlnode) {
avlnode = avlnode->parent;
}
if (avlnode->parent == nullptr) {
avlcursor->curnode = nullptr;
return nullptr;
}
avlcursor->curnode = avlnode->parent;
return avlnode->parent->item;
}
/*
* iAVLCloseSearchNode:
* Return a pointer to the node closest to the given key.
* Returns nullptr if the AVL tree is empty.
*/
iAVLNode *iAVLCloseSearchNode(iAVLTree const *avltree, iAVLKey key) {
iAVLNode *node;
node = avltree->top;
if (!node)
return nullptr;
for (;;) {
if (!iAVLKey_cmp(avltree, node->key, key))
return node;
if (iAVLKey_cmp(avltree, node->key, key) < 0) {
if (node->right)
node = node->right;
else
return node;
}
else {
if (node->left)
node = node->left;
else
return node;
}
}
}
/*
* iAVLRebalanceNode:
* Rebalances the AVL tree if one side becomes too heavy. This function
* assumes that both subtrees are AVL trees with consistent data. This
* function has the additional side effect of recalculating the depth of
* the tree at this node. It should be noted that at the return of this
* function, if a rebalance takes place, the top of this subtree is no
* longer going to be the same node.
*/
void iAVLRebalanceNode(iAVLTree *avltree, iAVLNode *avlnode) {
long depthdiff;
iAVLNode *child;
iAVLNode *gchild;
iAVLNode *origparent;
iAVLNode **superparent;
origparent = avlnode->parent;
if (origparent) {
if (iAVLKey_cmp(avltree, avlnode->key, avlnode->parent->key) < 0)
superparent = &(avlnode->parent->left);
else
superparent = &(avlnode->parent->right);
} else
superparent = &(avltree->top);
depthdiff = R_DEPTH(avlnode) - L_DEPTH(avlnode);
if (depthdiff <= -2) {
child = avlnode->left;
if (L_DEPTH(child) >= R_DEPTH(child)) {
avlnode->left = child->right;
if (avlnode->left != nullptr)
avlnode->left->parent = avlnode;
avlnode->depth = CALC_DEPTH(avlnode);
child->right = avlnode;
if (child->right != nullptr)
child->right->parent = child;
child->depth = CALC_DEPTH(child);
*superparent = child;
child->parent = origparent;
}
else {
gchild = child->right;
avlnode->left = gchild->right;
if (avlnode->left != nullptr)
avlnode->left->parent = avlnode;
avlnode->depth = CALC_DEPTH(avlnode);
child->right = gchild->left;
if (child->right != nullptr)
child->right->parent = child;
child->depth = CALC_DEPTH(child);
gchild->right = avlnode;
if (gchild->right != nullptr)
gchild->right->parent = gchild;
gchild->left = child;
if (gchild->left != nullptr)
gchild->left->parent = gchild;
gchild->depth = CALC_DEPTH(gchild);
*superparent = gchild;
gchild->parent = origparent;
}
}
else if (depthdiff >= 2) {
child = avlnode->right;
if (R_DEPTH(child) >= L_DEPTH(child)) {
avlnode->right = child->left;
if (avlnode->right != nullptr)
avlnode->right->parent = avlnode;
avlnode->depth = CALC_DEPTH(avlnode);
child->left = avlnode;
if (child->left != nullptr)
child->left->parent = child;
child->depth = CALC_DEPTH(child);
*superparent = child;
child->parent = origparent;
}
else {
gchild = child->left;
avlnode->right = gchild->left;
if (avlnode->right != nullptr)
avlnode->right->parent = avlnode;
avlnode->depth = CALC_DEPTH(avlnode);
child->left = gchild->right;
if (child->left != nullptr)
child->left->parent = child;
child->depth = CALC_DEPTH(child);
gchild->left = avlnode;
if (gchild->left != nullptr)
gchild->left->parent = gchild;
gchild->right = child;
if (gchild->right != nullptr)
gchild->right->parent = gchild;
gchild->depth = CALC_DEPTH(gchild);
*superparent = gchild;
gchild->parent = origparent;
}
}
else {
avlnode->depth = CALC_DEPTH(avlnode);
}
}
/*
* iAVLFreeBranch:
* Free memory used by this node and its item. If the freeitem argument
* is not nullptr, then that function is called on the items to free their
* memory as well. In other words, the freeitem function is a
* destructor for the items in the tree.
*/
void iAVLFreeBranch(iAVLNode *avlnode, void (freeitem)(void *item)) {
if (avlnode->left)
iAVLFreeBranch(avlnode->left, freeitem);
if (avlnode->right)
iAVLFreeBranch(avlnode->right, freeitem);
if (freeitem != nullptr)
freeitem(avlnode->item);
free(avlnode);
}
/*
* iAVLFillVacancy:
* Given a vacancy in the AVL tree by it's parent, children, and parent
* component pointer, fill that vacancy.
*/
void iAVLFillVacancy(iAVLTree *avltree,
iAVLNode *origparent, iAVLNode **superparent,
iAVLNode *left, iAVLNode *right) {
iAVLNode *avlnode;
iAVLNode *balnode;
iAVLNode *nextbalnode;
if (left == nullptr) {
if (right)
right->parent = origparent;
*superparent = right;
balnode = origparent;
}
else {
for (avlnode = left; avlnode->right != nullptr; avlnode = avlnode->right);
if (avlnode == left) {
balnode = avlnode;
} else {
balnode = avlnode->parent;
balnode->right = avlnode->left;
if (balnode->right != nullptr)
balnode->right->parent = balnode;
avlnode->left = left;
left->parent = avlnode;
}
avlnode->right = right;
if (right != nullptr)
right->parent = avlnode;
*superparent = avlnode;
avlnode->parent = origparent;
}
for (; balnode; balnode = nextbalnode) {
nextbalnode = balnode->parent;
iAVLRebalanceNode(avltree, balnode);
}
}
} // End of namespace Nuvie
} // End of namespace Ultima