How to compute std::hash<> for a binary-tree data structure? - c++11

I was thinking to store trees in an std::unordered_set in order to pick the unique trees from a collection of trees. If my tree node has representation like described below, how to come up with the hash function for that?
struct node {
int val;
node *left;
node *right;
};
Edit: By similar tree, I mean that they have the same values in all the respective nodes. So, compare function will be using the actual trees to compare rather than memory address of root.

If you can use boost, you can use its boost::hash_combine function to mix the hash values. Otherwise, you can write a simple function to do that. You can see a simple recursive function to compute the hash of a tree.
struct node
{
int val;
node *left;
node *right;
};
template <typename T>
size_t hash_combine(std::size_t s, const T & n)
{
s ^= std::hash<T>()(n) + 0x9e3779b9 + (s << 6) + (s >> 2);
return s;
}
size_t get_hash(const node & n)
{
if ((n.left == nullptr) && (n.right == nullptr))
{
return std::hash<int>()(n.val);
}
else if (n.left == nullptr)
{
return hash_combine(get_hash(*n.right), std::hash<int>()(n.val));
}
else if (n.right == nullptr)
{
return hash_combine(get_hash(*n.left), std::hash<int>()(n.val));
}
else
{
size_t s = std::hash<int>()(n.val);
s = hash_combine(s, get_hash(*n.right));
s = hash_combine(s, get_hash(*n.left));
return s;
}
}
namespace std
{
template <>
struct hash<node>
{
std::size_t operator()(const node & n) const
{
return ::get_hash(n);
}
};
} // std

Related

How to add a node in Binary search tree?

struct node {
int data{};
node *right{nullptr};
node *left{nullptr};
};
class BTree {
private:
node *root;
void insert(node *sr, int num);
public:
BTree();
void buildTree(int num);
};
void BTree::insert(node *sr, int num) {
if (sr == nullptr) {
sr = new node;
sr->data = num;
} else {
if (num < sr->data)
insert(sr->left, num);
else
insert(sr->right, num);
}
}
int main() {
BTree tree;
tree.buildTree(3);
return 0;
}
I am using the above insert method to add a node to Binary Search Tree. But this method is unable to add the node , if i add a number as its root or first node the root remains nullptr.
How do i resolve this issue.
At the first root is nullptr , and I am sending root i.e the nullptr as the argument. As nullptr does refer to any node therefor the root is not getting updated by the operation in the method.
possible solution:
use pointer to pointer, so that address of the node can be passed and and changed.
directly acces the root to do changes.
PROTOTYPE
void insert(node **sr, int num);
BUILD TREE METHOD
void BTree::buildTree(int num) {
insert(&root, num);
}
INSERT METHOD
void BTree::insert(node **sr, int num) {
if (*sr == nullptr) {
*sr = new node;
(*sr)->data = num;
} else {
if (num < (*sr)->data)
insert(&((*sr)->left), num);
else
insert(&((*sr)->right), num);
}
}

How to create a binary search tree for complex type that allows duplicates in C++?

