Data Structures Unit 3
Data Structures Unit 3
25.1 INTRODUCTION
A tree is a collection of nodes. The collection can be empty; otherwise, a tree consists of
a distinguished node r, called the root, and zero or more nonempty (sub) trees T1, T2, . . . , Tk,
each of whose roots are connected by a directed edge from r.
The root of each subtree is said to be a child of r, and r is the parent of each subtree root.
From the recursive definition, a tree is a collection of N nodes, one of which is the root,
and N − 1 edges. That there are N − 1 edges follows from the fact that each edge connects some
node to its parent, and every node except the root has one parent.
Root
The root of a tree is the node with no parents. There can be at most one root node in a
tree.
Node
Item of information.
Siblings
Edge
A path from node n1 to nk is defined as a sequence of nodes n1, n2, . . . , nk such that ni is
the parent of ni+1 for 1 ≤ i < k. In a tree there is exactly one path from the root to each node.
Length
The length of the path is the number of edges on the path, namely, k − 1. There is a path
of length zero from every node to itself. In a tree there is exactly one path from the root to each
node.
Degree
The number of subtrees of a node is called its degree. The degree of the tree is the
maximum degree of any node in the tree.
Level
The level of a node is defined by initially letting the root be at level one, if a node is at
level L then its children are at level L + 1.
Depth
For any node ni, the depth of ni is the length of the unique path from the root to ni. Thus,
the root is at depth 0. The depth of the tree is the maximum depth among all the nodes in the
tree.
Height
The height of ni is the length of the longest path from ni to a leaf. Thus all leaves are at
height 0. The height of a tree is equal to the height of the root.
Note: For a given tree, depth and height returns the same value. But for individual nodes
we may get different results.
25.3 EXAMPLES
Root – A
Leaf node - E, J, K, H and I
Siblings - B, C, D are siblings of A, and E, F are the siblings of B
Ancestor - A, C and G are the ancestors of K
Depth of G - 2, A – C – G
If every node in a tree has only one child (except leaf nodes) then we call such trees skew
trees. If every node has only left child, then we call them left skew trees. Similarly, if every node
has only right child then we call them right skew trees.
1. Define tree.
2. Define root of a tree.
3. Define child of a tree.
4. Define leaves of a tree.
5. Define siblings of a tree.
6. Define path of a tree.
7. Define length of a tree.
8. Define depth of a tree.
9. Define height of a tree.
A binary tree is a tree in which no node can have more than two children (each node has
zero child, one child or two children). Maximum number of nodes at level i of a binary tree is
2i-1.
We can visualize a binary tree as consisting of a root and two disjoint binary trees, called
the left (TL) and right (TR) subtrees of the root.
struct node
{
struct node *left;
int data;
struct node *right;
};
Here height is 3. No. of nodes in full binary tree is: 23+1 – 1 : 15 nodes.
A complete binary tree of height h has between 2h and 2h+1 - 1 nodes. In the bottom level
the elements should be filled from left to right.
A full binary tree can be a complete binary tree, but all complete binary tree is not a full
binary tree.
A binary tree is called strict binary tree if each node has exactly two children or no
children.
For the following properties, let us assume that the height of the tree is h. Also, assume
that root node is at height zero.
The number of nodes n in a full binary tree is 2h+1 – 1. Since, there are h levels we
need to add all nodes at each level [20 + 21+ 22 + + 2h = 2h+1 – 1].
The number of nodes n in a complete binary tree is between 2h (minimum) and 2h+1
– 1 (maximum).
The number of leaf nodes in a full binary tree is 2h.
The number of NULL links (wasted pointers) in a complete binary tree of n nodes is
n + 1.
Following are the some of the applications where binary trees play an important role:
27.1 INTRODUCTION
Linear Representation
Linked Representation
The elements are represented using arrays. For any element in position i, the left child
is in position 2i, the right child is in position (2i + 1), and the parent is in position (i/2).
The elements are represented using pointers. Each node in linked representation has
three fields:
28.1 INTRODUCTION
The leaves of an expression tree are operands, such as constants or variable names, and
the other nodes contain operators.
We now give an algorithm to convert a postfix expression into an expression tree. Since
we already have an algorithm to convert infix to postfix, we can generate expression trees from
the two common types of input. The method we describe strongly resembles the postfix
evaluation algorithm.
28.2.1 Example 1:
ab+c*
The first two symbols are operand, so create a one node tree and push the pointer on to
the stack.
Next '+' symbol is read, so two pointers are popped, a new tree is formed and a pointer
to this is pushed on to the stack.
Now '*' is read, so two trees are merged and the pointer to the final tree is pushed onto
the stack.
28.2.2 Example 2:
ab+cde+**
The first two symbols are operands, so we create one-node trees and push pointers to
them onto a stack.
Next, a '+' is read, so two pointers to trees are popped, a new tree is formed, and a pointer
to it is pushed onto the stack.
Continuing, a '*' is read, so we pop two tree pointers and form a new tree with a '*' as
root.
Finally, the last symbol is read, two trees are merged, and a pointer to the final tree is
left on the stack.
(a / (b * c / d + e / f * g))
28.2.4 Example 4:
(a + b * c) + ((d * e + f) * g)
28.2.5 Example 5:
(a + b * c) + (d * e / f − g)
29.1 INTRODUCTION
The process of visiting all nodes of a tree is called tree traversal. Each node is processed
only once but it may be visited more than once.
Inorder traversal
Preorder traversal
Postorder traversal
The inorder traversal of the binary tree for an arithmetic expression gives the
expression in an infix form.
29.2.1 Example
Inorder: 10 20 30
Inorder: A B C D E G H I J K
The preorder traversal of the binary tree for the given expression gives in prefix form.
29.3.1 Example
Preorder : 20 10 30
Preorder : D C A B I G E H K J
The postorder traversal of the binary tree for the given expression gives in postfix form.
29.4.1 Example
Postorder : 10 30 20
Postorder : B A C E H G J K I D
29.5 EXAMPLES
29.5.1 Example-1
Traverse the given tree using inorder, preorder and postorder traversals.
Inorder
o A+B*C–D/E
Preorder
o *+AB–C/DE
Postorder
o AB+CDE/-*
29.5.2 Example-2
Traverse the given tree using inorder, preorder and postorder traversals.
29.5.3 Example-3
Give the prefix, infix, and postfix expressions corresponding to the tree in following
Figure.
Prefix
o -**ab+cde
Infix
o a*b*c+d–e
Postfix
o ab*cd+*e-
29.5.4 Example-4
Traverse the tree given below using Inorder, Preorder and Postorder traversals.
Inorder
o DHBEAFCIGJ
Preorder
o ABDHECFGIJ
Postorder
o HDEBFIJGCA
Traverse the tree given below using Inorder, Preorder and Postorder traversals.
Inorder
o a*b+c*d+e
Preorder
o +*ab+*cde
Postorder
o ab*cd*e++
29.5.6 Example-6
Traverse the tree given below using Inorder, Preorder and Postorder traversals.
Inorder
o HDBEAFCG
Preorder
o ABDHECFG
Postorder
o HDEBFGCA
Traverse the tree given below using Inorder, Preorder and Postorder traversals.
Inorder
o 4251637
Preorder
o 1245367
Postorder
o 4526731
29.6 PROGRAM
#include <stdio.h>
#include <stdlib.h>
struct node
{
struct node *left;
int element;
struct node *right;
};
int main()
{
Node *Tree = NULL;
int n, i, e, ch;
printf("Enter number of nodes in the tree : ");
scanf("%d", &n);
printf("Enter the elements :\n");
for (i = 1; i <= n; i++)
{
scanf("%d", &e);
Tree = Insert(Tree, e);
}
OUTPUT
30.1 INTRODUCTION
The property that makes a binary tree into a binary search tree is that for every node, X,
in the tree, the values of all the keys in its left subtree are smaller than the key value in X, and
the values of all the keys in its right subtree are larger than the key value in X.
Fig. 30.2 Two binary trees (only the left tree is a search tree)
Note:
struct node
{
struct node *left;
int element;
struct node *right;
};
Following are the main operations that are supported by binary search trees:
Find operation is straightforward in a BST. Start with the root and keep moving left or
right using the BST property. If the data we are searching is same as nodes data then we return
current node. If the data we are searching is less than nodes data then search left subtree of
current node; otherwise search right subtree of current node. If the data is not present, we end
up in a NULL link.
30.5.1 Algorithm
30.5.2 Routine
This operation returns the position of the smallest element in the tree. To perform
findMin, start at the root and go left as long as there is a left child. The stopping point is the
smallest element.
30.6.1 Algorithm
30.6.4 Example
findMax routine return the position of largest elements in the tree. To perform a
findMax, start at the root and go right as long as there is a right child. The stopping point is the
largest element.
30.7.1 Algorithm
30.7.4 Example
To insert data into binary search tree, first we need to find the location for that element.
We can find the location of insertion by following the same mechanism as that of find operation.
While finding the location, if the data is already there then we can simply neglect and come out.
Otherwise, insert data at the last location on the path traversed.
30.8.1 Algorithm
30.8.2 Routine
30.8.4 Example
Deletion operation is the complex operation in the Binary search tree. To delete an
element, consider the following three possibilities.
Case 1:
o Node to be deleted is a leaf node (ie) no children.
Case 2:
o Node with one child.
Case 3:
o Node with two children.
Delete: 8
If the node has one child, it can be deleted by adjusting its parent pointer that points to
its child node.
To delete 5, the pointer currently pointing the node 5 is now made to its child node 6.
It is difficult to delete a node which has two children. The general strategy is to replace
the data of the node to be deleted with its smallest data of the right subtree and recursively
delete that node.
30.9.4 Example
To Delete 5.
To delete 25.
30.10 ROUTINE
30.11 APPLICATIONS
#include <stdio.h>
#include <stdlib.h>
struct node
{
struct node *left;
int element;
struct node *right;
};
typedef struct node Node;
Node *Insert(Node *Tree, int e);
void Find(Node *Tree, int e);
void FindMin(Node *Tree);
void FindMax(Node *Tree);
int main()
{
Node *Tree = NULL;
int n, i, e, ch;
printf("Enter number of nodes in the tree : ");
scanf("%d", &n);
printf("Enter the elements :\n");
for (i = 1; i <= n; i++)
{
scanf("%d", &e);
Tree = Insert(Tree, e);
}
do
{
printf("1. Find \n2. Find Min \n3. Find Max \n4. Exit\n");
printf("Enter your choice : ");
scanf("%d", &ch);
switch (ch)
{
case 1:
printf("Enter the element to find : ");
scanf("%d", &e);
Find(Tree, e);
printf("\n");
break;
case 2:
FindMin(Tree);
break;
case 3:
FindMax(Tree);
break;
}
} while (ch <= 3);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct node
{
struct node *left;
int element;
struct node *right;
};
typedef struct node Node;
int main()
{
Node *Tree = NULL;
Node *Result = NULL;
int n, i, e, ch;
printf("Enter number of nodes in the tree : ");
scanf("%d", &n);
printf("Enter the elements :\n");
for (i = 1; i <= n; i++)
{
scanf("%d", &e);
Tree = Insert(Tree, e);
}
do
{
printf("1. Find \n2. Find Min \n3. Find Max \n4. Exit\n");
printf("Enter your choice : ");
scanf("%d", &ch);
switch (ch)
{
case 1:
printf("Enter the element to find : ");
scanf("%d", &e);
Result = Find(Tree, e);
if (Result == NULL)
printf("Element is not found...!");
else
printf("Element is found...!");
printf("\n");
break;
OUTPUT
#include <stdio.h>
#include <stdlib.h>
struct node
{
struct node *left;
int element;
struct node *right;
};
typedef struct node Node;
int main()
{
Node *Tree = NULL;
int n, i, e;
printf("Enter number of nodes in the tree : ");
scanf("%d", &n);
printf("Enter the elements :\n");
for (i = 1; i <= n; i++)
{
scanf("%d", &e);
Tree = Insert(Tree, e);
}
printf("Tree elements in inorder :\n");
Display(Tree);
printf("\nEnter the element to delete : \n");
scanf("%d", &e);
Tree = Delete(Tree, e);
printf("Tree elements in inorder after deletion :\n");
Display(Tree);
return 0;
}
OUTPUT
1. What is a binary search tree? (or) When does a binary tree become a binary search tree?
2. Differentiate a binary tree from a binary search tree. (or) Compare binary tree and binary
search tree.
3. List out the steps involved in deleting a node from a binary search tree.
31.1 INTRODUCTION
An AVL (Adelson-Velskii and Landis) tree is a binary search tree with a balance
condition. The balance condition must be easy to maintain, and it ensures that the depth of
the tree is O(log N).
An AVL tree is identical to a binary search tree, except that for every node in the tree,
the height of the left and right subtrees can differ by at most 1. (The height of an empty tree is
defined to be -1.) In Figure 31.1 the tree on the left is an AVL tree but the tree on the right is
not. Height information is kept for each node (in the node structure).
Figure 31.1 Two binary search trees. Only the left tree is AVL.
A balance factor is the height of the left subtree minus height of the right subtree. For an
AVL tree all balance factor should be +1, 0, or -1. If the balance factor of any node in an AVL tree
becomes less than -1 or greater than 1, the tree has to be balanced by making either single or
double rotations.
Thus, all the tree operations can be performed in O(log N) time, except possibly insertion
and deletion. When we do an insertion, we need to update all the balancing information for the
nodes on the path back to the root, but the reason that insertion is potentially difficult is that
inserting a node could violate the AVL tree property. (For instance, inserting 6 into the AVL tree
in Figure 4.31 would destroy the balance condition at the node with key 8.) If this is the case,
then the property has to be restored before the insertion step is considered over. It turns out
that this can always be done with a simple modification to the tree, known as a rotation.
After an insertion, only nodes that are on the path from the insertion point to the root
might have their balance altered because only those nodes have their subtrees altered. As we
follow the path up to the root and update the balancing information, we may find a node whose
new balance violates the AVL condition. We will show how to rebalance the tree at the first (i.e.,
deepest) such node, and we will prove that this rebalancing guarantees that the entire tree
satisfies the AVL property.
Let us call the node that must be rebalanced α. Since any node has at most two children,
and a height imbalance requires that α’s two subtrees’ heights differ by two, it is easy to see
that a violation might occur in four cases:
1. An insertion into the left subtree of the left child of α
2. An insertion into the right subtree of the left child of α
3. An insertion into the left subtree of the right child of α
Cases 1 and 4 are mirror image symmetries with respect to α, as are cases 2 and 3.
Consequently, as a matter of theory, there are two basic cases. From a programming
perspective, of course, there are still four cases.
The first case, in which the insertion occurs on the “outside” (i.e., left–left or right– right),
is fixed by a single rotation of the tree. The second case, in which the insertion occurs on the
“inside” (i.e., left–right or right–left) is handled by the slightly more complex double rotation.
Figure 31.2 shows the single rotation that fixes case 1. The before picture is on the left
and the after is on the right. Let us analyze carefully what is going on. Node k 2 violates the AVL
balance property because its left subtree is two levels deeper than its right subtree (the dashed
lines in the middle of the diagram mark the levels). The situation depicted is the only possible
case 1 scenario that allows k2 to satisfy the AVL property before an insertion but violate it
afterwards. Subtree X has grown to an extra level, causing it to be exactly two levels deeper
than Z. Y cannot be at the same level as the new X because then k2 would have been out of
balance before the insertion, and Y cannot be at the same level as Z because then k 1 would be
the first node on the path toward the root that was in violation of the AVL balancing condition.
To ideally rebalance the tree, we would like to move X up a level and Z down a level. Note
that this is actually more than the AVL property would require. To do this, we rearrange nodes
into an equivalent tree as shown in the second part of Figure 32.2. Here is an abstract scenario:
Visualize the tree as being flexible, grab the child node k1, close your eyes, and shake it, letting
gravity take hold. The result is that k1 will be the new root. The binary search tree property tells
us that in the original tree k2 > k1, so k2 becomes the right child of k1 in the new tree. X and Z
remain as the left child of k1 and right child of k2, respectively. Subtree Y, which holds items that
are between k1 and k2 in the original tree, can be placed as k2’s left child in the new tree and
satisfy all the ordering requirements.
As a result of this work, which requires only a few pointer changes, we have another
binary search tree that is an AVL tree. This happens because X moves up one level, Y stays at
the same level, and Z moves down one level. k2 and k1 not only satisfy the AVL requirements,
but they also have subtrees that are exactly the same height. Furthermore, the new height of
the entire subtree is exactly the same as the height of the original subtree prior to the insertion
that caused X to grow. Thus no further updating of heights on the path to the root is needed,
and consequently no further rotations are needed. Figure 31.3 shows that after the insertion of
6 into the original AVL tree on the left, node 8 becomes unbalanced. Thus, we do a single
rotation between 7 and 8, obtaining the tree on the right.
SingleRotateWithLeft(Position K2)
{
Position K1;
K1 = K2 Left;
K2 Left = K1 Right;
K1 Right = K2;
K2 Height = Max(Height(K2 Left), Height(K2 Right)) + 1;
K1 Height = Max(Height(K1 Left), Height(K1 Right)) + 1;
return K1;
}
As we mentioned earlier, case 4 represents a symmetric case. Figure 31.4 shows how a
single rotation is applied.
SingleRotateWithRight(Position K1)
{
Position K2;
K2 = K1 Right;
K1 Right = K2 Left;
K2 Left = K1;
K2 Height = Max(Height(K2 Left), Height(K2 Right)) + 1;
K1 Height = Max(Height(K1 Left), Height(K1 Right)) + 1;
return K2;
}
Let us work through a rather long example. Suppose we start with an initially empty AVL
tree and insert the items 3, 2, 1, and then 4 through 7 in sequential order. The first problem
occurs when it is time to insert item 1 because the AVL property is violated at the root. We
perform a single rotation between the root and its left child to fix the problem. Here are the
before and after trees:
A dashed line joins the two nodes that are the subject of the rotation. Next we insert 4,
which causes no problems, but the insertion of 5 creates a violation at node 3 that is fixed by a
single rotation. Besides the local change caused by the rotation, the programmer must
remember that the rest of the tree has to be informed of this change. Here this means that 2’s
right child must be reset to link to 4 instead of 3. Forgetting to do so is easy and would destroy
the tree (4 would be inaccessible).
Next we insert 6. This causes a balance problem at the root, since its left subtree is of
height 0 and its right subtree would be height 2. Therefore, we perform a single rotation at the
root between 2 and 4.
The rotation is performed by making 2 a child of 4 and 4’s original left subtree the new
right subtree of 2. Every item in this subtree must lie between 2 and 4, so this transformation
makes sense. The next item we insert is 7, which causes another rotation:
Inserting the value 1 in the following AVL Tree makes AVL Tree imbalance.
Before After
Example 3 - Single Rotation (Case 4)
Inserting the value 10 in the following AVL Tree makes AVL Tree imbalance.
Before After
The algorithm described above has one problem: As Figure 31.5 shows, it does not work
for cases 2 or 3. The problem is that subtree Y is too deep, and a single rotation does not make
it any less deep. The double rotation that solves the problem is shown in Figure 31.6.
The fact that subtree Y in Figure 31.5 has had an item inserted into it guarantees that it
is nonempty. Thus, we may assume that it has a root and two subtrees. Consequently, the tree
may be viewed as four subtrees connected by three nodes. As the diagram suggests, exactly one
of tree B or C is two levels deeper than D (unless all are empty), but we cannot be sure which
one. It turns out not to matter; in Figure 31.6, both B and C are drawn at 1 ½ levels below D.
To rebalance, we see that we cannot leave k3 as the root, and a rotation between k 3 and
k1 was shown in Figure 31.5 to not work, so the only alternative is to place k2 as the new root.
This forces k1 to be k2’s left child and k3 to be its right child, and it also completely determines
the resulting locations of the four subtrees. It is easy to see that the resulting tree satisfies the
AVL tree property, and as was the case with the single rotation, it restores the height to what it
was before the insertion, thus guaranteeing that all rebalancing and height updating is
complete. Figure 31.7 shows that the symmetric case 3 can also be fixed by a double rotation.
In both cases the effect is the same as rotating between α’s child and grandchild, and then
between α and its new child.
DoubleRotateWithLeft(Position K3)
{
/* Rotation between K1 and K2 */
Position K1;
K1 Left = SingleRotateWithRight(K3 Left);
/* Rotation between K3 and K2 */
return SingleRotateWithLeft(K3);
}
DoubleRotateWithRight(Position K1)
{
/* Rotation between K2 and K3 */
K1 Right = SingleRotateWithLeft(K1 Right);
/* Rotation between K1 and K2 */
return SingleRotateWithRight(K1);
}
Next we insert 14, which also requires a double rotation. Here the double rotation that
will restore the tree is again a right–left double rotation that will involve 6, 15, and 7. In this
case, k1 is the node with item 6, k2 is the node with item 7, and k3 is the node with item 15.
Subtree A is the tree rooted at the node with item 5; subtree B is the empty subtree that was
originally the left child of the node with item 7, subtree C is the tree rooted at the node with
item 14, and finally, subtree D is the tree rooted at the node with item 16.
To insert 11, a single rotation needs to be performed, and the same is true for the
subsequent insertion of 10. We insert 8 without a rotation, creating an almost perfectly
balanced tree:
Let us consider how to balance a tree while inserting the numbers from I to 10.
Balanced Tree
Balanced Tree
Here the tree imbalances at the node 1, so the single rotation with left is performed.
Tree is imbalanced at node 3, perform the single rotation with left to balance it.
Balanced Tree
1. ..
2.
32.1 INTRODUCTION
The basic idea of the splay tree is that after a node is accessed, it is pushed to the root by
a series of AVL tree rotations. Notice that if a node is deep, there are many nodes on the path
that are also relatively deep, and by restructuring we can make future accesses cheaper on all
these nodes. Thus, if the node is unduly deep, then we want this restructuring to have the side
effect of balancing the tree (to some extent). Besides giving a good time bound in theory, this
method is likely to have practical utility, because in many applications, when a node is accessed,
it is likely to be accessed again in the near future. Studies have shown that this happens much
more often than one would expect. Splay trees also do not require the maintenance of height or
balance information, thus saving space and simplifying the code to some extent (especially
when careful implementations are written).
Splaying
The splaying strategy is similar to the rotation idea above, except that we are a little
more selectiveabout how rotations are performed. We will still rotate bottom up along the
access path. Let X be a (non-root) node on the access path at which we are rotating. If the parent
of X is the root of the tree, we merely rotate X and the root. This is the last rotation along the
access path. Otherwise, X has both a parent (P) and a grandparent (G), and there are two cases,
plus symmetries, to consider. The first case is the zig-zag case (see Fig. 4.48). Here X is a right
child and P is a left child (or vice versa). If this is the case, we perform a double rotation, exactly
like an AVL double rotation. Otherwise, we have a zig-zig case: X and P are both left children
(or, in the symmetric case, both right children). In that case, we transform the tree on the left
of Figure 4.49 to the tree on the right.
As an example, consider the tree from the last example, with a contains on k1:
The first splay step is at k1 and is clearly a zig-zag, so we perform a standard AVL double
rotation using k1, k2, andk3. The resulting tree follows:
The next splay step at k1 is a zig-zig, so we do the zig-zig rotation with k1, k4, andk5,
obtaining the final tree:
Although it is hard to see from small examples, splaying not only moves the accessed
node to the root but also has the effect of roughly halving the depth of most nodes on the access
path (some shallow nodes are pushed down at most two levels).
To see the difference that splaying makes over simple rotation, consider again the effect
of inserting items 1,2,3, . . , N into an initially empty tree. This takes a total of O(N), as before,
and yields the same tree as simple rotations. Figure 4.50 shows the result of splaying at the
node with item 1. The difference is that after an access of the node with item1, which takes N
units, the access on the node with item 2 will only take about N/2 units instead of N units; there
are no nodes quite as deep as before.
These figures highlight the fundamental and crucial property of splay trees. When access
paths are long, thus leading to a longer-than-normal search time, the rotations tend to be good
for future operations. When accesses are cheap, the rotations are not as good and can be bad.
The extreme case is the initial tree formed by the insertions. All the insertions were constant-
time operations leading to a bad initial tree. At that point in time, we had a very bad tree, but
we were running ahead of schedule and had the compensation of less total running time. Then
a couple of really horrible accesses left a nearly balanced tree, but the cost was that we had to
give back some of the time that had been saved. The main theorem, which we will prove in
Chapter 11, is that we never fall behind a pace of O(logN) per operation: We are always on
schedule, even though there are occasionally bad operations.
We can perform deletion by accessing the node to be deleted. This puts the node at the
root. If it is deleted, we get two subtrees TL and TR (left and right). If we find the largest element
in TL (which is easy), then this element is rotated to the root of TL, andTL will now have a root
with no right child. We can finish the deletion by making TR the right child.
The analysis of splay trees is difficult, because it must take into account the everchanging
structure of the tree. On the other hand, splay trees are much simpler to program than most
balanced search trees, since there are fewer cases to consider and no balance information to
maintain. Some empirical evidence suggests that this translates into faster code in practice,
although the case for this is far from complete. Finally, we point out that there are several
variations of splay trees that can perform even better in practice.
1. …
2.
6.1 INTRODUCTION
Binary heap is merely referred as Heaps. A heap is a tree with some special properties.
Heaps have two properties namely:
Structure property
Heap order property
A heap is a binary tree that is completely filled, with the possible exception of the bottom
level, which is filled from left to right. Such a tree is known as a complete binary tree.
It is easy to show that a complete binary tree of height h has between 2h and 2h+1-1 nodes.
The basic requirement of a heap is that the value of a node must be ≥ (or ≤) than the
values of its children. This is called heap property. This property allows the deleteMin and
deleteMax operations to be performed quickly has the minimum and maximum element can
always be found at the root. Thus, we get the findMin and findMax operations in constant time.
Based on the property of a heap we can classify heaps into two types:
Min heap
Max heap
The value of a node must be less than or equal to the values of its children.
The value of a node must be greater than or equal to the values of its children.
7.1 INTRODUCTION
To perform the insert and deleteMin operations ensure that the heap order property is
maintained.
To insert an element X into the heap, we create a hole in the next available location,
otherwise the tree will not be complete. If X can be placed in the hole without violating heap
order, then place the element X there itself. Otherwise, we slide the element that is in the hole's
parent node into the hole, thus bubbling the hole up toward the root. This process continues
until X can be placed in the hole. This general strategy is known as percolate up, in which the
new element is percolated up the heap until the correct location is found.
def percUp(self,i):
while i // 2 > 0:
if self.heapList[i] < self.heapList[i // 2]:
tmp = self.heapList[i // 2]
self.heapList[i // 2] = self.heapList[i]
self.heapList[i] = tmp
i = i // 2
def insert(self,k):
self.heapList.append(k)
self.currentSize = self.currentSize + 1
self.percUp(self.currentSize)
7.2.3 Example
To insert 10.
In min heap the minimum element is found in the root. When this minimum is removed,
a hole is created at the root. Since the heap becomes one smaller, makes the last element X in
the heap to move somewhere in the heap.
If X can be placed in hole without violating heap order property place it.
Otherwise, we slide the smaller of the hole's children into the hole, thus pushing the hole
down one level.
We repeat until X can be placed in the hole. This general strategy is known as percolate
down.
def minChild(self,i):
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1
def percDown(self,i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc
def delMin(self):
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval
7.3.1 Example
The hole's smallest children (20) is placed into the hole by pushing the hole one level
down.
Decrease key
Increase key
Delete
Build Heap
7.5 PROGRAM
class MinHeap:
def __init__(self):
self.heapList = [0]
self.currentSize = 0
def percUp(self,i):
while i // 2 > 0:
if self.heapList[i] < self.heapList[i // 2]:
tmp = self.heapList[i // 2]
self.heapList[i // 2] = self.heapList[i]
self.heapList[i] = tmp
i = i // 2
def percDown(self,i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc
def minChild(self,i):
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1
def delMin(self):
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval
def buildHeap(self,alist):
i = len(alist) // 2
self.currentSize = len(alist)
self.heapList = [0] + alist[:]
while (i > 0):
self.percDown(i)
i=i-1
minh = MinHeap()
minh.buildHeap([9,5,6,2,3])
print(minh.delMin())
print(minh.delMin())
print(minh.delMin())
print(minh.delMin())
print(minh.delMin())
2
3
5
6
9
1 INTRODUCTION
Splay trees are binary search trees with a self-adjusting mechanism (i.e.,) a frequently
accessed nodes are moved towards the root, which makes the further retrievals of the same
node to be efficient. A node is accessed either for search or for insertion, the newly accessed
node is pushed towards the root and the inactive nodes are moved further and further away
from the root.
2 SPLAY ROTATIONS
An insert or search operation on splay tree proceed as same as binary search tree. After
each operation, the tree is splayed with regard to a specific node.
Pushing the node towards the root is known as splay rotations. To move the accessed
node up by two levels, we need to keep track the path from the root to the accessed node.
Whenever the path turns left, it is named as zig and if it turns right, it is named as zag.
zig
zag
zig-zig
zig-zag
zag-zig
zag-zag
Fig. 1
Fig. 2
Fig. 3
Fig. 4
Fig. 5
Fig. 6
5 EXAMPLE
Insert 8
Fig. 7
Insert 17
Fig. 8
Fig. 9
Insert 14
Fig. 10
Insert 16
Fig. 11
Insert 15
Fig. 12
Fig. 13
To move the node 10 towards the root, we need to perform zag-zig rotation and zig
rotation.
Fig. 15
The node 10 is finally moved towards the root using zig rotation.
1 INTRODUCTION
The root node must have atleast two child nodes and atmost m child nodes.
All internal nodes other than the root node must have atleast m/2 to m non-empty
child nodes.
The number of keys in each internal node is one less than its number of child nodes,
which will partition the keys of the tree into subtree.
All internal nodes are at the same level.
Fig. 1
Here the non leaf nodes are represented as ellipses, which contain the two pieces of data
for each node.
Fig. 2
1. Represents the key value, which can be the largest element of the left sibling or the
smallest element of the right sibling. In this example we considered the smallest
element in the right sibling as the key element in the parent node.
Fig. 3
2. The dash line indicates that the node has only two children.
Fig. 4
Key element in the parent node Key element in the parent node has has
smallest element in the right sibling smallest element in the left sibling
3 OPERATIONS ON B-TREES
Insertion
Deletion
4 INSERTION
To insert a key k in the node X of the B-tree of order m can proceed in one of the two
ways.
Case 1:
When the node X of the B-tree of order m can accommodate the key K, then it is inserted
in that node and the number of child pointer fields are appropriately upgraded.
Example:
Case 2:
If the node is full, then the key K is apparently inserted into the list of elements and the
list is splitted into two on the same level at its median (K median). The keys which are less than
Kmedian are placed in the Xleft and those greater than Kmedian are placed at Xright.
The median key is not placed into either of the two new nodes, but is instead moved up
the tree to be inserted into the parent node of X. This insertion inturn will call case 1 and 2
depending upon whether the parent node can accommodate or not.
The deletion of a key K forkm a B-Tree order m may trigger many cases.
Case 1:
If the key K to be deleted belongs to a leaf node and its deletion does not result in the
node having less than its minimum number of elements. Then delete the key from the leaf and
adjust the child pointers.
To delete 17
Case 2:
If the key K belongs to a non leaf node. Then replace K with the largest key K Lmax in the
left subtree of K or the smallest key KRmin from the right subtree of K and then delete KRmin or
KLmax from the node, which in turn will trigger case 1 or 2.
To delete 25
The largest key KLmax, in the left subtree of 25 is replaced and then the key 23 is deleted
immediately since it is a leaf node.
If the key K to be deleted from a node leaves it with less than its minimum number of
elements, then the elements may be borrowed either from left or right sibling.
If the left sibling node has an element to spare, then move the largest key KLmax in the left
sibling node to the parent node and the element P in the parent node is moved down to set the
vacancy created by the deletion of K in node X.
If the left sibling node has no element to spare then move to case 4.
To delete 39
Fig. 15
Deleting the key 39 leaves the node less than its minimum number of elements.
Here so the largest key 23 from the left sibling is moved to the parent node and the
element 25 in the parent node is moved down to set the vacancy created by deleting 39.
Case 4:
If the key K to be deleted from a node X leaves it with less than its minimum number of
elements and both the sibling nodes are unable to spare an element. Then the node X is merged
with one ofthe sibling nodes along with intervening element P in the parent node.
92 B.BHUVANESWARAN | AP (SG) | CSE | Rajalakshmi Engineering College
Fig. 16
To delete 36
Deleting the key 36 leaves the nodes less than its minimum number of elements and
both the siblings are unable to spare. So the node containing key 36 is merged with the left
sibling and the intervening parent element 18.
Fig. 17
1 INTRODUCTION
An AVL tree is a binary search tree except that for every node in the tree, the height of
the left and right subtrees can differ by atmost 1.
A balance factor is the height of the left subtree minus height of the right subtree. For an
AVL tree all balance factor should be +1, 0, or -1. If the balance factor of any node in an AVL tree
becomes less than -1 or greater than 1, the tree has to be balanced by making either single or
double rotations.
(a)
(b)
(c)
Fig. AVL Tree
An AVL tree causes imbalance, when any one of the following conditions occur:
Case 1: An insertion into the left subtree of the left child of node α.
Case 2: An insertion into the right subtree of the left child of node α.
Case 3: An insertion into the left subtree of the right child of node α.
Case 4: An insertion into the right subtree of the right child of node α.
1. Single Rotation
2. Double Rotation
Single Rotation
General Representation
Case 4: An insertion into the right subtree of the right child of K1.
General Representation
Double Rotation
Case 2:
General Representation
Before After
General Representation
Before After
This can also be done by performing single rotation with left and then single rotation
with right.