As I have got more involved with Oracle ( love that product ) I was just checking how good my C, C++ coding skill is.
So first I took up a doubly linked list in C, coding came natural and easy and everything worked perfect.A menu driven interface to add, traverse, delete the list.
Next I took up a data base code in c++. Again menu driven program - Add, print, search, modify, delete a data structure with name, age as its components. Tough but could succeed. I had already done this code , so looked into it. Happy that I could code/debug with ease. Any one who has coded for file management knows the kind of issues one runs into - when one deletes/modifies record, the file is filled with infinite garbage data !! And this is C+, no FILE * concept - everything is class based , the ios class hierarchy is complex.
Having succeeded with that, I took up a Binary Search Tree program - Add, traverse inorder and print sorted data, remove an element. Add, traverse were not difficult to code, but removing a node from the binary search tree - Oh that was tough and kept me working at least 4 to 5 days . I looked up all the code lying around on the internet - but I want to master the ability to write the code myself with perfect understanding.
Recursive functions are tough to analyse, so I tried writing simple functions. One function required I write another, so I kept coding more and more functions. The beauty was now pointer manipulation came so easy. It was not tough to write p -> parent -> left -> right -> data.
I wrote a menu driven program in C++ for randomly inserting, printing sorted data and removing data .
Anyway the purpose of writing this blog is that I ended up with a final code around 400 lines, with many functions - looked more complex than any of those codes I found complex - made me laugh at myself. Along the way, I created complex requirements like printing tree statistics ( depth, number of nodes, order of original data entry, current node address, parent node address, is it a left/right/root node etc.. and got struck in the logic .
It was fun and lot of skill sharpening. Any one who wants to master coding skill must take up data structures like lists and trees - endless challenges here. And when I am playing the Oh so very addictive - Angry Birds game , I am wondering at the logic of the code behind. It must have been maddening to develop such an application.
# include
# include
# include
typedef int bool;
enum { true = 1 , false = 0 };
enum { ROOT = 0 , LEAFNODE = 1, WITH_SINGLE_SUBTREE = 2 , WITH_BOTH_SUBTREES = 3};
class bSearchTree
{
protected:
struct treeNode
{
int data ;
treeNode *left;
treeNode *right;
treeNode *parent;
char nodeType ;
};
struct treeInfo
{
treeNode * address;
int data;
treeNode * parentAddress;
treeInfo *next;
char nodeType;
};
treeNode * root ;
treeInfo *info , *prevInfo, *startInfo;
int depth ;
int isEmpty () const { return root==NULL; }
void inorder(treeNode *p);
treeNode *getNode(int data, treeNode *p);
bool removeNode(treeNode *t, int data);
int getNodeType(treeNode *t);
void printNode(int data);
void printNodeType(int data);
treeNode* getLargestRight(treeNode * p, int & replaceData);
treeNode* getSmallestLeft(treeNode * p, int & replaceData);
void removeFromTreeInfo(treeNode *p);
public:
bSearchTree () { root = NULL; depth = 0 ; nodeCount = 0 ; }
void insert(int data);
void remove(int data);
void print(){inorder(root);}
int nodeCount ;
int count () { return nodeCount ; }
void printTreeInfo();
int getTreeDepth(treeNode *t);
};
int bSearchTree::getTreeDepth(treeNode *t)
{
if (t == NULL)
return 0 ;
else
{
int lDepth = getTreeDepth(t -> left);
int rDepth = getTreeDepth(t -> right);
if ( lDepth > rDepth)
return lDepth + 1 ;
else
return rDepth + 1 ;
}
}
void bSearchTree::printTreeInfo()
{
clrscr();
cout << "Tree Information \n\n Tree Type: Binary Search Tree ";
cout << "\n\n Number of nodes : " << nodeCount ; cout << "\n\nDepth : " << getTreeDepth(root);
cout << "\n\nNode Address\tData\tParent Address\tNode Type\n";
treeInfo *ptr = startInfo ;
while(ptr)
{
cout <<"\n\n"<< ptr->address<<"\t"<
switch( ptr->nodeType)
{
case 'R' :
cout <<"Root" ;
break;
case 'l' :
cout <<"Left";
break;
case 'r' :
cout << "Right";
break;
}
ptr = ptr -> next;
}
}
int bSearchTree::getNodeType(treeNode *t)
{
int nodetype ;
if ( t == root) nodetype = ROOT ;
else
if ( t -> right == NULL && t -> left == NULL )
nodetype = LEAFNODE;
else
if( (t -> right == NULL && t-> left != NULL ) || ( t -> left == NULL && t -> right != NULL ))
nodetype = WITH_SINGLE_SUBTREE ;
else
if ( t -> right != NULL && t-> left != NULL)
nodetype = WITH_BOTH_SUBTREES ;
return nodetype;
}
void bSearchTree::printNodeType(int data)
{
switch ( getNodeType( getNode(data, root)))
{
case ROOT:
cout << "\nRoot" ; break ; case LEAFNODE: cout << "\nLeaf Node "; break; case WITH_SINGLE_SUBTREE : cout << "\nWith single subtree.. " ; break; case WITH_BOTH_SUBTREES : cout << "\nWith both subtrees" ; } } bSearchTree::treeNode *bSearchTree::getNode(int data, treeNode *p) { if (p == NULL ) return NULL; if (p -> data == data) return p ;
if ( p -> left) getNode(data, p -> left);
//cout << "\t" << p->data ;
if(p -> right) getNode(data, p -> right);
}
void bSearchTree::printNode(int data)
{
treeNode *t = getNode(data, root);
if (t)
cout << "\nData at node " << t << " Parent = " << t -> parent ;
else
cout << "\nData not found " ; } void bSearchTree::insert(int data) { treeNode *t = new treeNode(); t -> data = data;
t -> left = NULL;
t -> right = NULL ;
t -> parent = NULL ;
nodeCount++;
treeNode *curr , *parent;
curr = parent = NULL;
if (isEmpty() )
{ root = t ;
t -> nodeType = 'R' ;
t -> parent = NULL ;
/* update tree info */
info = new treeInfo();
info -> next = NULL ;
info -> address = root;
info -> parentAddress = NULL;
startInfo = info ;
info -> data = data ;
info->nodeType = root -> nodeType;
prevInfo = NULL ;
return ;
}
curr = root ;
while(curr)
{
parent = curr;
if( t -> data > curr -> data)
curr = curr -> right ;
else
curr = curr -> left ;
}
if ( t -> data <= parent -> data)
{parent -> left = t ;
t -> nodeType = 'l' ;
}
else
{ parent -> right = t ;
t -> nodeType = 'r' ;
}
t -> parent = parent ;
/* update tree info */
prevInfo = info ;
info = new treeInfo();
prevInfo -> next = info;
info -> address = t;
info -> parentAddress = t -> parent ;
info -> data = data ;
info -> next = NULL ;
info -> nodeType = t -> nodeType;
depth = getTreeDepth(root);
}
void bSearchTree::inorder(treeNode *p)
{
if (p == NULL ) return ;
if ( p -> left) inorder(p -> left);
cout << "\t" << p->data ;
if(p -> right) inorder(p -> right);
}
treeNode* bSearchTree::getLargestRight(treeNode * p, int & replaceData)
{
// traverse right till u have located the last in the line
treeNode *curr, *t ;
t = curr = p;
while(t )
{
curr = t ;
replaceData = t -> data ;
cout << "\nreplace data = " << replaceData << "\n" ; getch(); if ( t -> right )
t = t -> right;
else if( t -> left)
t = t -> left -> right ;
else break ;
}
cout << "\nout of glr ...."; return curr ; } treeNode* bSearchTree::getSmallestLeft(treeNode *p, int & d) { // traverse left till u have located the last in the line treeNode *curr, *t = p ; while(t ) { curr = t ; d = t -> data;
cout << "\nreplace data = " << d << "\n" ; getch(); if( t -> left )
t = t -> left ;
else if ( t -> right)
t = t -> right -> left ;
else
break ;
}
return curr;
}
void bSearchTree::removeFromTreeInfo(treeNode *p)
{
treeInfo *prev, *ptr= startInfo;
while(ptr)
{
prev= ptr ;
if (ptr -> address == p )
{
prev -> next = ptr -> next ;
delete ptr;
break;
}
else
ptr = ptr -> next;
}
}
void bSearchTree::remove(int data)
{
if (removeNode(root, data))
{
cout <<"\n\nSuccessful deletion of " << data << "\n\n" ; nodeCount--; depth = getTreeDepth(root); } else cout <<"\n\nDeletion of " << data << " Failed \n\n" ; getch(); } bool bSearchTree::removeNode(treeNode *t, int data) { bool removed = false; if (isEmpty() ) { cout << "\n\nEmpty Tree\n\n"; return false;} treeNode *p = getNode(data, t); int x = 0; int &replaceData = x ; treeNode *toDelete ; if ( !p ) { cout << "\nData does not exist. \n" ; return false; } // data does not exists int nodetype ; nodetype = getNodeType(p) ; //printNodeType(data); //getch(); treeInfo *deletedInfo , *infoPtr ; switch ( nodetype) { case LEAFNODE : (p -> parent) -> left = (p -> parent ) -> right = NULL;
cout << "\nDeleted a leaf node with "<< data << "\n "; removeFromTreeInfo(p); delete p ; removed =true; break ; case WITH_SINGLE_SUBTREE : // 4 possibilities if ( (p -> parent) -> left -> data == data) //delete left node
{ cout << "\nDeleting a left node \n" ; if (p -> left != NULL )
p-> parent -> left = p -> left ;
else if(p -> right != NULL)
p -> parent -> left = p -> right;
}
else if ( p -> parent -> right -> data == data ) // delete right node
{
cout << "\nDeleting a right node\n"; if (p -> left != NULL )
p -> parent -> right = p -> left ;
else if(p -> right != NULL)
p -> parent -> right = p -> right;
}
removed = true;
removeFromTreeInfo(p);
delete p ;
break ;
case WITH_BOTH_SUBTREES :
if ( data <= root -> data ) // left node
{
toDelete = getLargestRight(p->left, replaceData);
cout << "\nReplacing " << data << " with " << replaceData ; cout << " and deleting node at " << toDelete ; getch(); p -> data = replaceData ;
toDelete -> parent -> right = NULL ;
if ( toDelete -> left != NULL)
toDelete -> parent -> right = toDelete -> left ;
removeFromTreeInfo(toDelete);
delete toDelete;
}
else
if ( data > root -> data ) // right node
{
toDelete = getSmallestLeft(p, replaceData);
cout << "\nReplacing " << data << " with " << replaceData ; cout << " and deleting node at " << toDelete ; getch(); p -> data = replaceData ;
toDelete -> parent -> left = NULL ;
removeFromTreeInfo(toDelete);
delete toDelete;
}
removed = true;
break;
case ROOT:
if( p -> left) // left subtree exists
{
p -> data = p -> left -> data ;
cout << "\nLeft. Root replaced with " << p -> data << "\n" ; getch(); toDelete = getLargestRight(p -> left, replaceData) ;
cout << "\nfn returned Deleting node " << toDelete << " with data " << toDelete -> data << "\n"; getch(); toDelete -> parent -> right = NULL ;
if ( toDelete -> left != NULL)
toDelete -> parent -> right = toDelete -> left ;
p -> left -> data = replaceData ;
cout << "\ndeleting " << toDelete<< "\n" ; getch(); removeFromTreeInfo(toDelete); delete(toDelete); } else if ( p -> right) // suppose the root does not have a left node ??
{
p -> data = p -> right -> data ;
cout << "\nRight Root replaced with " << p -> data << "\n" ; getch(); toDelete = getSmallestLeft(p -> right, replaceData) ;
p -> right -> data = toDelete -> data ;
if ( toDelete -> right)
toDelete -> parent -> left = toDelete -> right;
removeFromTreeInfo(toDelete);
delete(toDelete);
}
else // the root is a single node
{
cout << "The root is a leaf root!! \n\nDeleting root will remove the tree itself\n" ; getch(); toDelete = root ; removeFromTreeInfo(toDelete); delete toDelete ; } getch(); removed = true ; break ; default: removed = false; } return removed; } int displayMenu() { int option ; clrscr(); int y = 3 ; int x = 30 ; gotoxy(x, y); cout << "Binary Search Tree"; y = 10; x = 20; gotoxy(x, y); cout << "1. Insert Data"; y = y + 5; gotoxy(x, y); cout << "2. Print Sorted Data "; y = y + 5; gotoxy(x, y); cout << "3. Print Tree Information"; y = y + 5; gotoxy(x, y); cout << "4. Delete Data"; y = y + 5; gotoxy(x, y); cout << "5. Close Application"; y = y + 10; gotoxy(x, y); cout << "Enter Menu option : "; cin.clear(); cin >> option ;
return option ;
}
main()
{
clrscr();
bSearchTree tree;
int data , option = 1;
while (option != 5)
{
clrscr();
option = displayMenu();
char ans = 'y' ;
switch (option)
{
case 1 :
clrscr();
while(ans == 'y' || ans =='Y')
{
cout << "\nEnter data to insert "; cin >> data;
tree.insert(data);
cout <<"1 node inserted\n\n"; cout<<"One more insert?? "; cin >> ans;
}
break;
case 2 :
clrscr();
cout << "Inserted Data in sorted order as follows:-\n\n" ; tree.print(); getch(); break; case 3 : tree.printTreeInfo(); getch(); break; case 4 : int data ; clrscr(); tree.print(); cout << "Enter data to delete " ; cin >> data ;
tree.remove(data);
getch();
break;
case 5:
default : break;
}
}
getch();
return 0 ;
}
=================================================================================
It was fun and lot of skill sharpening. Any one who wants to master coding skill must take up data structures like lists and trees - endless challenges here. And when I am playing the Oh so very addictive - Angry Birds game , I am wondering at the logic of the code behind. It must have been maddening to develop such an application.
One important learning was how to give a very simple public interface to a class and make the public functions call complex private functions, passing parameters which may be private variables. I liked that. Like tree.print() calls another function which requires the tree root to be passed to it as a parameter. On a public interface tree.print() is simpler than tree.print(tree.root) !!
When I copy/pasted a perfectly indented code into the blog editor, the final look of the code is a bit messed up in places and I am trying to figure out why it is so.