B+ Tree node implementation - c++

Im implementing a B+Tree for a class. The Nodes are currently implemented like this:
class Node {
public:
E* keys[order*2];
Node *children[order*2+1];
int size;
Node(){
size = 0;
}
bool empty() {
return size == 0;
}
bool isLeafNode() {
return false;
}
};
class LeafNode : public Node {
public:
E* data[order*2+1];
bool isLeafNode() {
return true;
}
};
When I want to add an element to a leaf node (by accessing LeafNode->data), I get
error: request for member ‘data’ in ‘left<int>’, which is of non-class type ‘BTree<int>::LeafNode*()’
I guess this happens because the compiler doesn't know whether the Node I'm accessing is an inner- or leaf-node, although I'm checking it first by using isLeafNode().
I can't merge the two classes into one, because the Leaf Nodes need one more Bucket for the data than the inner nodes.
I realize this is sort of a design-question, but is there some trivial approach to this problem that I'm missing? I'm fairly new to C++.

You really should use a virtual method for something like this. You can change your isLeafNode() query to return a pointer to the leaf node if it is one, and NULL otherwise.
class LeafNode; // forward declare
class Node {
//...
public:
virtual ~Node () {}
virtual LeafNode * isLeafNode () { return 0; }
//...
};
class LeafNode : public Node {
//...
public:
LeafNode * isLeafNode () { return this; }
//...
};
Then, you can use this method from a Node to access the data if it is actually a LeafNode.

The error message
error: request for member ‘data’ in ‘left<int>’, which is of non-class type ‘BTree<int>::LeafNode*()’
and other errors of this form usually mean that you are trying to access a field of a struct using a . when you should be using ->. For example, if you have
LeafNode* ptr = /* ... */;
ptr.data[0] = /* ... */;
You will get an error on the second line, because you are using . instead of ->.
Try seeing if this is the error you're having on the indicating line and, if so, change the dot to an arrow.
Hope this helps!

Related

AVL-tree clearing

I don't know, why _clear don't remove elements from a tree. Probably, needs to override destructor.
class Node {
T _val;
Node *_left;
Node *_right;
int _height;
explicit Node(T _val = T()) : _val(_val), _left(nullptr), _right(nullptr), _height(1) {};
friend class AVL_Tree<T>;
};
void AVL_Tree<T>::_clear(Node<T>*vertex) {
if(vertex != nullptr) {
_clear(vertex->_left);
_clear(vertex->_right);
delete vertex;
}
vertex = nullptr;
}
There are stay NULL linked nodes, why they don't delete?
The problem is here:
AVL_Tree<T>::_clear(Node<T>*vertex)
You passed a pointer to Node by value, but you want to change the pointer and have the change propagate out of the function. The C++ way to do this is pass a reference to the pointer-to-Node:
AVL_Tree<T>::_clear(Node<T>*&vertex)
PS. Don't begin an identifier with an underscore. That's reserved for library implementers. So:
AVL_Tree<T>::clear(Node<T>*&vertex)

Modifying private pointer of object within same type (but different object) public method