I have read many posts on BST and duplicates and I understand it isn't very possible / no clean way to allow duplicates especially for a complex type which I am using. So I need some help on how / if it's possible to implement a BST with duplicates with my scenario.
My scenario:
I am using a Transaction class as my node keys, and the main data I am comparing is the 'amount' in the transaction class, so my binary search tree can allow you to enter an amount and output any transactions with its 'toString()' function to the user, that matches the search amount. However, now I face the issue that I won't be able to have a duplicate transaction amount. How could I resolve this? Could anyone provide an example? thank you.
Code to reproduce the problem to solve:
#include<iostream>
using namespace std;
#include <algorithm>
#include <cctype>
#include <string>
#include <memory>
// Complex type used for the BST
class Transaction
{
private:
std::string desc;
time_t timestamp;
std::string value;
bool isWithdrawal;
public:
Transaction(const std::string& value, std::string reason = "None.")
: desc(reason), timestamp(time(nullptr)), value(value) { // timestamp is current date/time based on current system
// Lambda to convert reason to lower to we can identify elements easier
std::transform(reason.begin(), reason.end(), reason.begin(),
[](unsigned char c) { return std::tolower(c); });
this->isWithdrawal = (reason.find("withdrawal") != std::string::npos) ? true : false;
}
std::string toString() const {
// convert timestamp to string form
const char* string_timestamp = ctime(&timestamp);
if(this->isWithdrawal) { return "-- " + desc + ": -£" + value + " on " + string_timestamp;}
else {return "-- " + desc + ": £" + value + " on " + string_timestamp;}
}
// Gets the amount, converts it to a double and returns it
double getAmount() const {
return std::stod(this->value);
}
};
// The binary search tree implementation
class BST {
struct node {
std::shared_ptr<Transaction> data;
node* left;
node* right;
};
node* root;
node* makeEmpty(node* t) {
if(t == NULL)
return NULL;
{
makeEmpty(t->left);
makeEmpty(t->right);
delete t;
}
return NULL;
}
node* insert(std::shared_ptr<Transaction> x, node* t)
{
if(t == NULL)
{
t = new node;
t->data = x;
t->left = t->right = NULL;
}
else if(x->getAmount() < t->data->getAmount())
t->left = insert(x, t->left);
else if(x->getAmount() > t->data->getAmount())
t->right = insert(x, t->right);
return t;
}
node* findMin(node* t)
{
if(t == NULL)
return NULL;
else if(t->left == NULL)
return t;
else
return findMin(t->left);
}
node* findMax(node* t) {
if(t == NULL)
return NULL;
else if(t->right == NULL)
return t;
else
return findMax(t->right);
}
void inorder(node* t) {
if(t == NULL)
return;
inorder(t->left);
cout << t->data->getAmount() << " ";
inorder(t->right);
}
node* find(node* t, double x) {
if(t == NULL)
return NULL;
else if(x < t->data->getAmount())
return find(t->left, x);
else if(x > t->data->getAmount())
return find(t->right, x);
else
return t;
}
public:
BST() {
root = NULL;
}
~BST() {
root = makeEmpty(root);
}
void insert(std::shared_ptr<Transaction> x) {
root = insert(x, root);
}
void display() {
inorder(root);
cout << endl;
}
std::string search(double x) {
node* result = find(root, x);
if(result != NULL) { return result->data->toString(); }
else { return "N/A"; }
}
};
int main() {
BST t;
t.insert(std::make_shared<Transaction>("1500.50", "Deposit"));
t.insert(std::make_shared<Transaction>("1600.98", "Deposit"));
t.insert(std::make_shared<Transaction>("1400", "Withdrawal"));
t.insert(std::make_shared<Transaction>("1400.59", "Deposit"));
t.display();
std::cout << t.search(1500.50);
return 0;
}
I have read many posts on BST and duplicates and I understand it isn't very possible / no clean way to allow duplicates especially for a complex type which I am using.
This is incorrect you can use multimap or multiset for this case.
For example, cppreference
Multimap is an associative container that contains a sorted list of key-value pairs, while permitting multiple entries with the same key. Sorting is done according to the comparison function Compare, applied to the keys. Search, insertion, and removal operations have logarithmic complexity.
You just need to supply as a template parameter a Compare functor which says that for two equivalent keys, none is smaller than the other.
You can associate a BST node with multiple values by making your data member a container, for example, change:
std::shared_ptr<Transaction> data;
into
std::list<std::shared_ptr<Transaction>> data;
This is the same thing as associating a key with multiple values. In essence, this is what std::multimap and std::multiset do. You will have to update tree operations/iterator as well. You will have to zip individual std::lists together, when doing container traversal, for example.
EDIT:
An easy alternative would be to use std::multiset<std::tuple<std::string, time_t, std::string, bool>>.
A binary search tree lets you store keys and associated records. It allows efficient searches for a given key (with the purpose of retrieving the associated information).
As it also supports enumeration in sorted order, it allows retrieving all keys in a given range, and duplicate keys are not a problem.

preorder traversal using stack

i'm trying to implement binary tree, pre order traversal using stack.
here its popping the last left node and after that root=root->right doesnt seem to work. Pls help. here 7 is being popped out and is being displayed and after the the program is ending.
all the functions are working yet not the desired output
#include<stdio.h>
#include<stdlib.h>
#define maxsize 100
int a[maxsize];
int top=-1;
struct node{
int data;
struct node *left, *right;
};
struct node *newNode(int data)
{
struct node *nn;
nn=(struct node *)malloc(sizeof(struct node));
if(!nn)
return;
nn->data=data;
nn->left=nn->right=NULL;
return nn;
};
void push(struct node *root)
{
printf("pushcalled\n");
if(top!=maxsize-1)
a[++top]=root;
}
int isempty()
{
return(top==-1);
}
struct node *pop()
{
printf("popcalled\n");
if(top!=-1)
{
return a[top];
top--;
}
}
void deleteStack()
{
free(a[top--]);
}
void preorder(struct node *root)
{
while(1)
{
while(root)
{
printf("%d\t",root->data);
push(root);
root=root->left;
}
printf("hello\n");
if(isempty())
break;
printf("hello\n");
root=pop();
printf("Popped data is:%d\n",root->data);
root=root->right;
printf("right data is:%d\n",root->data);
}
deleteStack();
}
int main()
{
int data;
struct node *root=newNode(10);
root->left = newNode(11);
root->left->left = newNode(7);
root->right = newNode(9);
root->right->left = newNode(15);
root->right->right = newNode(8);
preorder(root);
return 0;
}
Your logic is correct but there are some errors in your code.
root=root->right;
printf("right data is:%d\n",root->data);
you should check whether the root is null or not before you try to access root->data. That is why you are getting a segmentation fault. So put a condition if(root!=NULL) above printf() statement.
Another mistake is in the implementation of stack's pop.
struct node *pop()
{
printf("popcalled\n");
if(top!=-1)
{
return a[top];
top--;
}
}
it should be like this,
struct node *pop()
{
printf("popcalled\n");
if(top!=-1)
{
struct node* temp = a[top];
top--;
return temp;
}
}
In your code, when you return a[top]; the line below it i.e. top--; never executes and the value of top remains same.

