Let's suppose we have a RedBlack-Tree implementation which consists of 2 classes:
Tree - holds the pointer to the Node *root of the tree and defines all operations over the tree (Insert, Delete, etc)
Node - a data storage, which holds pointers to Node *parent, Node *left, Node *right nodes and std::string key.
The Tree::Insert() has the following implementation:
void Tree::Insert(const std::string &key)
{
Node *z = new Node(key);
// adding node logic
}
Now the task: every node has to store the time of its creation.
Limitations: the base tree implementation should be modified as less as possible and should contain details of specific extensions (so it should know nothing about the creation time property).
My thoughts: extending NodeWithTime : Node and adding unsigned int creation_time property.
Where I'm in stuck: how would we instantiate the node now?
Any proposals?
PS: it's neither a homework or a job task - I'm just learning c++ and data structures.
It's relatively simple. First, the Node struct:
template<typename T> struct Node {
Node(T t) : value(std::move(t)), time(RightNow()) {}
T value;
TimeType time;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};
A quick helper make_unique:
template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args...)));
}
template<typename T> void Tree<T>::Insert(T key) {
auto z = make_unique<Node<T>>(std::move(key));
// insert
}
First, I fixed your crappy new and delete and replaced it with smart pointers. Then I also made your tree a template because who needs a tree that can only do one type? Then I swapped out your const T& with a T so that it might live with move-only types.
Then I just added a Time field and called RightNow() in the constructor. The exact TimeType and RightNow() you use depends on your needs and what exactly you mean by "time of it's creation". Are we talking about "6th July, 2013"? Or a very-high-resolution clock? In any case, these "creation time" details do not impact the tree.
Edit: Wait, you want to have one tree type where only some of the nodes know the creation time? Or just to alter the tree so that all the nodes know the creation time? I did #2, but for #1, you could indeed simply inherit from Node. To wit,
template<typename T> struct Node {
Node(T t) : value(std::move(t)) {}
T value;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};
template<typename T> struct NodeWithTime : Node<T> {
TimeType time;
NodeWithTime(T t) : Node(std::move(t)), time(RightNow()) {}
};
template<typename T> void Tree<T>::insert(T t) {
std::unique_ptr<Node> nodeptr;
if (IWantToStoreCreationTime)
nodeptr = make_unique<NodeWithTime<T>>(std::move(t));
else
nodeptr = make_unique<Node>(std::move(t));
// insert
}
Related
I'm learning C++ with some exercises from a book that I found a while ago. My task is described below and I've tried to find a work around for returning a template node I created for the template function getLastNode to add a node at the end of the list. Is it possible to do that, currently I can't find a way to let's call it explain the compiler what TNode is as a struct within a class return value.
I might have the best way to declare nodes in this code. Perhaps, a struct within a class can complicate class template methods implementations. Do you feel there's another strategy? Please let me know
Cheers!
/* Implement the data structure dynamic doubly linked list (DoublyLinkedList<T>) - list,
* the elements of which have pointers both to the next and the previous elements. Implement
* the operations for adding, removing and searching for an element, as well as inserting
* an element at a given index, retrieving an element by a given index and a method, which returns an array with the elements of the list*/
#include <iostream>
template<typename TValue>
class List{
struct TNode{
TValue value;
TNode *previous;
TNode *next;
}Node;
public:
List();
~List();
void addNode(TValue);
private:
TNode *root;
TNode getLastNode(TNode);
};
template<typename TValue>
List<TValue>::List():root(0) {}
template<typename TValue>
List<TValue>::~List<TValue>(){
}
template<typename TValue>
TNode List<TValue>::getLastNode(TNode node){
if(node.next==nullptr)
return node;
else
getLastNode(node.next);
}
template<typename TValue>
void List<TValue>::addNode(TValue value){
const TNode last = getLastNode(root);
last.next = Node;
last.next->value = value;
}
int main(){
List<int> test;
return 0;
}
To return TNode for the getLastNode method I had to add auto to its class method declaration.
Credits: #JaMiT
template<typename TValue>
auto List<TValue>::getLastNode(TNode node){
if(node.next==nullptr)
return node;
else
getLastNode(node.next);
}
I am studying generic binary search trees (BST) and AVL trees (AVL) on some notes that contain implementation pseudocodes. I am a bit puzzled about some details of their implementation.
The BST is based on the struct Node below
struct Node{
int key;
Node* parent;
Node* left;
Node* right;
//constructors
}
//methods
The AVL version is basically the same with a few fields more for balancing the tree (I'll call it AVLNode for clarity, but there's no such distinction on the notes):
struct AVLNode{
int key;
int height;
int size;
AVLNode* parent;
AVLNode* leftchild;
AVLNode* rightchild;
//constructors
}
//methods
A lot of operations are the same between the two trees and I can easily use templates in order to reuse them on both trees. However, consider the operation insert, which inserts a new node. The code for a BST is something like
//Insert node with key k in tree with root R
void insert(const int& k, Node* root){
Node* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->leftchild=new Node(k,N); //inserts as a left child
else
N->rightchild=new Node(k,N); //inserts as a right child
}
Now, the point is that the insert operation of an AVL tree is basically the same. The pseudocode presented in the notes is as follows:
void avlInsert(int k, AVLNode* R){
insert(k,R); //same operations as for Nodes, shown above
AVLNode* N=find(x,R); //find node inserted (generic operation for BST)
rebalance(N); //perform balancing operations specific to AVL trees
}
I'm a bit puzzled at this point, I know that the above is just a pseudocode but I was wondering whether there is a way to reuse the operation insert already provided for Node. Using template specialization would just mean writing a different specialization insert<AVLNode> for AVLNode, so that's not what I'm referring to.
I think a way would be to define AVLNode as a child class of Node and then use something like
struct AVLNode : Node {
//implementation
}
void avlInsert(int k, AVLNode* R){
Node *root=R;
insert(k,root);
AVLNode* N=find(x,R);
rebalance(N);
}
but I'm not quite sure this would work and I don't know how to manage the pointers to parent and the childs (i.e. they must be pointers to Node inside Node and to AVLNode inside AVLNode).
Is there a way to avoid rewriting the same code?
You could use CRTP here. This would allow you to create the left right and parent nodes in the baseclass. For example consider something like this:
template<typename T>
struct BaseNode{
int key;
T* parent;
T* left;
T* right;
};
struct AVLNode : public BaseNode<AVLNode>{
int height;
int size;
AVLNode(const int&k, AVLNode*root){};
AVLNode(){};
};
struct Node : public BaseNode<Node>{
Node(const int&k, Node*root){};
Node(){};
};
template<typename T>
T* find(const int& k, T* root){return root;};
template<typename T>
void insert(const int& k, T* root){
T* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->left=new T(k,N); //inserts as a left child
else
N->right=new T(k,N); //inserts as a right child
}
void test(){
AVLNode avl_root;
Node node_root;
insert(42, &avl_root);
insert(42, &node_root);
}
The downside is that the compiler will generate more code than necessary. Because it creates a new insert function for every type. This might not be a problem for you, but something worth considering. See godbolt for the generated code.
As an aside. Please please please please don't use raw pointers and new and delete. You'll be going to get so many memory leaks, especially if a pointer gets "lost" because its parent gets deleted. Consider using smart pointers like unique_ptr or shared_ptr
I know there are many questions about that issue but nothing seems to work for me or it's too complex for me to understand.
So I have template Node
template <typename T>
class Node {
public:
T value;
Node* right;
Node* left;
Node(T value, Node<T>* right, Node<T>* left);
};
And Tree
template <typename T>
class Tree {
public:
Node<T>* root;
Tree();
~Tree() {}
void insert(T value);
Node<T>* search(T value, Node<T>* root) noexcept(false);
};
Now I want to create different Tree templates basing on what the user chose. Users can choose int, double, or string, these are the only options. I tried to use base class solution but my problem is that Tree uses type T in functions (and in Node) so I don't know how I should declare them in, let's call it BaseTree. Then I would be able to something like this:
BaseTree* tree;
tree = new Tree<int>();
I'm looking for a simple solution, I'm sort of beginner and it surprises me how this simple issue is so difficult for me to solve.
The solution is to templatize the generic functions also. Let's say you have something like this in mind:
void traverse(BaseTree const& tree) noexcept {
/* Logic here */
}
Instead, you do:
template<typename T>
void traverse(Tree<T> const& tree) noexcept {
/* Logic here */
}
This also helps that you can use Node<T> to refer to the node instead of fabricating BaseNode and whatnot again.
Trying to make a B inary S earch T ree (BST for short) using a template.
When I try to create a new instance of my BST I get an unexpected error. I hope the solution does not involve pointers since I would like to keep them at a minimum.
For now I have:
template <typename Type>
class BST { // The binary search tree containing nodes
private:
BSTNode<Type> *root; // Has reference to root node
public:
BST ();
bool add (int, Type);
};
And the Node type:
EDIT: When I cut out code to un-encumber text, I forgot the constructor, now it's been added
template <typename Type>
class BSTNode { // Binary Search Tree nodes
private:
int key; // we search by key, no matter what type of data we have
Type data;
BSTNode *left;
BSTNode *right;
public:
BSTNode (int, Type&);
bool add (int, Type);
};
EDIT2: Here is the actual constructor
template <typename Type>
BSTNode<Type>::BSTNode (int initKey, Type &initData) {
this->key = initKey;
this->data = initData;
this->left = NULL;
this->right = NULL;
}
I want to try and test if anything works / doesn't work
BSTNode<int> data = new BSTNode (key, 10);
And I get: Expected type specifier before BSTNode. I have no idea what I'm doing wrong, but one thing I do hope is I don't have to use data as a pointer.
BSTNode<int> data = new BSTNode<int> (key, 10);
Also does not work, seems it believes < int > is < & int> and it doesn't match
First, you need to fully specify the type on the RHS of the assignment, and, since you are instantiating a dynamically allocated node with new, the LHS should be a pointer:
BSTNode<int>* data = new BSTNode<int> (key, 10);
^ ^
If you don't need a node pointer, then use
BSTNode<int> data(key, 10);
Second, your BSTNode<T> class doesn't have a constructor taking an int and a Type, so you need to provide that too.
template <typename Type>
class BSTNode {
public:
BSTNode(int k, const Type& val) : key(k), data(val), left(0), right(0) { .... }
};
I have one semestral work (own double linked list) and our teacher want this definition of class DoubleList:
template <typename T> //just part of all methods
class DoubleList {
public:
DoubleList(void); //We HAVE TO follow this definitions
void AddFirst(const T &); //const!
T &AccessActual(void);
T RemoveFirst(void);
}
My question is, how can I define a node? AddFirst have const argument and other methods haven't. Data must be set in constructor and then they can't be changed. Is this task so limited or are here other ways to complete the task?
Here is my actual Node:
template <class U>
class Node{
Node<U> * next;
Node<U> * previous;
const U * data;
public:
Node(const U *data){ //
next = NULL;
previous = NULL;
this->data = data;
}
void SetNext(Node<U> *next) {
this->next = next;
}
Node<U> *GetNext(){ return next; }
void SetPrevious(Node<U> *previous) {
this->previous = previous;
}
Node<U> *GetPrevious(){ return previous; }
const U *GetData() { return data; }
};
In containers, it's usually better to have a copy of the data so change const U * data; to U data;
The Node constructor would be easier to use if it had this signature Node(const U& data). No pointers.
The GetData would also have to change. Return a reference. U& GetData().
It is dangerous to hold addresses of data items. If the user of the lists wants that functionality he can use a list that stored pointers (e.g. U=int*)
Your node class seems fine, although i would keep using template argument T instead of U, right now it is confusing.
Your AddFirst() method should simply create a new node and assign the correct next pointer to the new node and the correct prev pointer to the "old" first node and adjust the actual object? what does that refer to?
Your interface of nodes differs from this one returning a reference instead of a pointer. I find it quite strange that the AccessActual can always return an object, while when the list is empty this can be a nullptr??
example implementation:
void AddFirst(const T &)
{
Node<T>* newNode = new Node<T>(T);
Node<T>* current = &AccessActual(); // how can there be an actual when the list can be empty or is that impossible?
{
while( current.GetPrev() != nullptr )
{
current = *current.GetPrev();
}
current.SetPrev(newnode);
newnode->SetNext(current);
}
}