Extends Balanced BST into C++ STL Map - c++

I've implemented a balanced binary search tree (Red-black tree) for practice purposes. here is the header of data-structure of underlying nodes and methods I've implemented so far:
#ifndef BST_H
#define BST_H
template <typename T>
class treeNode {
public:
treeNode *left;
treeNode *right;
T key;
treeNode(T key)
: key(key)
, left(nullptr)
, right(nullptr) {
}
};
template <typename T>
class BST {
public:
BST() {
root = nullptr;
nodes = 0;
}
BST(BST const& rhs);
BST& operator = (BST rhs) {
this->swap(rhs);
}
BST& operator = (BST&& rhs) {
this->swap(rhs);
}
~BST() {
clear(root);
}
void swap(BST& other) {
std::swap(root, other.root);
std::swap(nodes, other.nodes);
}
void clear(treeNode<T>* node) {
if(node) {
if(node->left) clear(node->left);
if(node->right) clear(node->right);
delete node;
}
}
bool isEmpty() const {
return root == nullptr;
}
void inorder(treeNode<T>*);
void traverseInorder();
void preorder(treeNode<T>*);
void traversePreorder();
void postorder(treeNode<T>*);
void traversePostorder();
void insert(T const& );
void remove(T const& );
treeNode<T>* search(const T &);
treeNode<T>* minHelper(treeNode<T>*);
treeNode<T>* min();
treeNode<T>* maxHelper(treeNode<T>*);
treeNode<T>* max();
size_t size() const;
void sort();
treeNode<T>* inOrderSuccessor(treeNode<T>*);
bool isBST(treeNode<T>*) const;
bool isBST() const;
private:
treeNode<T> *root;
size_t nodes;
};
#endif
I intend to implement C++ STL map (I've already implemented STL unordered_map using Hashtable) for which the underlying data-structure is Red-Black Tree AFAIK. How I can extend my tree to a key-value generic type map?
No need of any sort of source code. Some intuition will suffice. Thanks :)