Lowest Common Ancestor of a Binary Search Tree code doesn't pass

can anyone help me with this code? I cannot figure out where I'm blocked.
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* res=NULL;
int i=0;
postTrav(root,p,q,res,i);
return res;
}
void postTrav(TreeNode* root, TreeNode* p, TreeNode* q,TreeNode* res,int& i){
if(!root){
return;
}
postTrav(root->left,p,q,res,i);
postTrav(root->right,p,q,res,i);
if(root==p||root==q){
i++;
}
if(i==2){
res=root;
i++;
}
}
You may have a logic bug in addition to this, but, because C [I assume] is call-by-value, setting res and i are changed local to a given function invocation, but then discarded. You'll need to pass around some addresses, as below. Especially, note that res is now TreeNode ** in postTrav.
TreeNode *
lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
{
TreeNode *res = NULL;
int i = 0;
postTrav(root, p, q, &res, &i);
return res;
}
void
postTrav(TreeNode *root, TreeNode *p, TreeNode *q, TreeNode **res, int *i)
{
if (!root) {
return;
}
postTrav(root->left, p, q, res, i);
postTrav(root->right, p, q, res, i);
if (root == p || root == q) {
*i++;
}
if (*i == 2) {
*res = root;
*i++;
}
}
UPDATE:
The above code is fine. But, whenever, I have a function that needs to return or maintain two or more values [in parallel], a technique I use is to create an additional "traversal" or "helper" struct that simplifies the argument passing.
If you needed an additional variable that needed to be modified/maintained across the calls, instead of adding an additional argument to all functions, it becomes easy/easier to just add another variable to the struct. This works particularly well when you're building up your logic as you go along.
Here's the code for the refinement. Notice that fewer arguments need to be pushed/popped. And, this probably executes as fast or faster than the original. Also, for me, trav->res seems a bit cleaner than *res
// traversal "helper" struct
struct _traverse {
TreeNode *p; // not modified
TreeNode *q; // not modified
TreeNode *res; // result
int i; // depth
// add more variables here as desired ...
#ifdef WANT_TRAVERSAL_STATISTICS
int visited_count; // number of nodes we visited
#endif
};
typedef struct _traverse Traverse;
TreeNode *
lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
{
Traverse trav;
trav.p = p;
trav.q = q;
trav.res = NULL;
trav.i = 0;
#ifdef WANT_TRAVERSAL_STATISTICS
trav.visited_count = 0;
#endif
postTrav(root, &trav);
#ifdef WANT_TRAVERSAL_STATISTICS
printf("Visited %d Nodes\n",trav.visited_count);
#endif
return trav.res;
}
void
postTrav(TreeNode *root, Traverse *trav)
{
if (!root) {
return;
}
#ifdef WANT_TRAVERSAL_STATISTICS
trav->visited_count += 1;
#endif
postTrav(root->left, trav);
postTrav(root->right, trav);
if (root == trav->p || root == trav->q) {
trav->i++;
}
if (trav->i == 2) {
trav->res = root;
trav->i++;
}
}
You are not using the property of BST. postTrav should be like this:
TreeNode* postTrav(TreeNode* root, TreeNode* p, TreeNode* q,)
{
if (root == NULL||p==NULL||q==NULL) return NULL;
int n1=p->data;
int n2=q->data;
while (root != NULL)
{
// If both n1 and n2 are smaller than root, then LCA lies in left
if (root->data > n1 && root->data > n2)
root = root->left;
// If both n1 and n2 are greater than root, then LCA lies in right
else if (root->data < n1 && root->data < n2)
root = root->right;
else break;
}
return root;
}

How to access the root of rbtree in boost

