Used to implement C++ Standard Library Ordered Associative Containers
std::set
, std::multiset
std::map
, std::multimap
Complete tree of depth $d$
A binary tree $t$ is a finite set of nodes such that
Size = 9, Depth = 3
Size = 5, Depth = 4
template <typename T>
struct Node {
Node (T const& v = T(), Node* l = nullptr, Node* r = nullptr)
: data (v), left(l), right(r) { }
~Node();
T data;
Node* left;
Node* right;
};
// what is this similar to?
// We should probably keep track of a parent
template <typename T>
struct BTNode {
BTNode (T const& v = T(), BTNode* p = nullptr, BTNode* l = nullptr, BTNode* r = nullptr)
: data (v), parent(p), left(l), right(r) { }
T data;
BTNode* parent;
BTNode* left;
BTNode* right;
};
// but first -- cleaning up a tree!
template <typename T>
Node<T>::~Node() {
if (left != nullptr) {
delete left;
}
if (right != nullptr) {
delete right;
}
}
Systematically explore every node
3 primary methods
auto root = /* root -> 3 */
new Node<int>(3, /* / \ */
new Node<int>(1, /* / \ */
nullptr, /* / \ */
new Node<int>(2)), /* 1 5 */
new Node<int>(5, /* \ / */
new Node<int>(4), /* 2 4 */
nullptr));
template <typename T, typename Function>
void in_order (Node<T>* t, Function&& fn) {
if (t != nullptr) {
in_order (t->left, std::forward<Function>(fn));
fn (t->data);
in_order (t->right, std::forward<Function>(fn));
}
}
#include <iostream>
in_order (root, [] (int v) {
std::cout << v << ' ';
});
1 2 3 4 5
template <typename T, typename Function>
void pre_order (Node<T>* t, Function&& fn) {
if (t != nullptr) {
fn (t->data);
pre_order (t->left, std::forward<Function>(fn));
pre_order (t->right, std::forward<Function>(fn));
}
}
pre_order (root, [] (int v) {
std::cout << v << ' ';
});
3 1 2 5 4
template <typename T, typename Function>
void post_order (Node<T>* t, Function&& fn) {
if (t != nullptr) {
post_order (t->left, std::forward<Function>(fn));
post_order (t->right, std::forward<Function>(fn));
fn (t->data);
}
}
post_order (root, [] (int v) {
std::cout << v << ' ';
});
2 1 4 5 3
depth()
countLeaves()
countInterior()
size()
template <typename T>
size_t depth(Node<T>* t) {
if (t == nullptr) {
return -1;
}
// ...
}
template <typename T>
size_t countLeaves(Node<T>* t) {
if (t == nullptr) {
return 0;
}
// ...
}
template <typename T>
size_t countInterior(Node<T>* t) {
if (t == nullptr) {
return 0;
}
// ...
}
template <typename T>
size_t size(Node<T>* t) {
if (t == nullptr) {
return 0;
}
// ...
}
For each node $N$
Duplicates?
set
, map
)multiset
, multimap
)#include <vector>
#include <set>
#include <initializer_list>
std::vector<int> v {7, 3, 2, 5, 3, 2, 9, 3};
std::set<int> s {v.begin(), v.end()};
v
{ 7, 3, 2, 5, 3, 2, 9, 3 }
s
{ 2, 3, 5, 7, 9 }
To sort a vector:
Complexity?
std::(multi)set
--- $O(N lg(N))$bool empty() const noexcept;
size_type size() const noexcept;
size_type max_size() const noexcept;
std::pair<iterator, bool> insert (value_type const& value);
std::pair<iterator, bool> insert (value_type&& value);
iterator insert (const_iterator hint, value_type const& value);
iterator insert (const_iterator hint, value_type&& value);
void insert (InputIterator first, InputIterator last);
void insert (std::initializer_list<value_type>);
void clear();
iterator erase(const_iterator pos);
iterator erase(const_iterator first, const_iterator last);
size_type erase(key_type& key);
bool contains(value_type const& key) const;
size_type count(value_type const& key) const;
const_iterator find(value_type const& key) const;
iterator find(value_type const& key);
template <typename T>
class Set {
public:
struct TreeNode {
T data;
TreeNode* parent, *left, *right;
};
Set() : m_header(), m_size(0) {
m_header.parent = m_header.left = m_header.right = &m_header;
}
iterator find (T const& v);
std::pair<iterator, bool> insert (T const& v);
size_t erase (T const& v);
private:
TreeNode m_header;
// parent -> root of tree
// left -> max of tree
// right -> min of tree
size_t m_size;
};
find()
¶iterator find (T const& value) {
if (size() == 0)
return end();
TreeNode* parent = m_header->parent;
while (parent != nullptr) {
if (parent->data == value) {
// ...
}
// ...
}
// ...
}
and recursively?
insert()
¶std::pair<iterator,bool> insert (T const& value) {
if (size() == 0) {
TreeNode* newNode = new TreeNode (value, &m_header, nullptr, nullptr);
m_header.parent = m_header.left = m_header.right = newNode;
++m_size;
return std::make_pair (iterator (newNode), true);
}
TreeNode* p = &m_header;
for (TreeNode* n = m_header.parent; n != nullptr; ) { // find where to insert
p = n;
if (value < n->data) {
n = n->left;
} else if (value > n->data) {
n = n->right;
} else { // equal
return std::make_pair (iterator (n), false);
}
}
TreeNode* newNode = new TreeNode (value, p, nullptr, nullptr);
if (value < p->data) { // p is the parent -- find out where we inserted (left or right)
p->left = newNode;
if (p == m_header.right) m_header.right = newNode; // update new min
} else {
p->right = newNode;
if (p == m_header.left) m_header.left = newNode; // update new max
}
++m_size;
return std::make_pair (iterator (newNode), true);
}
How would we do this recursively?
TreeNode* findMinNode (TreeNode* t) {
// Iterative version
if (t == nullptr) return t;
// ...
}
TreeNode* findMaxNode (TreeNode* t) {
// Iterative version
if (t == nullptr) return t;
// ...
}
// Assumes t has a right subtree.
// Used to erase a node with 2 children
// Finds the *smallest* node greater than t
TreeNode* findSuccessorNode (TreeNode* t) {
// ...
}
TreeNode* & n = /* node to remove */;
// NOTE: n is a reference to a pointer
iterator iter (n);
++iter;
// NOT SHOWN: check if min or max -- if so, replace with parent
delete std::exchange (n, nullptr);
--m_size;
return iter;
TreeNode* & n = /* node to remove */;
iterator iter (n);
++iter;
// NOT SHOWN: check if max -- if so, replace with parent
delete std::exchange (n, n->left);
--m_size;
return iter;
TreeNode* & n = /* node to remove */;
// NOT SHOWN: check if min -- if so, replace with parent
delete std::exchange (n, n->right);
--m_size;
return iterator (findMinNode (n));
TreeNode* & n = /* node to remove */;
TreeNode* next = findSuccessorNode (n); // may have a right child
// splice out next
next->parent->left = next->right;
if (next->right != nullptr)
next->right->parent = next->parent;
// splice in next where n was
next->parent = n->parent;
next->left = n->left;
// update right -- make sure it doesn't reference itself
next->right = (n->right == next) ? nullptr : n->right;
delete std::exchange (n, next); // automatically fixes parent's link to n
--m_size;
return iterator (n);