// Tree.h
//
#ifndef TREE
#define TREE
#include <stddef.h>
struct Node
{
int key ;
Node *p ; // Pointer to parent
Node *l ; // Pointer to left child
Node *r ; // Pointer to right child
Node(int key)
{
this->key = key ;
p = l = r = NULL ;
}
// Bitwise copy constructor is good enough
// Replace is just re-write the 'key'
Node &Replace(const Node &other)
{
this->key = other.key ;
return *this ;
}
} ;
/*
I don't use template because I don't like put all code in header file
*/
class Tree
{
public:
Tree();
virtual ~Tree();
private:
Tree(const Tree &other) ;
Tree &operator=(const Tree &other) ;
public:
// Interface
void PreOrderTreeWalk() ;
void InOrderTreeWalk() ;
void PostOrderTreeWalk() ;
Node *Add(const int key) ;
int Min() ;
int Max() ;
void PreDelete(int key) ;
void SucDelete(int key) ;
Node *Search(const int key) ;
bool IsEmpty() ;
void Clear() ;
public:
// logic
void preOrderTreeWalk(const Node *root) ;
void inOrderTreeWalk(const Node *root) ;
void postOrderTreeWalk(const Node *root) ;
Node *add(Node *p, const Node &n) ;
/* In 'preDel', we use 'p' 's predecessor to replace the 'p' if needed.
This function, I use my idea to solve the problem,
which is a easy to be thought but awkward and complex way.
I reserve this method for compare with 'sucDel'
which in a more pretty and efficient way */
void preDel(Node *p) ;
/* In 'sucDel', we use 'p' 's successor to replace the 'p' if needed.
This function, I use a bright way(intruduced by <Introduction to Algorithms>) to solve the problem.
In this way, we divided all cases into three
1) 'p' has no child
2) 'p' has only one child
3) 'p' has two children
In 1) and 2), the handling is easy, for case 3), we have to find its successor,
and set focus on its successor, however, I'm sure its successor at most have one child,
so, the case become easy and is like to case 2) ! */
void sucDel(Node *p) ;
/* This function will delete node recursively from the 'p' */
void delTree(Node *p) ;
/* This function get the predecessor(前驱) element of 'p' */
Node *predecessor(Node *p) ;
/* This function get the successor(后继) element of 'p' */
Node *successor(Node *p) ;
Node *minimum(Node *p) ;
Node *maximum(Node *p) ;
/* This function does not in use. It can be reuse both in 'predecessor' and 'successor'.
But I do not use it for clarify my intention in other function */
void disconnect(Node *p) ;
private:
// data
Node *root ;
} ;
#endif // TREE
// Tree.cpp
//
#include "Tree.h"
#include <cassert>
#include <iostream>
using namespace std ;
Tree::Tree()
{
root = NULL ;
}
Tree::~Tree()
{
Clear() ;
}
// Interface
void Tree::PreOrderTreeWalk()
{
cout <<"PreOrderTreeWalk() : " <<endl ;
preOrderTreeWalk(root) ;
}
void Tree::InOrderTreeWalk()
{
cout <<"InOrderTreeWalk() : " <<endl ;
inOrderTreeWalk(root) ;
}
void Tree::PostOrderTreeWalk()
{
cout <<"PostOrderTreeWalk() : " <<endl ;
postOrderTreeWalk(root) ;
}
Node *Tree::Add(const int key)
{
Node *p = NULL ;
if (IsEmpty())
{
// If the tree is empty, we put new node to root
root = new Node(key) ;
}
else
{
// If the tree is not empty, we must find where to insert the new node by 'add' recursively
p = add(root, Node(key)) ;
}//if
return p ;
}
int Tree::Min()
{
return minimum(root)->key ;
}
int Tree::Max()
{
return maximum(root)->key ;
}
void Tree::PreDelete(int key)
{
preDel(Search(key)) ;
}
void Tree::SucDelete(int key)
{
sucDel(Search(key)) ;
}
Node *Tree::Search(const int key)
{
Node *p = root ;
while (NULL != p)
{
if (key == p->key)
{
break ;
}//if
p = key > p->key ? p->r : p->l ;
}//while
return p ;
}
bool Tree::IsEmpty()
{
return NULL == root ;
}
void Tree::Clear()
{
delTree(root) ;
root = NULL ;
}
// logic
void Tree::preOrderTreeWalk(const Node *p)
{
// Pre-Order is
// 1) current node
// 2) it's left node
// 3) it's right node
if (NULL != p)
{
cout <<p->key <<" " ;
preOrderTreeWalk(p->l) ;
preOrderTreeWalk(p->r) ;
}//if
if (root == p)
{
cout <<endl ;
}//if
}
void Tree::inOrderTreeWalk(const Node *p)
{
// In-Order is
// 1) it's left node
// 2) current node
// 3) it's right node
if (NULL != p)
{
inOrderTreeWalk(p->l) ;
cout <<p->key <<" " ;
inOrderTreeWalk(p->r) ;
}//if
if (root == p)
{
cout <<endl ;
}//if
}
void Tree::postOrderTreeWalk(const Node *p)
{
// In-Order is
// 1) it's left node
// 2) it's right node
// 3) current node
if (NULL != p)
{
postOrderTreeWalk(p->l) ;
postOrderTreeWalk(p->r) ;
cout <<p->key <<" " ;
}//if
if (root == p)
{
cout <<endl ;
}//if
}
Node *Tree::add(Node *p, const Node &n)
{
assert(p) ;
Node *rtn = p ;
// Sort by 'key', lower side left and greater side right
// we fine a place to put new node recursively
if (n.key > p->key)
{
// We use comma expression to set the ' ? : ' express's return value
rtn = (p->r == NULL ? p->r = new Node(n), p->r->p = p, p->r : add(p->r, n)) ;
}
else if (n.key < p->key)
{
// We use comma expression to set the ' ? : ' express's return value
rtn = (p->l == NULL ? p->l = new Node(n), p->l->p = p, p->l : add(p->l, n)) ;
}
else // n.key == p->key
{
// Already exist
return rtn ;
}//if
return rtn ;
}
void Tree::preDel(Node *p)
{
// Can't delete a null position
// or an NULL tree
if (NULL == p || NULL == root)
{
return ;
}//if
// We should make sure if NULL != 'parent/lChild/rChild' before dereference
Node *parent = p->p ;
Node *lChild = p->l ;
Node *rChild = p->r ;
// If 'p' is root, we should connect root to new node
if (NULL == parent)
{
if (NULL == lChild)
{
// 'p' has no left child
// Connect 'root' and 'rChild'
root = (NULL == rChild ? NULL : rChild->p = root, rChild) ;
}
else
{
// 'p' has left child
// Connect 'root' and 'lChild'
root = lChild, lChild->p = parent ;
// Connect 'lChild' and 'rChild'
lChild->r = rChild ;
NULL == rChild ? NULL : rChild->p = root ;
}//if NULL == lChild
delete p ;
// p = NULL ; // Here we needn't to set a formal parameter to NULL
return ;
}//if NULL == parent
// We divide into three cases
// a) 'p' has no child, we just delete it and disconnect with its parent if necessary
// b) 'p' has one child, we connect 'p' 's parent and 'p' 's left/right child before deleting 'p'
// c) 'p' has two children, we find and delete 'p' 's predecessor, then repace 'p' with the predecessor
if (NULL == lChild
&& NULL == rChild)
{
// a) 'p' has no child
// If 'p' 's parent isn't null, we should disconnect its left/right child with 'p'
(parent->l == p ? parent->l : parent->r) = NULL ;
delete p ;
// p = NULL ; // Here we needn't to set a formal parameter to NULL
}
else if (NULL == lChild
|| NULL == rChild)
{
// b) 'p' has one child
if (p == parent->l)
{
// 'p' is left child of its parent
// connect two nodes
parent->l = NULL == lChild ? (rChild->p = parent, rChild) : (lChild->p = parent, lChild) ;
}
else // p == parent->r
{
// 'p' is right child of its parent
// connect two nodes
parent->r = (NULL == lChild ? (rChild->p = parent, rChild) : (lChild->p = parent, lChild)) ;
}//if p == parent->l
// Here have an one line statement but complex some, I'll not use trick in my code
// (p == parent->l ? parent->l : parent->r)
// = (NULL == lChild ? (rChild->p = parent, rChild) : (lChild->p = parent, lChild)) ;
delete p ;
// p = NULL ; // Here we needn't to set a formal parameter to NULL
}
else // NULL != lChild && NULL != rChild
{
// c) 'p' has two children
// I'm sure the 'pre' has an non-NULL parent
Node *pre = predecessor(p) ;
pre == pre->p->l ? pre->p->l = pre->l : pre->p->r = pre->l ;
NULL == pre->l ? NULL : pre->l->p = pre->p ;
p->Replace(*pre) ;
}//if
}
void Tree::sucDel(Node *p)
{
// When we delete a node with two children,
// we use its successor to replaced it and then disconnect before deleting the successor
// Can't delete a null position
// or an NULL tree
if (NULL == p || NULL == root)
{
return ;
}//if
// Find which node is to be disconnect
Node *toDel = (NULL == p->l || NULL == p->r) ? p : successor(p) ;
// Disconnect the 'toDel' from its parent and child
// I'm sure 'toDel' at most have one child
Node *parent = toDel->p ;
Node *child = NULL == toDel->l ? toDel->r : toDel->l ;
if (NULL == parent)
{
// 'toDel' is root
root = child ;
}
else // NULL != parent
{
// parent is non-NULL
(toDel == parent->l ? parent->l : parent->r) = child ;
}//if
// If 'p' isn't 'toDel', we'd copy the content from 'toDel' to 'p'
toDel == p ? NULL : p->Replace(*toDel) ;
// We delete 'toDel' at last
delete toDel ;
toDel = NULL ;
}
void Tree::delTree(Node *p)
{
if (NULL != p)
{
delTree(p->l) ;
delTree(p->r) ;
// Seperate from its parent
if (NULL != p->p)
{
(p->p->l == p ? p->p->l : p->p->r) = NULL ;
}//if
delete p ;
// p = NULL ; // 'p' is formal parameter, we need not set it NULL
}//if
}
Node *Tree::predecessor(Node *p)
{
Node *rtn = p ;
if (NULL != p)
{
if (NULL != p->l)
{
return maximum(p->l) ;
}
else // NULL == p->l
{
// We want to find it's ancestor who is right child of it's parent
while (NULL != p->p && p->p->r != p)
{
p = p->p ; // Move to parent
}//while
rtn = p->p ;
}//if
}//if
return rtn ;
}
Node *Tree::successor(Node *p)
{
Node *rtn = p ;
if (NULL != p)
{
if (NULL != p->r)
{
return minimum(p->r) ;
}
else // NULL == p->r
{
// We want to find it's ancestor who is left child of it's parent
while (NULL != p->p && p->p->l != p)
{
p = p->p ; // Move to parent
}//while
rtn = p->p ;
}//if
}//if
return rtn ;
}
Node *Tree::minimum(Node *p)
{
while (NULL != p && NULL != p->l)
{
p = p->l ;
}//while
return p ;
}
Node *Tree::maximum(Node *p)
{
while (NULL != p && NULL != p->r)
{
p = p->r ;
}//while
return p ;
}
void Tree::disconnect(Node *p)
{
// We assume 'p' can't have both children
// Can't disconnect a NULL node
if (NULL == p)
{
return ;
}//if
// Connect 'p' 's child and its parent
Node *child = NULL == p->l ? p->r : p->l ;
NULL == child ? NULL : child->p = p->p ;
if (NULL == p->p)
{
// 'p' is root
root = child ;
}
else // NULL != p->p
{
(p == p->p->l ? p->p->l : p->p->r) = child ;
}//if
}
// main.cpp
//
#include <iostream>
using namespace std;
#include "Tree.h"
int main()
{
Tree t ;
int arr[] = {4, 3, 2, 1, 10, 9, 6, 5, 8, 7, 11, 12} ;
int arrLen = sizeof(arr) / sizeof(arr[0]) ;
for (int i = 0 ;i < arrLen ;i++)
{
t.Add(arr[i]) ;
}//for
// Show the tree structure
t.PreOrderTreeWalk() ;
t.InOrderTreeWalk() ;
t.PostOrderTreeWalk() ;
cout <<"min value is : " <<t.Min() <<endl ;
cout <<"max value is : " <<t.Max() <<endl ;
// Illerstrate predecessor/successor delete policy
t.PreDelete(4) ;
t.PreOrderTreeWalk() ;
t.SucDelete(6) ;
t.SucDelete(12) ;
cout <<"min value is : " <<t.Min() <<endl ;
cout <<"max value is : " <<t.Max() <<endl ;
// You can analysis and exam the tree structure after delete with two policies
t.PreOrderTreeWalk() ;
t.InOrderTreeWalk() ;
t.PostOrderTreeWalk() ;
return 0;
}