Files
secondo/Tools/Flob/DBATree.h
2026-01-23 17:03:45 +08:00

1086 lines
24 KiB
C++

/*
This file is part of SECONDO.
Copyright (C) 2018,
University in Hagen,
Faculty of Mathematics and of Computer Science,
Database Systems for New Applications.
SECONDO 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 2 of the License, or
(at your option) any later version.
SECONDO 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 SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1 Class DBATree
This class provides an AVL tree implementation within a DBArray
*/
#ifndef DBATREE_H
#define DBATREE_H
#include <Tools/Flob/DbArray.h>
#include <ostream>
namespace dbatree{
/*
1.1 Class StdComparator
This class privides a comparator function for the most
standard types.
*/
template <class Elem>
class StdComparator{
public:
int operator()(const Elem& e1, const Elem& e2) const{
if(e1<e2) return -1;
if(e1==e2) return 0;
return 1;
}
};
template<class Elem, class Comp = StdComparator<Elem> >
class DBATree{
private:
/*
1.2 Class TreeEntry
This class is used to represent a single node in the tree.
Besides the element to store, it contains symbolic links to
the childs and the father as well as the information
of the height of the corresponding subtree.
*/
class TreeEntry{
public:
/*
1.2.1 Constructor
The standard constructor does nothing. This is a requirement of
the DBArray.
*/
TreeEntry(){}
/*
1.2.2 Constructor
This constructor create a node without connections.
*/
TreeEntry(Elem e): elem(e), left(-1),right(-1),father(-1),height(1){}
/*
1.2.3 Copy Constructor
*/
TreeEntry(const TreeEntry& e): elem(e.elem), left(e.left),
right(e.right), father(e.father),height(e.height){}
/*
1.2.4 Assignment operator
*/
TreeEntry& operator=(const TreeEntry& e){
elem = e.elem;
left = e.left;
right = e.right;
father = e.father;
height = e.height;
return *this;
}
/*
1.2.5 swapConnections
This operator swaps all members of this and e except the content.
*/
void swapConnections(TreeEntry& e){
std::swap(left,e.left);
std::swap(right,e.right);
std::swap(father,e.father);
std::swap(height, e.height);
}
/*
1.2.6 print
Writes a readable representation to out.
*/
std::ostream& print(std::ostream& out)const{
//out << elem << ", l:" << left << ",R:" << right
// << ",f: " << father << ",h:" << height;
return out;
}
/*
1.2.7 Member variables
Because this class is only accessible from the Tree class,
all members are public.
*/
Elem elem;
int left;
int right;
int father;
int height;
};
public:
/*
1.3 Class DBATree
1.3.1 Standard Constructor
This constructor creates an empty tree.
*/
DBATree( ): array(0), lastPos(0), cmp(), root(-1){}
/*
1.3.2 ~clear~
Removes all entries from the tree.
*/
void clear(){
array.clean();
lastPos = 0;
root = -1;
}
void destroy(){
clear();
array.Destroy();
}
/*
1.3.3 ~inserts~
Inserts an element to the tree. If a corresponding entrie already
exists in the tree, the tree is keept unchanged.
*/
bool insert(const Elem& elem){
TreeEntry te(elem);
if(lastPos==0){ // first elem in tree
array.Put(0,te);
lastPos++;
root = 0;
return true;
} else {
bool ok = insert(root,te,false);
return ok;
}
}
/*
1.3.4 ~insertOrUpdate~
Inserts a element into the tree. If the element is already part of the
tree, its value is overwritten by the new value. This makes sense if
the comparison function only uses a part of elem.
*/
bool insertOrUpdate(const Elem& elem){
TreeEntry te(elem);
if(lastPos==0){ // first elem in tree
array.Put(0,te);
lastPos++;
root = 0;
return true;
} else {
bool ok = insert(root,te,true);
return ok;
}
}
/*
1.3.5 ~contains~
Checks whether an element with given value is part of the tree.
*/
inline bool contains(const Elem& elem){
return getIndex(elem,false) >=0;
}
/*
1.3.6 ~update~
Updates an entry in the tree.
*/
inline bool update(const Elem& elem){
return getIndex(elem,true) >=0;
}
/*
1.3.7 ~get~
Changes the value of elem to that is stored in the tree.
If the element is not found, the argument is not changed.
*/
inline bool get(Elem& elem){
return get(root,elem);
}
/*
1.3.6 remove
Removes an element from the tree.
*/
inline bool remove(const Elem& elem) {
if(lastPos==0) {
return false;
}
return remove(root,elem);
}
inline bool isEmpty() const{
return lastPos == 0;
}
/*
1.3.7 print
Prints out a textual representation of the tree.
*/
std::ostream& print(std::ostream& out) const{
out << "( tree ";
printRec(root,out,true);
out << ")";
return out;
}
/*
1.3.8 printArray
prints out the internal representation of the tree
*/
std::ostream& printArray(std::ostream& out) const{
out << "size of Array " << array.Size() << std::endl;;
out << "lastPos " << lastPos << std::endl;
out << "root " << root << std::endl;
out << "Contents" << std::endl;
TreeEntry e ;
for(int i=0;i<array.Size();i++){
array.Get(i,e);
out << i << ":" ; e.print(out) << std::endl;
}
return out;
}
/*
1.3.9 check
This function checks whether the structire of the tree is correct.
*/
bool check(){
bool res = check(root,true);
if(!res){
std::cerr << " invalid tree structure" << std::endl;
printArray(std::cerr) << std::endl << std::endl;
print(std::cerr) << std::endl << std::endl;
}
return res;
}
private:
/*
1.3.10 Member variables
*/
DbArray<TreeEntry> array; // container
size_t lastPos; // number of nodes in the tree
Comp cmp; // the used comparator
int root; // index of tree's root
/*
1.3.11 insert
Inserts a new element or updates an existing one.
*/
bool insert(int root, TreeEntry& elem, bool update){
TreeEntry current;
bool done = false;
while(!done){
array.Get(root,current);
int c = cmp(elem.elem, current.elem);
if(c==0){
if(update){
current.elem = elem.elem;
array.Put(root,current);
}
return false; // element already present
}
if(c<0){
if(current.left<0){ // found insertion pos
current.left = lastPos;
array.Put(root,current);
elem.father = root;
array.Put(lastPos,elem);
lastPos++;
done = true;
} else {
root = current.left;
}
} else {
if(current.right<0){
current.right = lastPos;
array.Put(root,current);
elem.father = root;
array.Put(lastPos,elem);
lastPos++;
done = true;
} else {
root = current.right;
}
}
}
// correct heights if necessary
correctPath(root);
return true;
}
void correctPath(int index){
while(index >=0 ){
index = balance(index);
}
}
/*
1.3.12 ~getIndex~
Returns the index of an stored element or -1 if the element is
not part of the tree. If update is set to true, an existing
element is overwritten by the input argument.
*/
int getIndex(const Elem& elem,bool update) {
if(lastPos==0) return -1;
int root = this->root;
TreeEntry current;
while(true){
array.Get(root,current);
int c = cmp(elem, current.elem);
if(c==0){
if(update){
current.elem = elem;
array.Put(root,current);
}
return root;
}
if(c<0){
if(current.left<0){
return -1;
}
root = current.left;
} else {
if(current.right<0){
return -1;
}
root = current.right;
}
}
return -1;
}
/*
1.3.12 ~get~
*/
bool get(int root, Elem& elem) {
if(lastPos==0) return -1;
root = this->root;
TreeEntry current;
while(true){
array.Get(root,current);
int c = cmp(elem, current.elem);
if(c==0){
elem = current.elem;
return true;
}
if(c<0){
if(current.left<0){
return false;
}
root = current.left;
} else {
if(current.right<0){
return false;
}
root = current.right;
}
}
return false;
}
/*
1.3.13 Remove
Removes a given element from the tree. Returns true if the
element was found, false otherwise.
*/
bool remove(int dummy, const Elem& elem){
int index = getIndex(elem,false);
if(index < 0){
return false;
}
TreeEntry victim;
array.Get(index, victim);
if(victim.left<0 || victim.right < 0){
int father = removeEnd(index);
correctPath(father);
return true;
}
int min = getMinIndex(victim.right);
swap(index,min);
int father = removeEnd(min);
correctPath(father);
if(lastPos==0){
this->root = -1;
}
return true;
}
/*
1.3.14 swap
Swaps the contents of two entries in the array.
This will destroy the ording of the tree but keep its
structure.
*/
void swap(int index1, int index2){
if(index1==index2) return;
TreeEntry e1;
TreeEntry e2;
array.Get(index1,e1);
array.Get(index2,e2);
e1.swapConnections(e2);
array.Put(index2,e1);
array.Put(index1,e2);
}
/*
1.3.15 ~removeEnd~
Removes an entry from the array. The element at the specified
index can have at most one child.
*/
int removeEnd(int index) {
TreeEntry victim;
array.Get(index,victim);
int r = victim.father;
assert(victim.left<0 || victim.right<0);
// disconnect this node
if(r>=0){
TreeEntry father;
array.Get(r,father);
if(father.left==index){
if(victim.left>=0) {
father.left = victim.left;
setFather(victim.left, victim.father);
} else if(victim.right>=0){
father.left = victim.right;
setFather(victim.right,victim.father);
} else {
father.left = -1;
}
} else {
assert(father.right==index);
if(victim.left>=0) {
father.right = victim.left;
setFather(victim.left, victim.father);
} else if(victim.right>=0){
father.right = victim.right;
setFather(victim.right, victim.father);
} else {
father.right = -1;
}
}
array.Put(victim.father,father);
} else { // removing the root of the tree
if(victim.left>=0){
this->root = victim.left;
setFather(victim.left, -1);
} else if(victim.right>=0){
this->root = victim.right;
setFather(victim.right,-1);
} else {
this->root = -1;
}
}
return freePosition(index, r);
}
/*
1.3.16 ~setFather~
Changes the information about the father of a node.
*/
void setFather(int index, int father){
if(index<0) return; // empty subtree has no father
TreeEntry elem;
array.Get(index,elem);
elem.father = father;
array.Put(index,elem);
}
/*
1.3.17 ~getMaxIndex~
Returns the index of the maximum value stored in the
subtree rooted by root.
*/
int getMaxIndex(int root) const{
TreeEntry current;
array.Get(root,current);
while(current.right>=0){
root = current.right;
array.Get(root,current);
}
return root;
}
/*
1.3.17 ~getMinIndex~
Returns the index of the minimum value stored in the
subtree rooted by root.
*/
int getMinIndex(int root) const{
TreeEntry current;
array.Get(root,current);
while(current.left>=0){
root = current.left;
array.Get(root,current);
}
return root;
}
/*
1.3.18 ~freePosition~
Removed an entry in the array. If the entry is not the
last one in the array, the last entry is moved to that
position and the last entry is removed. It returns the
possible new position of someNode, i.e. someNode or index
if someNode was at the last position in the array.
*/
int freePosition(int index,int someNode){
assert(lastPos>0);
lastPos--;
if(someNode==lastPos){
someNode = index;
}
if(index==lastPos){ // last used array element, no movement required
return someNode;
}
// move element at last position to index position
TreeEntry entry;
array.Get(lastPos,entry);
array.Put(index,entry);
// correct connection of father and childs
replaceSon(entry.father, lastPos, index);
setFather(entry.left, index);
setFather(entry.right, index);
return someNode;
}
/*
1.3.19 ~printRec~
recursive printing out the tree.
*/
void printRec(int root, std::ostream& out, bool full) const{
if(root<0){
out << " \"\" ";
} else {
TreeEntry entry;
array.Get(root,entry);
out << "(";
printLabel(root,entry,out,full);
printRec(entry.left,out,full);
printRec(entry.right,out,full);
out << ")";
}
}
/*
1.3.20 ~printLabel~
Auxiliary function for printing out the tree.
*/
void printLabel(int index, TreeEntry& e, std::ostream& out,
bool full) const{
out << "\"";
out << e.elem;
if(full){
out << " [i: " << index << ", f: " << e.father
<< ", h: " << e.height << "]";
}
out << "\"";
}
/*
1.3.21 ~correctHeight~
Changes the height information of a node based on the height
information of its direct childs.
*/
bool correctHeight(TreeEntry& elem) {
int nh = max(getHeight(elem.left) , getHeight(elem.right))+1;
if(elem.height!=nh){
elem.height = nh;
return true;
}
return false;
}
/*
1.3.22 ~getHeight~
Returns the height of a node.
*/
int getHeight(int index){
if(index<0) return 0; // empty tree
TreeEntry elem;
array.Get(index,elem);
return elem.height;
}
/*
1.2.23 max
Returns the maximum of two integer values.
*/
inline static int max(int a, int b){
return a>b?a:b;
}
/*
1.2.24 ~abs~
Returns the absolute of an integer value.
*/
inline static int abs(int a){
return a<0?-a:a;
}
/*
1.2.25 ~getBalanceAndCorrectHeight~
Returns the balance value of a node. If the height of this
node is not correct, the correct height will be stored.
*/
int getBalanceAndCorrectHeight(int index, TreeEntry& elem){
int hl = getHeight(elem.left);
int hr = getHeight(elem.right);
int nh = max(hl,hr) + 1;
if(elem.height!=nh){
elem.height = nh;
array.Put(index,elem);
}
return hl - hr;
}
/*
1.2.26 ~balance~
Balances a single node if necessary and returns the father of this node.
*/
int balance(int index){
TreeEntry x;
array.Get(index,x);
int b = getBalanceAndCorrectHeight(index, x);
if(abs(b) < 2){
// no balancing required
return x.father;
}
if(b < -1 ){ // right subtree is heigher
TreeEntry y;
array.Get(x.right,y);
int by = getBalanceAndCorrectHeight(x.right,y);
if(by <= 0){ // case a1
return rotateLeft(index,x, x.right,y);
} else { // case a2
TreeEntry z;
array.Get(x.right,z);
array.Get(z.left,y);
return rotateRightLeft(index, x, x.right, z, z.left,y);
}
} else { // left subtree is heigher
TreeEntry y;
array.Get(x.left,y);
int by = getBalanceAndCorrectHeight(x.left,y);
if(by>=0) {
return rotateRight(index,x, x.left,y);
} else {
TreeEntry z;
array.Get(x.left,z);
array.Get(z.right,y);
return rotateLeftRight(index, x, x.left, z, z.right,y);
}
}
}
/*
1.3.26 replaceSon
Replaces the son oldson by newson of node at index father.
*/
void replaceSon(int father, int oldson, int newson){
if(father<0){
assert(root==oldson);
root = newson;
} else {
TreeEntry f;
array.Get(father,f);
if(f.left == oldson){
f.left = newson;
} else if(f.right==oldson){
f.right = newson;
} else {
std::cerr << " try to replace son of node at index "
<< father << std::endl;
std::cerr << " son to replace " << oldson << std::endl;
std::cerr << " new son " << newson << std::endl;
std::cerr << " father.left = " << f.left << std::endl;
std::cerr << " father.right = " << f.right << std::endl;
assert(false);
}
correctHeight(f);
array.Put(father, f);
}
}
/*
1.3.26 Left Rotation
*/
int rotateLeft(int posx, TreeEntry& x,
int posy, TreeEntry& y){
int father = x.father;
x.right = y.left;
setFather(y.left,posx);
x.father = posy;
correctHeight(x);
array.Put(posx,x);
y.left = posx;
y.father = father;
correctHeight(y);
array.Put(posy,y);
replaceSon(father,posx, posy);
return father;
}
/*
1.3.27 Right rotation
*/
int rotateRight(int posx, TreeEntry& x,
int posy, TreeEntry& y){
int father = x.father;
x.left = y.right;
setFather(y.right,posx);
x.father = posy;
correctHeight(x);
array.Put(posx,x);
y.right = posx;
y.father = father;
correctHeight(y);
array.Put(posy,y);
replaceSon(father,posx,posy);
return father;
}
/*
1.3.28 Double rotation Right Left
*/
int rotateRightLeft(int posx, TreeEntry& x,
int posz, TreeEntry& z,
int posy, TreeEntry& y) {
int father = x.father;
x.right = y.left;
setFather(y.left,posx);
x.father = posy;
correctHeight(x);
array.Put(posx,x);
z.left = y.right;
setFather(y.right,posz);
z.father = posy;
correctHeight(z);
array.Put(posz,z);
y.left = posx;
y.right = posz;
y.father = father;
correctHeight(y);
array.Put(posy,y);
replaceSon(father,posx,posy);
return father;
}
/*
1.3.29 Double Rotation Left Right
*/
int rotateLeftRight(int posx, TreeEntry& x,
int posz, TreeEntry& z,
int posy, TreeEntry& y){
int father = x.father;
z.right = y.left;
setFather(y.left,posz);
z.father = posy;
correctHeight(z);
array.Put(posz,z);
x.left = y.right;
setFather(y.right,posx);
x.father = posy;
correctHeight(x);
array.Put(posx,x);
y.left = posz;
y.right = posx;
y.father = father;
correctHeight(y);
array.Put(posy,y);
replaceSon(father, posx, posy);
return father;
}
/*
1.3.30 ~checkFather~
Checks whether son is a son of father
*/
bool checkFather(int father, int son){
if(father>=0){
TreeEntry f;
array.Get(father,f);
if( f.left!=son && f.right!=son){
return false;
}
}
if(son>=0){
TreeEntry s;
array.Get(son,s);
if(s.father !=father){
return false;
}
}
return true;
}
/*
1.3.31 ~check~
Checks the structure of the tree rooted by index.
If rec is true, all substrees are checkes.
If checkBalance is set to false, checking height
information is omitted.
*/
bool check(int index, bool rec, bool checkBalance = true){
if(index < 0) return true;
if(index >=lastPos){
std::cerr << "try to check correctness of index "
<< index << std::endl;
std::cerr << "that is outside the allowed range "
<< lastPos << std::endl;
return false;
}
TreeEntry elem;
array.Get(index, elem);
// check for height and balance
if(checkBalance){
int lh = getHeight(elem.left);
int rh = getHeight(elem.right);
int h = max(lh,rh)+1;
if(h!=elem.height){
std::cerr << "invalid height entry at index " << index << std::endl;
std::cerr << "should be " << h << " butr stored is "
<< elem.height << std::endl;
return false;
}
if(abs(lh-rh)>1){
std::cerr << "node at index " << index
<< " is unbalanced" << std::endl;
std::cerr << "height left : " << lh << std::endl;
std::cerr << "height right:" << rh << std::endl;
return false;
}
}
// check whether index in some son of the father
int father = elem.father;
if(father < 0){
if(root!=index){
std::cerr << "father at node at index " << index
<< " is not there " << std::endl;
std::cerr << "hence the node should be the root of the tree"
<< std::endl;
std::cerr << "but the root of the tree is " << root << std::endl;
return false;
}
} else {
TreeEntry f;
array.Get(father,f);
if( f.left!=index && f.right!=index){
std::cerr << "father of node at index " << index << " is "
<< father << std::endl;
std::cerr << "but the cilds of that node are " << f.left
<< " and " << f.right << std::endl;
return false;
}
}
// check whether index is the father of its sons
if(elem.left >=0){
TreeEntry son;
array.Get(elem.left,son);
if(son.father != index){
std::cerr << "left son of node at index " << index << " is "
<< elem.left << std::endl;
std::cerr << "but the father of this node is "
<< son.father << std::endl;
return false;
}
}
if(elem.right >=0){
TreeEntry son;
array.Get(elem.right,son);
if(son.father != index){
std::cerr << "right son of node at index " << index
<< " is " << elem.right << std::endl;
std::cerr << "but the father of this node is "
<< son.father << std::endl;
return false;
}
}
if(!rec){
return true;
}
return check(elem.left,true,checkBalance)
&& check(elem.right,true,checkBalance);
}
};
} // end of namespace
#endif