Iv implemented a red-black tree based on this example. But I don't understand the meaning of the header, is it the root of the tree? according to the descriptions:
the header node is maintained with links not only to the root but also to the leftmost node of the tree, to enable constant time begin(), and to the rightmost node of the tree, to enable linear time performance when used with the generic set algorithms (set_union, etc.);
How can I access the root of my tree using header node? and what is the complexity of that?
The header node in Boost Intrusive's RBTree implementations contains the link to the root, leftmost and rightmost nodes (see here).
So, parent_ is the pointer to the root node then.
You can use a container abstraction based on the "algorithm policy" shown in that example. You'd write custom value traits, like I linked in my previous answer: Accessing left child or right child of a node in avl_set
Here's a simple, self-contained example that shows how to use an actual rbtree container (not just the algorithms) built on your node type.
Note how you can still "drill through" and get at the nodes using the containers traits.
Live On Coliru
struct my_node
{
my_node(int i = 0) :
parent_(nullptr),
left_ (nullptr),
right_ (nullptr),
int_ (i)
{ }
my_node *parent_, *left_, *right_;
int color_;
//data members
int int_;
bool operator<(my_node const& other) const { return int_ < other.int_; }
};
//Define our own rbtree_node_traits
struct my_rbtree_node_traits
{
typedef my_node node;
typedef my_node * node_ptr;
typedef const my_node * const_node_ptr;
typedef int color;
static node_ptr get_parent(const_node_ptr n) { return n->parent_; }
static void set_parent(node_ptr n, node_ptr parent){ n->parent_ = parent; }
static node_ptr get_left(const_node_ptr n) { return n->left_; }
static void set_left(node_ptr n, node_ptr left) { n->left_ = left; }
static node_ptr get_right(const_node_ptr n) { return n->right_; }
static void set_right(node_ptr n, node_ptr right) { n->right_ = right; }
static color get_color(const_node_ptr n) { return n->color_; }
static void set_color(node_ptr n, color c) { n->color_ = c; }
static color black() { return color(0); }
static color red() { return color(1); }
};
#include <boost/intrusive/link_mode.hpp>
namespace bi = boost::intrusive;
struct my_value_traits
{
typedef my_rbtree_node_traits node_traits;
typedef node_traits::node value_type;
typedef node_traits::node_ptr node_ptr;
typedef node_traits::const_node_ptr const_node_ptr;
typedef value_type* pointer;
typedef value_type const* const_pointer;
static const bi::link_mode_type link_mode = bi::link_mode_type::normal_link;
static node_ptr to_node_ptr (value_type &value) { return &value; }
static const_node_ptr to_node_ptr (const value_type &value) { return &value; }
static pointer to_value_ptr (node_ptr n) { return n; }
static const_pointer to_value_ptr (const_node_ptr n) { return n; }
};
#include <boost/intrusive/rbtree.hpp>
using mytree = bi::rbtree<my_node, bi::value_traits<my_value_traits> >;
#include <iostream>
#include <vector>
int main() {
std::vector<my_node> storage { {1}, {3}, {4}, {2}, {3}, };
mytree container;
container.insert_equal(storage.begin(), storage.end());
// NOW for the "have your cake and eat it too" moment:
for (my_node& n : container) {
std::cout << n.int_
<< " (parent: " << n.parent_ << ")"
<< " (left: " << n.left_ << ")"
<< " (right: " << n.right_ << ")"
<< "\n";
}
}
Which prints (e.g.):
1 (parent: 0xb01c40) (left: 0) (right: 0xb01c80)
2 (parent: 0xb01c20) (left: 0) (right: 0)
3 (parent: 0x7fff6da3f058) (left: 0xb01c20) (right: 0xb01c60)
3 (parent: 0xb01c60) (left: 0) (right: 0)
4 (parent: 0xb01c40) (left: 0xb01ca0) (right: 0)
The structure of a Boost.Intrusive tree is explained in the documentation of bstree_algorithms (http://www.boost.org/boost/intrusive/bstree_algorithms.hpp). The "header" node is also explained:
"At the top of the tree a node is used specially. This node's parent pointer is pointing to the root of the tree. Its left pointer points to the leftmost node in the tree and the right pointer to the rightmost one. This node is used to represent the end-iterator."
So you can access the root node using:
root = rbtree_algorithms::get_parent(header);
If you are building your own container using value traits, as explained by sehe, since commit:
https://github.com/boostorg/intrusive/commit/bbb4f724d037a6ab5ee0d9bde292f0691564960c
tree-based containers have a root() function that returns an iterator to the root node (or end() if not present) with O(1) complexity, which might be easier to use:
#include <boost/intrusive/set.hpp>
#include <cassert>
using namespace boost::intrusive;
struct MyClass : public set_base_hook<>
{
friend bool operator<(const MyClass&, const MyClass&)
{ return true; }
};
int main()
{
set<MyClass> set;
//end() is returned when the tree is empty
assert(set.root() == set.end() );
//insert myobject, must be root
MyClass myobject;
set.insert(myobject);
assert(&*set.root() == &myobject);
//erase and check root is again end()
set.erase(set.root());
assert(set.croot() == set.cend());
return 0;
}

Resources