I've been attempting to create a node class which mimics a node on a graph. Currently, storage of the predecessor and successor nodes are stored via a node pointer vector: std::vector<Node*> previous. The vectors for the predecessor/successor nodes are private variables and are accessible via setters/getters.
Currently, I am dealing with updating the pointer values when adding a new node. My current method to update the predecessor/successor nodes is through this method (the method is the same for successor/previous nodes, just name changes):
void set_next(std::vector<Node*> new_next) {
this->next.clear();
for (Node* node : new_next) {
this->next.push_back(node);
}
}
This works for the current node but I was wondering the best way to update the new_next nodes that are passed in, especially the most 'C++'-esque way to accomplish this. I have previously written a method which adds individual nodes to the successor/predecessor vector:
void add_next(Node* new_node, bool one_way = false) {
this->next.pushback(new_node);
if (!one_way) {
new_node->add_prev(this, one_way = true);
}
}
The one_way variable was used to determine the depth(?) of the add_next() call. If it is true, it will add to the successor set and then add to the predecessor set of new_node. Since the boolean value is set to false when the new_node->add_next() method call occurs, it will only add to the predecessor set and not attempt to call add_next()/add_prev() again. This solution does work, but I'd rather not have the one_way variable and would prefer that the method would be private.
Here is the structure of the class currently:
class Node {
private:
std::vector<Node*> previous;
std::vector<Node*> next;
boost::any data;
public:
std::vector<Node*> get_previous()
void set_previous(std::vector<Node*> new_previous)
std::vector<Node*> get_next()
void set_next(std::vector<Node*> new_next)
void add_prev(Node* new_node, bool one_way = false)
void add_next(Node* new_node, bool one_way = false)
}
Avoiding the one_way parameter, it seems my best solution would be to just create an add_next/add_prev method that only updates the current node, not the passed node, unlike the solution above. With this solution, when adding new nodes, I could call the inverse of the add_next/add_prev on the new node. However, I have an inkling there may be a better solution for this.
Thank you!
I think this should get you going (edge-cases left to you to figure out, if any):
template<typename T>
class Node {
// Everything made public for debugging purposes, change this to fit your needs
public:
std::vector<Node<T>*> previous;
std::vector<Node<T>*> next;
T data;
Node(T val) {
data = val;
}
void set_next(std::vector<Node<T>*>& new_next);
};
template<typename T>
void Node<T>::set_next(std::vector<Node<T>*>& new_next) {
next = new_next;
for (Node<T>* node : new_next)
node->previous.push_back(this);
}
int main() {
// Little proof of concept where 0 is parent to 1, 2, 3
Node<int> one = 1;
Node<int> two = 2;
Node<int> three = 3;
Node<int> zero = 0;
std::vector<Node<int>*> new_next = { &one , &two, &three };
zero.set_next(new_next);
return 0;
}
To sum up the differences:
Use templates, boost::any is non standard and is all around terrible for this task.
Leverage operators (= makes a copy of std::vectors).
Leverage reference types (this way you can modify the argument passed to your function).

Object not instantiating properly