With intuition: T will probably be pair<const key_type,mapped_type>. I'm Assuming that currently you use node.key < another_node.key for comparisons. That will not do, because a map should be only using the first part of the pair for that. You could add a Compare functor as a template parameter (in similar manner as you'll have to for your map class) to your tree to make it useful for implementing a stl compatible map.
You may choose to design your tree so that key and value classes are separate rather than combined. Here's example code for the template definition:
template<class Key, class Value, class Comp=std::less<Key>>
class BST {
Compare comp;
public:
BST(const Comp& comp = Comp()): comp(comp)
//...
// usage
if(comp(node.key, another_node.key)) {
// node is considered to be strictly before another_node
You can use std::less as a sensible default parameter for other users of the tree, but the map implementation should forward the comparator which was given for the map.
A fully stl compatible container should support custom allocators too and to make that possible, so must the internal tree structure.

Related

BST with function ptr, How to add in extra argument?

is there a way to pass in an extra argument to my function pointer in BST? I am trying to use BST inOrder to get value from a map<string, int>. this BST will be storing the key of the map.
The map will act as a database that uses date + time as the key. each BST will be created to store the date+time of each year and saved into another map (bstMap) which holds all bst. bstMap will use the year as key.
BST inOrder with function ptr.
#ifndef BST_H
#define BST_H
#include<iostream>
using namespace std;
template <class T>
class Node.
{
public:
T m_key;
Node<T> *m_left;
Node<T> *m_right;
};
template <class T>
class BST
{
typedef void(*funcPtr)(T &);
public:
BST();
void Insert(T key);
void Delete();
void InOrder(void(*funcPtr)(T &)) const;
void PreOrder(void(*funcPtr)(T &)) const;
bool Search(T key);
T MaxValue();
bool IsEmpty() const {return m_root == nullptr;}
void DeleteTree();
private:
Node<T> *m_root;
protected:
Node<T> *Insert(Node<T>* node, T key);
Node<T> *Search(Node<T>* node, T key);
void InOrder(Node<T>* node, void (*funcPtr)(T &)) const;
void PreOrder(Node<T>* node, void (*funcPtr)(T &)) const;
void DeleteTree(Node<T>* node);
Node<T>* MaxValue(Node<T>* node);
};
template<class T>
BST<T>::BST(){
m_root = nullptr;
}
template<class T>
void BST<T>::InOrder(Node<T>* node, void(*funcPtr)(T &)) const
{
if (node != nullptr)
{
InOrder(node-> m_left, funcPtr); //recursive call for node left
funcPtr(node-> m_key);
InOrder(node->m_right, funcPtr);
}
}
template<class T>
void BST<T>::InOrder(void(*funcPtr)(T &)){
InOrder(m_root, funcPtr);
}
This line of code is called from main.cpp which pass the user input year into the map to return the bst which stores all relevant keys.
void GetData(string& year, map<string, BST<string>>& bstMap)
{
BST<string> bstKey = bstMap[year];
bstKey.InOrder(&GetTotal);
}
So here is where i am stuck..
void GetTotal(string& key) <- how do i reference my database map here?
{
cout<< key <<endl;
}
If you want to access variables outside of the BST template class (such as the map), then I advise changing your template to the following (assuming that m_root is a member variable of BST<T>, and that it is the root of tree):
template<class T, class Fn>
void BST<T>::InOrder(Fn funcPtr) const
{
InOrder(m_root, fn);
}
template<class T, class Fn>
void BST<T>::InOrder(Node<T>* node, Fn funcPtr) const
{
if (node)
{
InOrder(node-> m_left, funcPtr); //recursive call for node left
funcPtr(node-> m_key);
InOrder(node->m_right, funcPtr);
}
}
Then this way, you can pass a function object or lambda that knows about the map. In the case below, a lambda function is used:
void GetData(string& year, map<string, BST<string>>& bstMap)
{
BST<string> bstKey = bstMap[year];
bstKey.InOrder([&](std::string& key) { std::cout << bstMap[key] << "\n"; });
}
The above provides a lambda that captures the passed-in map parameter.

Accessing the left node with an iterator object in a Binary Search Tree

This is a function to find the maximum amount of left nodes. I do realize that there is already a thread for that:
Count number of left nodes in BST
but I don't want pointers in my main file. So I am trying to find a slightly different approach.
bst<int>::binTreeIterator it;
int findMax(bst<int>::binTreeIterator it)
{
int l = 0, r;
if (!(it.leftSide() == NULL)) {
l += 1 + findMax(it.leftSide());
}
if (!(it.rightSide() == NULL)) {
r = findMax(it.rightSide());
}
return l;
}
my problem is with the leftSide()/rightSide() function; How do I implement them so that it returns an iterator object that points to the left side/ right side of the iterator "it" object?
template <class Type>
typename bst<Type>::binTreeIterator bst<Type>::binTreeIterator::leftSide()
{
}
Edit:
template <class Type>
class bst
{
struct binTreeNode
{
binTreeNode * left;
binTreeNode * right;
Type item;
};
public:
class binTreeIterator
{
public:
friend class bst;
binTreeIterator();
binTreeIterator(binTreeNode*);
bool operator==(binTreeNode*);
bool operator==(binTreeIterator);
binTreeIterator rightSide();
binTreeIterator leftSide();
private:
binTreeNode * current;
};
bst();
bst(const bst<Type>&);
const bst& operator=(const bst<Type>&);
~bst();
void insert(const Type&);
void display(); // TEST
binTreeIterator begin();
binTreeIterator end();
private:
binTreeNode * insert(binTreeNode*, const Type&);
void inorder(binTreeNode*);
void destroyTree(binTreeNode*);
void cloneTree(binTreeNode*, binTreeNode*);
binTreeNode * root;
};
This very simple snipped of code should do the trick already:
template <class Type>
typename bst<Type>::binTreeIterator bst<Type>::binTreeIterator::leftSide()
{
return current->left;
}
As you did not declare the iterator's constructor explicit, it will get called automatically from the pointer to left/right returned.

LNK error 1561 with BOOST

I am trying to create an avl tree then using boost to check if what I created is working. However, in all my boost test cases I don't have a main(which I thought is the issue causing the problem). This is my avl .hpp
I'm somewhat new to c++.
template <typename T>
class avlTreeNode
{
public:
// data in the node
T nodeValue;
// pointers to the left and right children of the node
avlTreeNode<T> *left;
avlTreeNode<T> *right;
int balanceFactor;
// CONSTRUCTOR
avlTreeNode (const T& item, avlTreeNode<T> *lptr = NULL,
avlTreeNode<T> *rptr = NULL, int bal = 0):
nodeValue(item), left(lptr), right(rptr), balanceFactor(bal)
{
}
};
const int leftheavy = -1;
const int balanced = 0;
const int rightheavy = 1;
template <typename T>
class avlTree
{
public:
// CONSTRUCTORS, DESTRUCTOR, ASSIGNMENT
// constructor. initialize root to NULL and size to 0
avlTree();
~avlTree();
typedef T* iterator;
typedef T const* const_iterator;
// constructor. insert n elements from range of T* pointers4
avlTree(T *first, T *last);
// search for item. if found, return an iterator pointing
// at it in the tree; otherwise, return end()
iterator find(const T& item);
// search for item. if found, return an iterator pointing
// at it in the tree; otherwise, return end()
const_iterator find(const T& item) const;
// indicate whether the tree is empty
int empty() const;
// return the number of data items in the tree
int size() const;
// give a vertical display of the tree .
void displayTree(int maxCharacters) const;
// insert item into the tree
//std::pair<iterator, bool> insert(const T& item);
// insert a new node using the basic List operation and format
//std::pair<iterator, bool> insert(const T& item);
// delete all the nodes in the tree
void clear();
// constant versions
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
///////////////////////////////////////////////////////////
//when I created this it started giving me the lnk 1561 error
std::pair<iterator,bool> insert(const T& item)
{
avlTree<T>::iterator iter;
// quietly return if item is already in the tree
if ((iter = find(item)) != end() )
return std::pair<iterator,bool>(iter,false);
// declare AVL tree node pointers.
avlTreeNode<T> *treeNode = root,*newNode;
// flag used by AVLInsert to rebalance nodes
bool reviseBalanceFactor = false;
// get a new AVL tree node with empty pointer fields
newNode = getavlTreeNode(item,NULL,NULL);
// call recursive routine to actually insert the element
avlInsert(treeNode, newNode, reviseBalanceFactor);
// assign new values to data members root, size and current
root = treeNode;
treeSize++;
return std::pair<iterator, bool> (iterator(newNode), true);
}
private:
// pointer to tree root
avlTreeNode<T> *root;
// number of elements in the tree
int treeSize;
// allocate a new tree node and return a pointer to it
avlTreeNode<T> *getavlTreeNode(const T& item,
avlTreeNode<T> *lptr,avlTreeNode<T> *rptr);
// used by copy constructor and assignment operator
avlTreeNode<T> *copyTree(avlTreeNode<T> *t);
// delete the storage occupied by a tree node
void freeavlTreeNode(avlTreeNode<T> *p);
// used by destructor, assignment operator and clear()
void deleteTree(avlTreeNode<T> *t);
// locate a node item and its parent in tree. used by find()
avlTreeNode<T> *findNode(const T& item,
avlTreeNode<T>* & parent) const;
// member functions to insert and erase a node
void singleRotateLeft (avlTreeNode<T>* &p);
void singleRotateRight (avlTreeNode<T>* &p);
void doubleRotateLeft (avlTreeNode<T>* &p);
void doubleRotateRight (avlTreeNode<T>* &p);
void updateLeftTree (avlTreeNode<T>* &tree,
bool &reviseBalanceFactor);
void updateRightTree (avlTreeNode<T>* &tree,
bool &reviseBalanceFactor);
};
On the top of your cpp file, define BOOST_TEST_MAIN. It is used to automatically generate main function by the unit testing library.
For example:
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include "avl.hpp"
BOOST_AUTO_TEST_CASE( test )
{
// ...
}

defining iterators for a generic tree

I have this class called "Node". I've been considering renaming it "Tree", but either name makes about as much sense. This class implements a generic tree container. Each node can have any number of children. The basic header definition of the class is as follows:
template<class Elem>
class Node
{
public:
Node();
~Node();
Node(const Elem& value);
Node(const Node& rNode);
const Elem& operator*() const;
Elem& operator*();
Elem* operator->();
void operator=(const Elem& rhs);
Node* addChild(const Elem& value);
Node* addChild(Node childNode);
Node* addChild(Node* pChildNode);
HRESULT removeNode(DFSIterator<Node>& iter);
template <class Node, class List, class Iter> friend class DFSIterator;
private:
bool hasChild() const;
Node* m_pParentNode;
Elem m_value;
std::vector<Node*> m_childList;
static std::set<Node*> sNodeSet;
};
The header definition of my DFSIterator is:
template<class Item,
class List = std::vector<Item*>,
class Iter = typename std::vector<Item*>::iterator>
class DFSIterator
{
public:
DFSIterator(Item& rRootNode);
~DFSIterator();
DFSIterator* begin();
DFSIterator* operator++();
Item& operator*() const;
Item* operator->() const;
bool operator!=(const DFSIterator& rhs) const;
bool isDone() const;
operator bool() const {return !isDone();}
private:
template <class Node> friend class Node;
void initChildListIterator(Item* currentNode);
bool m_bIsDone;
Item* m_pRootNode;
Item* m_pCurrentNode;
ChildListIterator<Item>* m_pCurrentListIter;
std::map<Item*, ChildListIterator<Item, List, Iter>*> m_listMap;
};
Item is the iterator's alias for Node<Elem>.
The problem I am having is that I want to define iterators for this tree that the user can declare in a similar way to STL containers. I was thinking that putting typedef statements like typedef DFSIterator<Node<Elem>> dfs_iterator; would work fine. But whenever I add those statements into the header, I get the following error error C2512<Item>: no appropriate default constructor available. Wherever I try to go and use it.
So right now, to declare an iterator I have to do something like DFSIterator<Node<DataMap>> dfsIter = rRootNode.begin(); or DFSIterator<Node<DataMap>> dfsIter(rNode); if I don't want to start at the root node of the tree. What I want to be able to do is something more like Node<DataMap>::dfs_iterator it = rRootNode.begin(). Is there a way to do this that I am missing?
Note: I do want to change a few other things about this implementation. I don't really want the user to be passing a node element to the addChild() method. I'd rather have the user pass an iterator that is pointing to a node.
If you define dfs_iterator inside Node, then you can use it basically like you describe:
template<class Elem>
class Node
{
public:
typedef Node<Elem> Item;
template<
class List = std::vector<Item*>,
class Iter = typename std::vector<Item*>::iterator
> class dfs_iterator;
.
.
.
};
template<class Elem>
template<class List, class Iter>
class Node<Elem>::dfs_iterator
{
public:
.
.
.
};
and use
Node<DataMap>::dfs_iterator<> it = rRootNode.begin();
The only difference is that since dfs_iterator is a template, you have to specify the template parameters, even though they both can be defaulted.

Sorting vector of pointers

I'm having a little trouble trying to sort a vector of pointers.
This is what I have done so far:
class Node
{
private:
vector <Node*> _children;
string _data;
...
public:
void Node::add_child(Node* child)
{
...
sort(_children.begin(), _children.end());
}
bool Node::operator<(const Node& node)
{
return (this->_data.compare(node._data) == -1);
}
};
My less-than operator works, if I write like this:
Node* root = new Node("abc");
Node* n = new Node("def");
cout << (*root<*n) << endl;
Why does sort never call the operator?? Any help would be appreciated!
Thanks.
madshov
Because you sort the pointer values, not the Nodes they point to.
You can use the third argument of the std::sort algorithm to specify a custom comparator.
For example :
bool comparePtrToNode(Node* a, Node* b) { return (*a < *b); }
std::sort(_children.begin(), _children.end(), comparePtrToNode);
(note that this code is just an indication - you'll have to add extra safety checks where needed)
Your less-than operator takes const Node& arguments, but your vector is sorting Node*s. You need to specify a comparison function as the third parameter to std::sort.
class Node
{
private:
vector <Node*> _children;
string _data;
struct PointerCompare {
bool operator()(const Node* l, const Node* r) {
return *l < *r;
}
};
public:
void add_child(Node* child)
{
sort(_children.begin(), _children.end(), PointerCompare());
}
bool operator<(const Node& node) const
{
return (this->_data.compare(node._data) == -1);
}
};
Also, your operator< needs to be declared const.
Your operator<() operates on references to Node objects; but the vector contains pointers to Node objects, which can't be compared with that function. You'll have to explicitly supply a proper function (one that accepts pointers as arguments) to the sort() algorithm.