04 binarytrees

Upload: amitfegade121

Post on 07-Jan-2016

215 views

Category:

Documents


0 download

DESCRIPTION

Binary tree notes

TRANSCRIPT

DS - DSAA ( Common Topics )

4. Binary Trees

A tree consists of a finite set of elements, called nodes, and finite set of directed lines called branches, that connect the nodes. The number of branches associated with a node is called Degree of a node. If a tree is not empty then the first node is called a root.

Binary tree is a tree with all the nodes having degree not more than two. It consists of a root node with one or two branches. These branches are also called left or right subtrees. Each branch can consist of further one, two or no branches (i.e. subtrees). A tree having no nodes is called null tree. A binary tree is shown diagrammatically as follows.

Fig. 1

In the above figure a tree with 6 nodes is shown. Node with value A is at the root of the tree or we can say A is a root node. Nodes B and C appear immediately in the left and right subtrees of node A. Here B is called Left child and C is called Right child of node A. Similarly D and E are the left and right children of B. Also, A is called father of B and C. A node that has no children is called a Leaf or a Leaf node. In above figure F,D and E are the leaf nodes.

If every non-leaf node in a binary tree has non-empty left and right subtrees, the tree is termed as Strictly Binary Tree.

The root of a binary tree is said to be at level 0. Level of its children is 1. Thus, level of any node in a tree is one more than level of its father. Nodes B and C are at level 1. Nodes D,E and F are at level 2.

Depth or Height of a binary tree is the maximum level of any leaf node in the tree.

Complete Binary Tree is a strictly binary tree of depth d whose all leaves are at level d.

Binary Tree structure ( Binary Tree Representations ) :

From above discussion it is clear that a tree consists of multiple nodes. Each node must contain the data to be stored and tow links, one to the left child (subtree) and other to the right child (subtree). Some implementations also use a link for a father node.

1. Linked (i.e. Node) implementation of a Binary Tree :

Node of a Binary tree holds three major parts (fields).

Data field : Holds data for the element.

Link Fields : There are two link fields i.e. pointers for the two sub-trees (Left and Right subtrees) of the current Node. If a particular subtree (say Left subtree ) does not exist for a node then that pointer will hold NULL link. A node for a Binary tree can be implemented by a following structure definition,

struct Node

{

int data;

struct Node *left, *right;

};The above node consists of an int no as data field and two pointer variables

left and right that are used to store the references for the left and right subtrees. Following figure shows Binary tree with Six nodes.

Node with value 10 is Root of the Tree.

12, 5, 20 are leaf nodes.

Depth (Height) of the tree is 3.

10

15

7NULL

2. Array implementation of Binary Trees:

Nodes in the tree are numbered in a particular sequence. The root element is stored at index 0. In the next level the numbering will start from the left-most node and the nodes will be numbered in that order.

An almost complete binary tree as shown in figure below can be represented using an array.

0

1

2

3 4 5

Thus if the tree has 6 nodes then root will be numbered 0 and the node that is at the right-most position in the last level will be numbered as 5.

In array implementation, the information will be stored in the array elements in a continuous manner and the numbers discussed above will be the indexes of the different nodes in array. The above tree can be represented in array as follows,

ABCDEF

Positions of Child Nodes : The node at position p has two sons (i.e. children) at positions 2p+1 and 2p+2. The relationship is shown with the arrows in above figure.

In case of other trees (trees that are not complete or almost complete) as shown in the figure (a) below, its almost complete tree is imagined as shown in fig.(b) below. The tree in array will be as shown in figure (c) with some empty array elements.

Fig. a

Fig. b

01 2 3 4 5

ABCnullnullD Fig. c

If in a particular level child-nodes are present on right but left part is empty then those absent child nodes are also numbered.

Thus the array implementation has to indicate the null nodes by some means.

This is one of the drawbacks of Array implementation of linked list.

Tree is a collections of elements in which one element is linked to further two or more elements. Linked list is a linear structure, where as tree is non-linear structure.Common operations on binary trees:

( Linked implementation of Binary Tree )

Node structure (as given above ).For following operations and corresponding C functions we will assume a global variable (pointer) for Root of the tree as follows,

struct Node * root;

Binary Tree Traversals:

Tree traversal means moving through all the nodes or visiting all the nodes in a tree.

Binary tree traversals require that each node of a tree be processed once and only once in a predefined sequence.