I am currently using VS2015 for this.
I am trying to create a binary search tree in c++ so that I can learn both the language and the data structure while trying to see if I can follow good practices. However, I am coming through a problem where I am not properly instantiating the object properly in the driver file.
BSTHeader.h
#pragma once
/*
Properties of Binary Search Tree:
1.) Elements less than root will go to the left child of root
2.) Elements greater than root will go to the right child of root
*/
#include <memory>
// Binary Search Tree handler class
class BSTHeader {
/*
Naive implementation of BSTNode (non-generic version)
Nested class is private, but it's internal fields and member functions
are public to outer class: BSTHeader
*/
class BSTNode {
public:
int data;
std::unique_ptr<BSTNode> left;
std::unique_ptr<BSTNode> right;
BSTNode(int val) {
data = val;
left = NULL;
right = NULL;
}
~BSTNode() {}
};
std::unique_ptr<BSTNode> root; // Root of BST
unsigned int size; // Total amount of nodes in tree from root
public:
BSTHeader();
BSTHeader(int val);
~BSTHeader();
bool insert(std::unique_ptr<BSTNode>& root, int val);
}
BSTHeader.cpp
#include "BSTHeader.h"
/*
Constructors:
*/
BSTHeader::BSTHeader() {
root = NULL;
size = 0;
}
BSTHeader::BSTHeader(int val) {
root = std::unique_ptr<BSTNode>(new BSTHeader::BSTNode(val)); // Smart pointer to an internal BSTNode
size = 1;
}
BSTHeader::~BSTHeader() {} // Empty destructor from use of smart pointer
/*
Member functions:
*/
bool BSTHeader::insert(std::unique_ptr<BSTNode>& root, int val) {
if (root == NULL) { // Place new element here
root = std::unique_ptr<BSTNode>(new BSTHeader::BSTNode(val));
size++;
return true;
}
if (val < root.get()->data) { // val < root
insert(root.get()->left, val);
}
else if (val > root.get()->data) { // val > root
insert(root.get()->right, val);
}
The issue I get is here, where I believe I am trying to instantiate a BSTHeader object.
Program.cpp
#include "BSTHeader.h"
int main()
{
BSTHeader::BSTHeader bst(); // <----- ERROR
return 0;
}
The error I am getting is cannot determine which instance of overloaded function "BSTHeader:BSTHeader" is intended
However, whenever I do:
BSTHeader bst()
I am not able to access the insert(..., ...) function for the object doing bst.insert(..., ...) due to expression must have class type even though the error above does not appear.
Yet everything works fine and I am able to access all the member methods by doing this: BSTHeader bst(5) by using the overloaded constructor.
I am not sure whether its a namespace issue or not. I feel as though I am missing something.
The line
BSTHeader::BSTHeader bst(); // <----- ERROR
is a declaration of a function named bst that takes no arguments and returns a BSTHeader::BSTHeader.
This is known as the "most vexing parse", and often described in less polite language.
If you want to instantiate an instance, giving the constructor no arguments, remove the ().

Changing where a function's behavior is described is causing an error?

I have the following class declaration (I've tried to remove as much excess code as possible):
class List {
public:
struct Node {
int value;
};
Node * findNode(unsigned int) {
return new Node;
}
};
This gives no error. However, as soon as I define the function "findNode" outside of the class, I get an error; here's the code:
class List {
public:
struct Node {
int value;
};
Node * findNode(unsigned int);
};
Node * List::findNode(unsigned int index) {
return new Node;
}
Now, when running the code, I get an error saying "LinkedList.cpp:9:1: error: 'Node' does not name a type".
I would appreciate any help in determining the problem.
Until the compiler encounters List:: it has no idea the Node you're talking about is a member of List. Change the definition to:
List::Node * List::findNode(unsigned int index) {
return new Node;
}
The "naked" Node inside the function is fine because by that time the compiler knows the function is a member of List.

Can I use a member element as the default argument for a method of the class?

The method Minimum returns the minimum element in the binary search tree. If no argument is passed it prints the minimum of calling object. If address of a node is passed it prints the minimum of the subtree whose root is node
When compiled it shows "Invalid use of non static data member Tree::root"
#include<stdlib.h>
#include<iostream>
class Node
{
public:
Node *leftchild;
Node *rightchild;
Node *parent;
int info;
};
class Tree
{
public:
Node *root;
Tree()
{
root=NULL;
}
void Minimum(Node*);
};
void Tree::Minimum(Node *curnode=root)
{
Node *parent;
while(curnode!=NULL)
{
parent=curnode;
curnode=curnode->leftchild;
}
std::cout<<parent->info<<endl;
}
int main()
{
Tree tree;
tree.Minimum();
return 0;
}
No, you cannot.
For the default value you can use either a value, a variable or a function that is accessible in the context of the function definition that is, in the class definition, which is outside of any particular object's context.
It usually helps me thinking on how the compiler really processes this. In particular, when the compiler does overload resolution for a function and finds an overload that has more arguments than those used at the place of call, the compiler will generate code at the place of call to fill in the rest of the arguments. The generated code will always generate a call with all of the arguments:
int g();
void f(int x = g());
int main() {
f(); // [1]
}
When the compiler processes [1] and it does overload resolution it finds that void ::f(int x = g()) is the best candidate and picks it up. It then fills the default argument and generates the call for you:
int main() {
f( /*compiler injected*/g() );
}
If you consider a call to a member function, or a member variable of the class, it would not make sense in the context of the caller (the language could be changed to adapt to this, it is not impossible to handle that, but with the current model it does not work).
You can alternatively set it to NULL for example as default, and then check and set it to the member in the method.
Or overload the method with void Minimum(); and in that method call the one with an argument with the member.
void Tree::Minimum() {
Minimum(root);
}
I couldn't find any way to get the default parameter to work like that. But you can get the same result by overloading the function, like so:
class Tree
{
public:
Node *root;
Tree()
{
root=NULL;
}
void Minimum(Node*);
void Minimum();
};
void Tree::Minimum(Node *curnode)
{
Node *parent;
while(curnode!=NULL)
{
parent=curnode;
curnode=curnode->leftchild;
}
std::cout<<parent->info<<std::endl;
}
void Tree::Minimum()
{
Minimum(root);
}
If the case where an argument of NULL is explicitly passed doesn't need to be differentiated from no argument being passed, you could set NULL as the default and use root if curnode is NULL.
void Tree::Minimum(Node *curnode=NULL)
{
if (curnode==NULL)
curnode = root;
Node *parent;
while(curnode!=NULL)
{
parent=curnode;
curnode=curnode->leftchild;
}
std::cout<<parent->info<<endl;
}