#include <iostream>
#include <stack>
#include <memory>
#include <queue>
#include <limits>
using namespace std;
template<typename T>
struct Node{
T data;
typedef shared_ptr<Node<T>> nodeptr;
nodeptr left, right;
Node(const T &t, const nodeptr &l = nullptr, const nodeptr &r = nullptr) :
data(t), left(l), right(r){}
};
template<typename T>
void visit(const typename Node<T>::nodeptr &r){
cout << r->data << ", ";
}
template<typename T>
void inorder_traversal_rec(const typename Node<T>::nodeptr &r){
if (r){
inorder_traversal_rec<T>(r->left);
visit<char>(r);
inorder_traversal_rec<T>(r->right);
}
}
template<typename T>
void inorder_traversal_itr(const typename Node<T>::nodeptr &r){
stack<typename Node<T>::nodeptr> st;
typename Node<T>::nodeptr n = r;
while(n || !st.empty()){
if (n){
st.push(n);
n = n->left;
}
else{
n = st.top(); st.pop();
visit<char>(n);
n = n->right;
}
}
}
template<typename T>
void inorder_morris(const typename Node<T>::nodeptr &r){
typename Node<T>::nodeptr c = r;
while(c){
// if there is a left subtree
if (c->left){
// make b the predecessor of c but stop the alg if p already points c
typename Node<T>::nodeptr p = c->left;
while(p->right && p->right != c){
p = p->right;
}
// now: p may be the leaf or p was already visited before and need fix
if (!p->right){
p->right = c; // it will be used later...
// keep going with c
c = c->left; // we need to get the first element in the tree
}
// element was already visited so right points to the next and
// it means that everything on the left from c was visited
// also we need to fix the link of p
else{
p->right = nullptr;
visit<char>(c);
c = c->right; // no matter it exists or not
}
}
// no left subtree => print the element and switch to the right subtree
else{
visit<char>(c);
c = c->right;
}
}
}
template<typename T>
void preorder_traversal_rec(const typename Node<T>::nodeptr &r){
if (r){
visit<char>(r);
preorder_traversal_rec<T>(r->left);
preorder_traversal_rec<T>(r->right);
}
}
template<typename T>
void preorder_traversal_itr(const typename Node<T>::nodeptr &r){
stack<typename Node<T>::nodeptr> st;
typename Node<T>::nodeptr n = r;
while(n || !st.empty()){
if (n){
visit<char>(n);
st.push(n);
n = n->left;
}
else{
n = st.top(); st.pop();
n = n->right;
}
}
}
template<typename T>
void postorder_traversal_rec(const typename Node<T>::nodeptr &r){
if (r){
postorder_traversal_rec<T>(r->left);
postorder_traversal_rec<T>(r->right);
visit<char>(r);
}
}
template<typename T>
void postorder_traversal_itr(const typename Node<T>::nodeptr &r){
stack<typename Node<T>::nodeptr> st;
typename Node<T>::nodeptr n = r;
typename Node<T>::nodeptr last;
while(n || !st.empty()){
if (n){
st.push(n);
if (n->left){
n = n->left;
}
else{
n = n->right;
}
}
else{
last = st.top(); st.pop();
visit<char>(last);
if (!st.empty() && st.top()->right && st.top()->right != last){
n = st.top()->right;
}
else{
n = nullptr;
}
}
}
}
template<typename T>
void breadth_traversal(const typename Node<T>::nodeptr &r){
queue<typename Node<T>::nodeptr> q;
q.emplace(r);
while(!q.empty()){
visit<char>(q.front());
if (q.front()->left) q.emplace(q.front()->left);
if (q.front()->right) q.emplace(q.front()->right);
q.pop();
}
}
template<typename T>
bool isBST(const typename Node<T>::nodeptr &r, T left = numeric_limits<T>::min(), T right = numeric_limits<T>::max()){
// no tree is valid
if (!r){
return true;
}
// if value is not in ranges it is invalid
if (!(left <= r->data && r->data < right)){
return false;
}
// but if it is in a valid range, we need to check subtrees
else{
return isBST(r->left, left, r->data) && isBST(r->right, r->data, right);
}
}
int main() {
Node<char>::nodeptr binary_tree =
make_shared<Node<char>>('F',
make_shared<Node<char>>('B',
make_shared<Node<char>>('A'),
make_shared<Node<char>>('D',
make_shared<Node<char>>('C'),
make_shared<Node<char>>('E'))),
make_shared<Node<char>>('G',
nullptr,
make_shared<Node<char>>('I',
make_shared<Node<char>>('H')))
);
cout << "inorder" << endl;
inorder_traversal_rec<char>(binary_tree);
cout << endl;
inorder_traversal_itr<char>(binary_tree);
cout << endl;
inorder_morris<char>(binary_tree);
cout << endl << "preorder" << endl;
preorder_traversal_rec<char>(binary_tree);
cout << endl;
preorder_traversal_itr<char>(binary_tree);
cout << endl << "postorder" << endl;
postorder_traversal_rec<char>(binary_tree);
cout << endl;
postorder_traversal_itr<char>(binary_tree);
cout << endl << "breadth first traversal" << endl;
breadth_traversal<char>(binary_tree);
cout << endl << "is valid BST? " <<
(isBST<char>(binary_tree) ? "yes" : "no") << endl;
}