While traversing the tree one can process the data in the node according to the requirement of a program. For example one can traverse a tree to display all the positive numbers stored a tree or to get a sum of all the even numbers in a tree.

All the traversal methods read the root node reference and then using links in the nodes move through the entire Tree.

According to the order in which nodes are processed, there are following types of Tree Traversals :

1. Breadth First Traversal ( BFT ) :

2. Depth First Traversal ( DFT ) :

a. In-order Traversal, b. Pre-order Traversal, c. Post=order Traversal

Consider following tree with the following values: A, B, C, D, E, F, G.

Fig 7Breadth-First Traversal (BFT):

In this traversal, root is processed first then each level of Tree is processed from the second level to the height of the tree.

Each level is completely processed before the next level is started. Each level is traversed from left to right.

For this purpose we use a Queue (since its a FIFO list) to store child node references.

The algorithm for this type of traversal is as shown below,

A-1

Algo: BFTraverse

1.Read a root Node reference

2. Create an empty Queue

3.Store the root reference in a Queue

4.While Queue has nodes, repeat following

a. Remove first element (node reference) from queue

b.Process the node (i.e. node referred by the element)

c.If left child of the node exists

Add left child link in a queue

d.If right child of the node exists

Add right child link in a queue

3. Go to step 4

The algorithm assumes that the Queue holds links (i.e. pointers) for tree nodes.Traversal Path : A B F C D G - E

Function :

// Traversing a Tree (Breadth First Traversal )

void BreadthFirstTrav( )

{

if( root != NULL )

{

struct Node *r =root;

Add( r );

while( ! IsQEmpty() )

{

r = Remove();

printf("%d\t", r->data );

if( r->left != NULL )

Add( r->left );

if( r->right != NULL )

Add( r->right );

}

}

else

printf("Tree is empty\n");

}Depth First Traversals (DFT):

Figures below show the traversal sequences for these three types. Node is shown with letter N in circle, Left subtree with letter L and Right subtree with letter R.

Pre-order (NLR)

In-order (LNR)

Post-order (LRN)

8-a

8-b

8-c

Pre-order Traversal:

In the preorder traversal current node is processed first, then its left and right subtrees are traversed. This is done for every node in the path. The process is repeated till the entire tree is traversed. This, can be achieved with a following recursive algorithm. The input to the algorithm is root node reference.

A-2

Algo: Preorder( Node reference )

1.If Node reference is not null

a. Process the current node

b. Taverse left-subtree of the current node

c. Traverse right-subtree of the current node

The Pre-order traversal path : A B C- E- D F - GFunction :void Preorder( struct Node *r )

{

if( r != NULL )

{

printf( "%d\n", r->data );

Preorder( r->left );

Preorder( r->right );

}

}

In-order Traversal:

Inorder traversal traverses the left subtree first then processes the current node and then traverses the right subtree. The recursive algorithm for the inorder traversal is as follows,

A-3

Algo: InTraverse( Node reference )

1.If Node reference is not null

a.Traverse left-subtree of the current node

b.Process the current node

c.Traverse right-subtree of the current nodeIn-order Traversal path : E C B D A F - G Function :void Inorder( struct Node *r )

{

if( r != NULL )

{

Inorder( r->left );

printf( "%d\n", r->data );

Inorder( r->right );

}

}

Post-order Traversal:

In post-order traversal the root (current node ) is processed after the subtrees are processed.

The recursive algorithm for post-order traversal is as follows,

A-4

Algo: PostTraverse( Node reference)

1.If the node reference is not null

a. Traverse the left-subtree of a current node

b. Traverse yhe right-subtree of a current node

c. Process the current node

Post-order traversal path : E C D B F G - AFunction :void Postorder( struct Node *r )

{

if( r != NULL )

{

Postorder( r->left );

Postorder( r->right );

printf( "%d\n", r->data );

}

}Inorder Traversal using Stack:

The recursion internally uses stack (in computer system memory). The recursive functions can be replaced by iteration, in some cases, by using a (user defined) stack. The method inTraverse()given below can be added to the BinaryTree class.The algorithm uses a stack of Node reference variables,

A-5

Algo. InTraverse( Root Node )

1. Create an empty stack to hold Node pointers.

2. read the root pointer into a variable

3. Repeat following steps

a. While null reference not found

i. Push the pointer to the stack

ii. Read the left child link of the node.b. If stack is not empty do following

i. Pop a pointer from stack

ii. Display the node given by pointeriii. Read the right child link from this node

4. go to step 3 if stack has elements or current pointer is not null

The algorithm assumes a Stack of tree node pointers.

Function :

void InTraverse( )

{

struct Node *r = root;

do

{

while( r != NULL)

{

Push( r );

r = r->left;

}

if( ! IsStackEmpty() )

{

r = Pop();

printf("%d\t" , r->data );

r = r->right;

}

}while( !IsStackEmpty() || r!= NULL );

}

Threaded Binary trees:

In the previous topics we discussed the most important operation of binary tree i.e. tree traversals using depth-first technique. It uses a recursive function to traverse a given tree. The recursion involves multiple function calls and its an inefficient process. The more efficient alternative is use of stack. The reason we use recursion or a stack is that, at each step, we cant access the next node in a sequence directly (i.e. we have to move backwards and retrieve a reference from a stack to the node which we want to access next). This is called backtracking. For example in inorder tree traversal we follow the left child links to get the leftmost leaf-node. When we get a leftmost leaf-node, we begin backtracking to process the right subtree that we bypassed on our way down. This especially inefficient when the parent node has no right subtree.

Fig. 9Consider inorder traversal for the tree shown above. We visit the left most leaf B and after processing it move to C and for this we use recursion or stack. Note that when we are processing B we do not need recursion or stack because Bs right subtree is empty. Similarly after processing node D using recursion or stack we must return to C before moving to A. Here again we need not visit C.

From this example, it is clear that the nodes whose right subtree is empty create more work when we use stack or recursion.

The solution to this is Threaded tree. In a threaded tree, the null pointers are replaced by pointers to it successor node. In inorder traversals whenever right subtree pointer is empty, we can use a pointer to point to the successor of the node. In other words we can use right null pointer as a thread.

The figure below shows threaded binary tree with nodes.

Fig 10Binary Search Trees (BST):

Binary Search Tree is a binary tree in which left subtree of every node is either empty or contains key values less than the node and the right subtree is either empty or contains key values greater than or equal to the node.

Building a Binary Search Tree:

Suppose, we entered five numbers as: 10, 16, 6, 4, 8 to build a Binary Search tree. The steps to create the tree will be as follows.

13-a

13-b

13-c

13-d

13-e

To add a new node in the tree we have to find where the new node will be linked. The search starts from root of the tree.

Then according to value of new node we move towards left or right in a tree till empty subtree is found.

The new node is linked to last node found.

e.g to add node with value 8 to the tree in Fig.-13.d, we start checking from the root node i.e. value 10. Since the value to add is smaller than value in current node, we move to its left child. Left subtree of 10 is not empty. Here the value to add is larger than that in the current node. Hence we move to right child. Since the right subtree is empty (i.e. link is null), the new node with value 8 is linked to this node as a right child.

In Binary Search tree a new node is always added (or inserted) as a Leaf node.Binary Search Tree operations:

The node composition for this tree is same as that for general binary tree. All the operations (and algorithms) that we discussed so far for general binary tree are also applicable to the Binary Search Tree.

Binary Search tree has following additional operations.Searching a Node :

Note : (Imp )

In case of linear structures like arrays or linked lists multiple elements are stored one after the other. To search for a particular element we need to begin from the first element and go through the list linearly (sequentially). Consider we have a list of N (N=10) elements stored in any linear structure.

50, 35, 20, 75, 65, 80, 90, 60, 30, 45

To get any element from the list we need maximum N attempts to reach the element. If its a last element then search goes through N elements. In average case the search will take N/2 attempts. If above elements were stored in linear list, then to get the last element (here, 45) will take 10 attempts on average 5 attempts.Now consider the elements are stored in Binary Search Tree form as shown below.

Now, to search any element in a binary search tree, we begin from root of the tree and advance to left or right child if the current node is not the required node. For example to search the node with value 30, we will follow the path 1 shown in above figure. This will take 4 attempts to reach the node 30. Also to reach node 45 we follow the path 2 that will take 3 attempts and for reaching node 90, we follow path 3 that will take 4 attempts. Thus, it is clear that to reach any node starting from first node, the maximum attempts required will be equal to the depth (i.e. height) of the binary search tree, which is very fast compared to sequential searching. Hence, this type tree is called as Search Tree. The algorithm and function to search the node is given below. The search algorithm returns the pointer for the node if found else returns null pointer to indicate search failed.

Algo: Search Node( key value to search )

Returns: Reference for the required node

1.Read pointer for root node

2. While pointer is not null repeat following

a. If current node holds the required key then

stop search

b. Else

If the key is less than the key in current node,

Read its left child pointer

Else

Read its right child pointer.

3. Return the pointerFunction :

struct Node* SearchNode( int v )

{

struct Node *r = root;

while( r != NULL )

{

if( r->data == v )

break;

else

{

if( v < r->data )

r = r->left;

else

r = r->right;

}

}

return r;

}Inserting new node in BST:

New node is always inserted (i.e. added) in a binary search tree as a leaf node. Refer the example given in topic Building Binary search tree and figure 13.

Algo: Insert Node( value to add )

1. Create a new node with given value and put null in both left and right pointers.

2. If the tree is empty then,

a. make it a root node

3. Else

a. Read the root node pointerb. While pointer is not null, do followingi. Keep the current link aside as a parent node link

ii. If value is less than that in current node, then

1. Read pointer to left childiii. Else 1. Read pointer to right child

c. If value is less than the parent node value, then

i. make new node a Left child of parent

d. Else i. make new node a Right child of parent

Non-recursive function :void InsertNode(int v)

{

struct Node* nw, *cur=NULL, *par=NULL;

// allocate and create new Node

nw = (struct Node*)malloc(sizeof(struct Node));

nw->data = v;

nw->left = nw->right = NULL;

if( root == NULL) // if tree is empty

{

root = nw;

}

else // find the node, where to link new node

{

cur = root;

while( cur != NULL )

{

par = cur;

if( v < cur->data )

cur = cur->left;

else

cur = cur->right;

}

if( v < par->data )

par->left = nw;

else

par->right = nw;

}

}

Recursive function :

struct Node* RecInsert( struct Node* t, int v )

{

if( t == NULL )

{

t = (struct Node*) malloc( sizeof(struct Node) );

t->data = v;

t->left = t->right = NULL;

}

else if( v < t->data )

t->left = RecInsert( t->left, v );

else

t->right = RecInsert( t->right, v );

return t;

}Deleting a node:

To delete a node from a BST we have to first locate it. There are four possible cases in deleting procedure.

Consider a following BST.

a. Deleting a Leaf node : (A node to deleted has no child ): Here the corresponding child node link in the parent node is set to null.

Deleting node 12.

b. A node to be deleted has one sub-tree (Node has one child): There are two cases, the child node (of the node of the node to be deleted) may be present on the left or right side. If there is only a Left (Right) child, then we can simply update the corresponding child pointer in the parent of the node to be deleted.

Deleting node 50.

c. The node to be deleted has two children: In this case the in-order successor of the node (to be deleted) is found. The data in this node is kept in the node to be deleted. Then this in-order successor of the given node (node to be deleted) is actually deleted.

Deleting node 25.

Non-Recursive Function :void DeleteNode(int v)

