Skip to content

Instantly share code, notes, and snippets.

@DBJDBJ
Last active August 29, 2017 15:21
Show Gist options
  • Save DBJDBJ/b8920ffc1da2655c350ca113e6f647e5 to your computer and use it in GitHub Desktop.
Save DBJDBJ/b8920ffc1da2655c350ca113e6f647e5 to your computer and use it in GitHub Desktop.
Binary Tree made more useful, the easy and quick way thanks to modern C++ (c) 2017 by [email protected]
// IDE ONE link: https://ideone.com/X30oXu
#pragma once
#include <iostream>
using std::wostream;
namespace dbj {
namespace tree {
using namespace std;
/*
Binary Tree made more useful, the easy and quick way thanks to modern C++
(c) 2017 by [email protected]
*/
/*
each type T stored in a node has to have its releasing function
this will obviously be a policy pattern
*/
template<typename T> void data_destroyer(T data) { data.~T(); }
/*
T is data type stored
*/
template<typename T>
struct Node {
T data;
struct Node *left;
struct Node *right;
Node() {
this->data = T{} ;
this->left = this->right = nullptr;
}
typedef std::unique_ptr<Node<T>> NodeP;
NodeP smart () {
return std::make_unique<Node<T>>(
reinterpret_cast<Node<T> &>(*this)
);
}
/*
reminder: delete will call Node destructor
*/
~Node() {
if (this->data)
data_destroyer(this->data);
if (this->left)
delete this->left;
if (this->right)
delete this->right;
}
/*
type T requires well behaved swap
*/
static Node * root_creator( T data ) {
Node * root = new Node();
std::swap(root->data, data);
return root;
}
/*
node insertion method creates balanced tree by default
it uses operator '<=' on the data type used and stored
it also uses Node<T>::root_creator() to create the node
*/
static Node * insert(Node * root, const T & data) {
if (root == nullptr) {
root = root_creator(data);
}
else if (data <= root->data)
root->left = insert(root->left, data);
else
root->right = insert(root->right, data);
return root;
}
};
//Function to visit nodes in preorder
template<typename T, typename F>
void preorder(struct Node<T> *root, F f) {
// base condition for recursion
// if tree/sub-tree is empty, return and exit
if (root == nullptr) return;
f(root); // Print data
preorder(root->left, f); // Visit left subtree
preorder(root->right, f); // Visit right subtree
}
//Function to visit nodes in inorder
template<typename T, typename F>
void inorder(Node<T> *root, F f) {
if (root == nullptr) return;
inorder(root->left, f); //Visit left subtree
f(root); // Print data
inorder(root->right, f); // Visit right subtree
}
//Function to visit nodes in postorder
template<typename T, typename F>
void postorder(Node<T> *root, F f) {
if (root == nullptr) return;
postorder(root->left, f); // Visit left subtree
postorder(root->right, f); // Visit right subtree
f(root); // Print data
}
/*
Test on an example tree of Node<char>
0
/ \
1 2
/ \ \
3 4 5
*/
void test(std::wostream & wos ) {
const wchar_t & nl = { L'\n' };
typedef Node<char> NodeT;
typedef void(*Node_printer) (NodeT *);
/* tree imp operates with naked pointers
that makes it more obvious what is goin on
*/
NodeT * root{} ;
/*
this lambda is our processor
it can be any lambda, function or functor that receives pointer to Node<> instance
and returns void.
That means in this implementation the whole tree will be always visited. There is no
retval from the processor to stop the processing.
*/
auto printF = [](NodeT * x) { printf("%c ", x->data); };
/*root = NodeT::insert(root, '0'); */
root = NodeT::insert(root, '0'); root = NodeT::insert(root, '1');
root = NodeT::insert(root, '3'); root = NodeT::insert(root, '2');
root = NodeT::insert(root, '5'); root = NodeT::insert(root, '4');
NodeT::NodeP smart = root->smart();
wos << nl << L"Process then visit left and right: ";
preorder(root, printF);
wos << nl;
wos << nl << L"Visit left, Process then visit right: ";
inorder(root, printF);
wos << nl;
wos << nl << L"Visit left, visit right then Process: ";
postorder(root, printF);
wos << nl;
// after this point smart_root deletes the tree;
}
} // tree
} // dbj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment