395 lines
10 KiB
C++
395 lines
10 KiB
C++
/*
|
|
* Created on: Feb 3, 2010
|
|
* @brief: purely functional binary tree
|
|
* @Author: Chris Beall
|
|
* @Author: Frank Dellaert
|
|
*/
|
|
|
|
#include <stack>
|
|
#include <sstream>
|
|
#include <boost/shared_ptr.hpp>
|
|
#include <boost/function.hpp>
|
|
|
|
namespace gtsam {
|
|
|
|
/**
|
|
* @brief Binary tree
|
|
*/
|
|
template<class Key, class Value>
|
|
class BTree {
|
|
|
|
public:
|
|
|
|
typedef std::pair<Key, Value> value_type;
|
|
|
|
private:
|
|
|
|
/**
|
|
* @brief Node in a tree
|
|
*/
|
|
struct Node {
|
|
|
|
const size_t height_;
|
|
const value_type keyValue_;
|
|
const BTree left, right;
|
|
|
|
/** default constructor */
|
|
Node() {
|
|
}
|
|
|
|
/**
|
|
* leaf node with height 1
|
|
*/
|
|
Node(const value_type& keyValue) :
|
|
keyValue_(keyValue), height_(1) {
|
|
}
|
|
|
|
/**
|
|
* Create a node from two subtrees and a key value pair
|
|
*/
|
|
Node(const BTree& l, const value_type& keyValue, const BTree& r) :
|
|
left(l), keyValue_(keyValue), right(r),
|
|
height_(l.height() >= r.height() ? l.height() + 1 : r.height() + 1) {
|
|
}
|
|
|
|
inline const Key& key() const { return keyValue_.first;}
|
|
inline const Value& value() const { return keyValue_.second;}
|
|
|
|
}; // Node
|
|
|
|
// We store a shared pointer to the root of the functional tree
|
|
// composed of Node classes. If root_==NULL, the tree is empty.
|
|
typedef boost::shared_ptr<const Node> sharedNode;
|
|
sharedNode root_;
|
|
|
|
inline const value_type& keyValue() const { return root_->keyValue_;}
|
|
inline const Key& key() const { return root_->key(); }
|
|
inline const Value& value() const { return root_->value(); }
|
|
inline const BTree& left() const { return root_->left; }
|
|
inline const BTree& right() const { return root_->right; }
|
|
|
|
/** create a new balanced tree out of two trees and a key-value pair */
|
|
static BTree balance(const BTree& l, const value_type& xd, const BTree& r) {
|
|
size_t hl = l.height(), hr = r.height();
|
|
if (hl > hr + 2) {
|
|
const BTree& ll = l.left(), lr = l.right();
|
|
if (ll.height() >= lr.height())
|
|
return BTree(ll, l.keyValue(), BTree(lr, xd, r));
|
|
else {
|
|
BTree _left(ll, l.keyValue(), lr.left());
|
|
BTree _right(lr.right(), xd, r);
|
|
return BTree(_left, lr.keyValue(), _right);
|
|
}
|
|
} else if (hr > hl + 2) {
|
|
const BTree& rl = r.left(), rr = r.right();
|
|
if (rr.height() >= rl.height())
|
|
return BTree(BTree(l, xd, rl), r.keyValue(), rr);
|
|
else {
|
|
BTree _left(l, xd, rl.left());
|
|
BTree _right(rl.right(), r.keyValue(), rr);
|
|
return BTree(_left, rl.keyValue(), _right);
|
|
}
|
|
} else
|
|
return BTree(l, xd, r);
|
|
}
|
|
|
|
public:
|
|
|
|
/** default constructor creates an empty tree */
|
|
BTree() {
|
|
}
|
|
|
|
/** copy constructor */
|
|
BTree(const BTree& other) :
|
|
root_(other.root_) {
|
|
}
|
|
|
|
/** create leaf from key-value pair */
|
|
BTree(const value_type& keyValue) :
|
|
root_(new Node(keyValue)) {
|
|
}
|
|
|
|
/** create from key-value pair and left, right subtrees */
|
|
BTree(const BTree& l, const value_type& keyValue, const BTree& r) :
|
|
root_(new Node(l, keyValue, r)) {
|
|
}
|
|
|
|
/** Check whether tree is empty */
|
|
bool empty() const {
|
|
return !root_;
|
|
}
|
|
|
|
/** add a key-value pair */
|
|
BTree add(const value_type& xd) const {
|
|
if (empty()) return BTree(xd);
|
|
const Key& x = xd.first;
|
|
if (x == key())
|
|
return BTree(left(), xd, right());
|
|
else if (x < key())
|
|
return balance(left().add(xd), keyValue(), right());
|
|
else
|
|
return balance(left(), keyValue(), right().add(xd));
|
|
}
|
|
|
|
/** add a key-value pair */
|
|
BTree add(const Key& x, const Value& d) const {
|
|
return add(make_pair(x, d));
|
|
}
|
|
|
|
/** member predicate */
|
|
bool mem(const Key& x) const {
|
|
if (!root_) return false;
|
|
if (x == key()) return true;
|
|
if (x < key())
|
|
return left().mem(x);
|
|
else
|
|
return right().mem(x);
|
|
}
|
|
|
|
/** Check whether trees are *exactly* the same (occupy same memory) */
|
|
inline bool same(const BTree& other) const {
|
|
return (other.root_ == root_);
|
|
}
|
|
|
|
/**
|
|
* Check whether trees are structurally the same,
|
|
* i.e., contain the same values in same tree-structure.
|
|
*/
|
|
bool operator==(const BTree& other) const {
|
|
if (other.root_ == root_) return true; // if same, we're done
|
|
if (empty() && !other.empty()) return false;
|
|
if (!empty() && other.empty()) return false;
|
|
// both non-empty, recurse: check this key-value pair and subtrees...
|
|
return (keyValue() == other.keyValue()) && (left() == other.left())
|
|
&& (right() == other.right());
|
|
}
|
|
|
|
inline bool operator!=(const BTree& other) const {
|
|
return !operator==(other);
|
|
}
|
|
|
|
/** minimum key binding */
|
|
const value_type& min() const {
|
|
if (!root_) throw std::invalid_argument("BTree::min: empty tree");
|
|
if (left().empty()) return keyValue();
|
|
return left().min();
|
|
}
|
|
|
|
/** remove minimum key binding */
|
|
BTree remove_min() const {
|
|
if (!root_) throw std::invalid_argument("BTree::remove_min: empty tree");
|
|
if (left().empty()) return right();
|
|
return balance(left().remove_min(), keyValue(), right());
|
|
}
|
|
|
|
/** merge two trees */
|
|
static BTree merge(const BTree& t1, const BTree& t2) {
|
|
if (t1.empty()) return t2;
|
|
if (t2.empty()) return t1;
|
|
const value_type& xd = t2.min();
|
|
return balance(t1, xd, t2.remove_min());
|
|
}
|
|
|
|
/** remove a key-value pair */
|
|
BTree remove(const Key& x) const {
|
|
if (!root_) return BTree();
|
|
if (x == key())
|
|
return merge(left(), right());
|
|
else if (x < key())
|
|
return balance(left().remove(x), keyValue(), right());
|
|
else
|
|
return balance(left(), keyValue(), right().remove(x));
|
|
}
|
|
|
|
/** Return height of the tree, 0 if empty */
|
|
size_t height() const {
|
|
return (root_ != NULL) ? root_->height_ : 0;
|
|
}
|
|
|
|
/** return size of the tree */
|
|
size_t size() const {
|
|
if (!root_) return 0;
|
|
return left().size() + 1 + right().size();
|
|
}
|
|
|
|
/**
|
|
* find a value given a key, throws exception when not found
|
|
* Optimized non-recursive version as [find] is crucial for speed
|
|
*/
|
|
const Value& find(const Key& k) const {
|
|
const Node* node = root_.get();
|
|
while (node) {
|
|
const Key& key = node->key();
|
|
if (k < key) node = node->left.root_.get();
|
|
else if (key < k) node = node->right.root_.get();
|
|
else /* (key() == k) */ return node->value();
|
|
}
|
|
throw std::invalid_argument("BTree::find: key '" + (std::string) k + "' not found");
|
|
}
|
|
|
|
/** print in-order */
|
|
void print(const std::string& s = "") const {
|
|
if (empty()) return;
|
|
Key k = key();
|
|
std::stringstream ss;
|
|
ss << height();
|
|
k.print(s + ss.str() + " ");
|
|
left().print(s + "L ");
|
|
right().print(s + "R ");
|
|
}
|
|
|
|
/** iterate over tree */
|
|
void iter(boost::function<void(const Key&, const Value&)> f) const {
|
|
if (!root_) return;
|
|
left().iter(f);
|
|
f(key(), value());
|
|
right().iter(f);
|
|
}
|
|
|
|
/** map key-values in tree over function f that computes a new value */
|
|
template<class To>
|
|
BTree<Key, To> map(boost::function<To(const Key&, const Value&)> f) const {
|
|
if (empty()) return BTree<Key, To> ();
|
|
std::pair<Key, To> xd(key(), f(key(), value()));
|
|
return BTree<Key, To> (left().map(f), xd, right().map(f));
|
|
}
|
|
|
|
/**
|
|
* t.fold(f,a) computes [(f kN dN ... (f k1 d1 a)...)],
|
|
* where [k1 ... kN] are the keys of all bindings in [m],
|
|
* and [d1 ... dN] are the associated data.
|
|
* The associated values are passed to [f] in reverse sort order
|
|
*/
|
|
template<class Acc>
|
|
Acc fold(boost::function<Acc(const Key&, const Value&, const Acc&)> f,
|
|
const Acc& a) const {
|
|
if (!root_) return a;
|
|
Acc ar = right().fold(f, a); // fold over right subtree
|
|
Acc am = f(key(), value(), ar); // apply f with current value
|
|
return left().fold(f, am); // fold over left subtree
|
|
}
|
|
|
|
/**
|
|
* @brief Const iterator
|
|
* Not trivial: iterator keeps a stack to indicate current path from root_
|
|
*/
|
|
class const_iterator {
|
|
|
|
private:
|
|
|
|
typedef const_iterator Self;
|
|
typedef std::pair<sharedNode, bool> flagged;
|
|
|
|
/** path to the iterator, annotated with flag */
|
|
std::stack<flagged> path_;
|
|
|
|
const sharedNode& current() const {
|
|
return path_.top().first;
|
|
}
|
|
|
|
bool done() const {
|
|
return path_.top().second;
|
|
}
|
|
|
|
// The idea is we already iterated through the left-subtree and current key-value.
|
|
// We now try pushing left subtree of right onto the stack. If there is no right
|
|
// sub-tree, we pop this node of the stack and the parent becomes the iterator.
|
|
// We avoid going down a right-subtree that was already visited by checking the flag.
|
|
void increment() {
|
|
if (path_.empty()) return;
|
|
sharedNode t = current()->right.root_;
|
|
if (!t || done()) {
|
|
// no right subtree, iterator becomes first parent with a non-visited right subtree
|
|
path_.pop();
|
|
while (!path_.empty() && done())
|
|
path_.pop();
|
|
} else {
|
|
path_.top().second = true; // flag we visited right
|
|
// push right root and its left-most path onto the stack
|
|
while (t) {
|
|
path_.push(std::make_pair(t, false));
|
|
t = t->left.root_;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
// traits for playing nice with STL
|
|
typedef ptrdiff_t difference_type; // correct ?
|
|
typedef std::forward_iterator_tag iterator_category;
|
|
typedef std::pair<Key, Value> value_type;
|
|
typedef const value_type* pointer;
|
|
typedef const value_type& reference;
|
|
|
|
/** initialize end */
|
|
const_iterator() {
|
|
}
|
|
|
|
/** initialize from root */
|
|
const_iterator(const sharedNode& root) {
|
|
sharedNode t = root;
|
|
while (t) {
|
|
path_.push(std::make_pair(t, false));
|
|
t = t->left.root_;
|
|
}
|
|
}
|
|
|
|
/** equality */
|
|
bool operator==(const Self& __x) const {
|
|
return path_ == __x.path_;
|
|
}
|
|
|
|
/** inequality */
|
|
bool operator!=(const Self& __x) const {
|
|
return path_ != __x.path_;
|
|
}
|
|
|
|
/** dereference */
|
|
reference operator*() const {
|
|
if (path_.empty()) throw std::invalid_argument(
|
|
"operator*: tried to dereference end");
|
|
return current()->keyValue_;
|
|
}
|
|
|
|
/** dereference */
|
|
pointer operator->() const {
|
|
if (path_.empty()) throw std::invalid_argument(
|
|
"operator->: tried to dereference end");
|
|
return &(current()->keyValue_);
|
|
}
|
|
|
|
/** pre-increment */
|
|
Self& operator++() {
|
|
increment();
|
|
return *this;
|
|
}
|
|
|
|
/** post-increment */
|
|
Self operator++(int) {
|
|
Self __tmp = *this;
|
|
increment();
|
|
return __tmp;
|
|
}
|
|
|
|
}; // const_iterator
|
|
|
|
// hack to make BTree work with BOOST_FOREACH
|
|
// We do *not* want a non-const iterator
|
|
typedef const_iterator iterator;
|
|
|
|
/** return iterator */
|
|
const_iterator begin() const {
|
|
return const_iterator(root_);
|
|
}
|
|
|
|
/** return iterator */
|
|
const_iterator end() const {
|
|
return const_iterator();
|
|
}
|
|
|
|
}; // BTree
|
|
|
|
} // namespace gtsam
|
|
|