{

// root is a tree root pointer

struct Node * curr, *sucr=NULL, *par=NULL;

curr= root;

if (curr == NULL) // Tree root is null

{

printf("Tree is empty\n");

return;

}

while( curr != NULL && curr->data != v )

{

// par keeps link for parent node of the node being checked

par = curr;

// Select a child (left or right) to search further

curr = v < curr->data ? curr->left : curr->right;

}

if( curr == NULL) // if node not found stop procedure

{

printf("\n\tNode not found in a tree\n");

return;

}

// If a node has 2 child nodes

if(curr->left != NULL && curr->right != NULL)

{

// Select in-order successor of the node to deleted

par = curr;

sucr = curr->right;

while( sucr->left != NULL)

{

par = sucr;

sucr = sucr->left;

}

// update data in node to deleted

curr->data = sucr->data;

// Let the successor be a cur. node. i.e. a node to be deleted

curr = sucr;

}

// If the node has no child (i.e the Node is a Leaf-node)

if(curr->right == NULL && curr->right == NULL)

{

if( curr == root ) //for a tree with single node

root= NULL;

else

{

if(par->left == curr)

par->left = NULL;

else

par->right = NULL;

}

return; // Stop procedure }

// if the node has only right child

if( curr->right != NULL && curr->left == NULL)

{

if( curr == root )

root = root->right;

else

{

if( par->left == curr )

par->left = curr->right;

else

par->right = curr->right;

}

return; // Stop procedure

}

// if the node has only left child

if( curr->right == NULL && curr->left != NULL)

{

if( curr == root )

root = root->left;

else

{

if( par->left == curr )

par->left = curr->left;

else

par->right = curr->left;

}

return; // Stop procedure }

}

Recursive Function :

struct Node* RecDelete( struct Node *r, int v)

{

struct Node *temp;

if( r == NULL )

printf("Given node not found\n");

else

{

if( v < r-> data)

r->left = RecDelete( r->left, v );

else if ( v > r->data )

r->right = RecDelete( r->right, v );

else

{ // node with two children

if( r->left != NULL && r->right != NULL )

{

temp = r->right;

while( temp->left != NULL )

{

temp = temp->left;

}

r->data = temp->data;

r->right = RecDelete( r->right, r->data );

}

else

{

temp = r;

if( r->left == NULL )

r = r->right;

else if( r->right == NULL )

r = r->left;

free( temp );

}

}

return r;

}

}Binary Tree applications:

1. Expression Trees:

We will consider expressions with only binary operators in the form:

operand1 operator operand2

An expression tree is a binary tree with following properties:

1. Each leaf is operand.

2. The root and internal nodes are operators.

3. Sub-trees represent sub-expressions and the root (of the subtrees) holds the operator.

4. Given an expression tree, the three standard traversals (inorder, postorder and preorder) represent the three different expression formats: infix, postfix and prefix.Building and Expression tree for a given expression :

Expression tree for an expression A * ( B + C ) - D is built using following steps:

1. Number the operators with their operating order in the expression. ( e.g. in above expression order will be : + , * , -

2. Choose the operator that will operate at the end. Here, (- D) is last evaluated. Make root node to hold operator -, put right operand ( D ) on the right , and the left operand i.e. A*(B+C) into right child.

3. Now the child nodes of node with - are holding the two expressions [ A*(B+C) ] and [ D ]. Use same steps as given in step 1, to split the expressions into further two parts. The right subtree cant be split since it holds only D. Split left tree at the operator *, since, in the expression A*(B+C), it will operate last.

4. Repeat these steps till all child nodes have single operands.

The steps in building the expression tree for the above expression are shown with the following figure.

Infix traversal: This produces the infix expression. When we generate an infix expression, we must add opening parenthesis at the beginning of each expression and a closing parenthesis at the end of each expression. This is required since, the root of the tree and each of its subtrees represents a sub-expression. The algorithm for infix traversal of an expression tree is as follows,

Input: Root of the expression tree.

Output: Infix expression.

1. If link is not null

a. If node has operand

Print operand

b. Else

Print open parenthesis

Traverse left subtree

Print data in the node

Traverse right subtree

Print close parenthesis

End of If

2. Stop.

Evaluation of an expression:

The algorithm to evaluate an expression stored in a binary tree is as follows,

Input: Tree node

Output: Returns value of the expression

If symbol in a node is an operand

Return the float value of symbol

Else

Evaluate left subtree

Evaluate right subtree

Read the operator in the node

Operate the operator on the two operands to get value of expression

Return the value.

Note : The algorithm assumes tree nodes hold symbols or digits i.e. characters.The method works on the basis of post-order traversal of a binary tree. It recursively evaluates the left and right subtrees and then applies the operator (stored in the root of the two expressions). Functions for different operations on Expression Tree :

int IsOperand(char c) // function to check for whether a symbol is an Operand{

if( c>='0' && csymb = c;

nw->left = nw->right = NULL;

if( IsOperand(c))

Push( nw );

else

// if operator: build a tree using two subtrees from two operands on stack

{

re = Pop( );

le = Pop( );

nw->left = le;

nw->right = re;

Push( nw );

}

i++; // advance to next symbol in the given expr.

}

root= Pop( ); // get the root i.e. tree}float Operate(char ch, float op1, float op2)

{

switch( ch )

{

case '+' : return op1 + op2 ;

case '-' : return op1 - op2 ;

case '*' : return op1 * op2 ;

case '/' : return op1 / op2 ;

}

}float EvalTree( struct Node *t ) //Evaluates the expr. in the tree{

float opnd1, opnd2, v;

char ch;

ch = t->symb;

if( t->left== NULL && t->right==NULL)

return ( ch -'0' );

else

{

opnd1 = EvalTree( t->left );

opnd2 = EvalTree( t->right);

v = Operate( ch, opnd1 , opnd2 );

}

return v;

}

void Inorder( struct Node *r)

{

if( r != NULL )

{

if( r->left != NULL && r->right != NULL )

printf(" ( " );

Inorder( r->left );

printf("%c ", r->symb );

Inorder( r->right );

if( r->left != NULL && r->right != NULL )

printf(" ) " );

}

}

void Preorder( struct Node *r)

{

if( r != NULL )

{

printf("%c ", r->symb );

Preorder( r->left );

Preorder( r->right );

}

}

Note : Above functions also use a Stack of Nodes.Huffman Code (Huffman Algorithm) :

The ASCII characters are 8-bit long (and Unicode characters are 16 bit long) i.e. each character corresponds to one byte (or two bytes in case of Unicode char) of memory i.e. the character length does not vary. Thus, a data or file having 50 characters will take 50 bytes of memory or storage space on disk.

Huffman code gives a method for storing the same amount of data in smaller space. In Huffman code we assign shorter codes to the characters that occur more frequently and longer codes to those that occur less frequently. For example characters E and T occur more frequently in any text in English language. These characters can be assigned only one bit. (e.g. 0 for E and 1 for T). The group characters that occur less than E and T (e.g. A,O, R and N) can be assigned 2 bits and so on. Thus, using the Huffmans code the data storage space can be reduced. What is Huffman Code and its use :

Huffman Coding algorithm is used for Data Compression.

For creating codes, it uses Binary tree of characters: called as Huffmans Tree.

Large data (like data in file) can be compressed by using Huffmans codes for the characters in the data.

The Compressed data will serve following purposes:

Occupy less amount of space on storage devices ( disk, pen drives etc.).

Since data is in compressed it can much quickly transferred over networks.Since, data is coded, it safe to handle or send the data over internet.

Same codes can be used to de-compress the data and get original data back.

To assign codes to the characters following steps are taken:

1. Assign weights to each character using the frequency of their occurrences.

2. Arrange them in the ascending order of their frequencies.

3. Build a tree (called Huffman Tree) using the characters where the characters will always appear at the leaf of the tree. Properties of Huffmans tree :Each nodes in the tree hold a character and its frequency.

The leaf nodes hold the characters for which coding is to be done.

Each internal node holds the sum of the frequencies of it subtrees.

The branches are assigned value 1 or 0 : Left branch:0, Right branch: 1

The code for each character is generated by tracing the branches from Root to the character(leaf node). While tracing the path, branch codes (i.e. 1 / 0) are collected. This sequence of 1s and 0s forms a code for the character.

Huffman Algorithm (for building the Tree ) :

The actual Algorithm for building an Huffman Code Tree is as follows.

Here, a Huffman tree is built which is a binary tree. Each tree node holds character and frequency of occurrence as data field. We assume a min-priority queue to hold tree nodes in ascending order of character frequency.

Algo:

Input : a string to form tree from

Output : Code tree for the string

4. Compute frequencies of occurrences of each character in the given string

5. Initialize an empty min Priority Queue to hold tree nodes (according to frequencies of chars.)6. For each (distinct) character in the given string do,

a. Create a node to hold character and its frequency

b. Insert the node into the Queue

7. While queue has at least one element

a. Remove node t1 from a queue. Store the frequency into f1

b. Remove (next) node t2 from a queue. Store the frequency into f2. (here f1freq = freqs[i];

nw->left = nw->right = NULL;

Add( nw );

}

}

// While Q has only at least one node left

while( 1 )

{

t1 = Remove(); // Remove node with min freq.

if( IsEmpty() )

{

root = t1;

break;

}

f1 = t1->freq;

t2 = Remove(); // Remove node with 2nd min freq.

f2 = t2->freq;

// build a tree nw with t1 as left subtree and t2 as right subtree

// and root with freq. = f1 + f2

nw = (struct Node*) malloc( sizeof( struct Node) );

nw->chr = '\0'; // parent node holds null char

nw->freq = f1+f2;

nw->left = t1;

nw->right = t2;

Add( nw );

}

}AVL-Trees :

Binary Search trees are used for searching data using the key values stored in its nodes. In some cases binary search trees may not give satisfactory searching times, because of imbalance in its subtrees.

In case of Binary Search Tree, if multiple nodes are created by reading keys one by one, it can happen that the height of the one of the sub-trees (say right sub-tree) will go on increasing and the other (say left) may not increase at all or may increase by very few levels as compared to the other (i.e. right). AVL tree is a Balanced Binary Search tree. ( The name comes from the two mathematicians, G.M. Adelson-Velskii, and E.M. Landis, who created it).

This means, AVL trees are Binary Search Trees in which height of any two sub-trees (of any node) differ no more than one.

Requirement : Consider normal Binary Search tree. For numbers inserted in the following order

45, 60, 12, 25, 65, 75, 55, 90, 110

the Binary Search Tree will be as follows,

To locate any key in the left sub-tree it takes maximum three attempts, and to search any key in the right sub-tree it takes maximum six attempts.

The AVL tree for the above sequence of numbers will be as follows, which will give better results for searching data.

Thus, AVL tree is a Binary search tree which is either empty or always has all sub-trees as AVL trees i.e. height balanced trees.

Balance factor of any tree is the difference between the height of the left subtree and the height of right subtree.

In case of AVL trees the balance factor cant be greater than 1. i.e. balance factor for AVL trees is 0, 1, -1.

From the balance factor of the tree one can use three different terms for AVL trees: LH i.e. Left High, to indicate that the left subtree is higher, RH i.e. Right High to indicate the right subtree is higher and EH i.e. Even High to indicate that both subtrees have same height.

(In exam, for notes on AVL tree, write above part + any one or two balancing methods.)Balancing an AVL tree: ( Rotating tree for Balancing )Whenever we insert a node into a tree or delete a node from a tree, the resulting tree may be unbalanced. We have to balance it to keep its structure intact. Balancing of AVL tree is done by rotating nodes either towards left or right.

AVL tree has four different unbalanced status,

Single Rotation :

1) Left of Left:

The subtree of a Left High tree has become Left high. This type of tree is balanced by rotating the out of balance node to the right. Lets insert a node 12 into a tree with two nodes 20 and 18 as shown in fig.a below. In a simple example below we rotate (out of balance) node 20 on right to balance the tree.

Inserting node 12

(a1) After inserting 12

(a2) After rotation

Now consider a following an example. Here, also a node 18 is rotated right to balance the tree.

Inserting node 4

In the process the node 12 becomes a root node and we attach its right sub-tree 14 to the left of 18.

2) Right of Right:

The subtree of a Right High tree has become Right high. e.g. insert a node 20 on the right of right high tree (fig.a). Here, we rotate the root node 12 (which is unbalanced) on the left to balance the tree. See the example below,

In the process the node 18 becomes the new root node. The left sub-tree of 18 is connected as right subtree of old root (i.e.12)

Double Rotation :

In following two cases we require to rotate the tree twice to balance it.

3) Right of Left:

The subtree of a Left High tree has become Right high. We first consider simple example in which tree is Left-high with only two nodes (12 and 4), and it its left sub-tree (here 4) becomes right-high when a new node (here 8) is added.

To balance it we first rotate left sub-tree (which has become RH) to the left and then we rotate the tree (here 12) to the right. See the example below,

4) Left of Right:

he subtree of a Right High tree has become Left high. Here the right subtree is rotated towards right then we rotate its upper node (here root18) to left to balance the tree.

Example: Constructing an AVL tree for given month names (assume alphabetical order). The names given are, MAR, MAY, JUN, JUL, JAN, FEB, AUS, SEP, NOV, OCT, APR, DEC. The steps are shown below.

Multi-way trees:

( Following two paragraphs are only for reading )

Binary trees ( Binary search tree, AVL trees etc) have nodes with maximum two subtrees, i.e. the out-degree of each node is restricted to two. As the tree grows their height become significant. e.g. a tree with 1000 entries has a height of at least 10. If the trees are un-balanced, their heights can be significantly larger.

Multi-way tree retain some of the properties of Binary search trees, but its outdegree is not restricted to 2. Also, every node in multiway tree will have multiple entries.

These are search trees, used in internal search trees, spelling checkers, and external file indexes.

m-Way search trees:An m-way tree is search tree in which each node can have zero to m sub-trees, where m is defined as order of the tree. Following are the properties of m-way tree.

1. Each node has 0 to m subtrees.

2. Given a node with k