Vous êtes sur la page 1sur 43

DEPARTMENT OF COMPUTER SCIENCE& ENGINEERING

Advanced Data Structures & Algorithms


LAB MANUAL
B.Tech II Year, I Semester Information Technology

Auroras Scientific & Technological Institute, Gagillapur

CONTENTS
S. No 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Topic List of Exercises Introduction to OOPS Introduction to Templates Exercise 1. Stack and Queue ADT using an Array Exercise 2. Stack and Queue ADT using Linked List Exercise 3. Deque using an Array and DLL Exercise 4. Binary search Tree Operations Exercise 5. Traversal of binary tree Exercise 6. BFS and DFS of graph Exercise 7. Sorting Methods Exercise 8. B-Tree Operations Exercise 9. AVL-Tree Operations Exercise 10. Kruskals Algorithm Exercise 11. Prims Algorithm Exercise 12. Dictionary ADT using Hashing Bibliography Page No. 5 7 11 13 15 17 19 21 23 27 29 33 35 37 39 43

LIST OF EXERCISES
S. No 1 2 3 4 5 6 7 8 9 10 11 12 Name of the Exercise Exercise 1. Stack and Queue ADT using an Array Exercise 2. Stack and Queue ADT using Linked List Exercise 3. Deque using an Array and DLL Exercise 4. Binary search Tree Operations Exercise 5. Traversal of binary tree Exercise 6. BFS and DFS of graph Exercise 7. Sorting Methods Exercise 8. B-Tree Operations Exercise 9. AVL-Tree Operations Exercise 10. Kruskals Algorithm Exercise 11. Prims Algorithm Exercise 12. Dictionary ADT using Hashing

Note: All the above programs are to be done using class templates in C++.

CHAPTER 1

Introduction to OOPS
CLASSES AND OBJECTS A class is a definition of an object. It's a type just like int. A class resembles a struct with just one difference: all struct members are public by default. All class members are private. Remember, a class is a type, and an object of this class is just a variable. General form of class declaration:
class class-name { private data and functions access-specifier: data and functions access-specifier: data and functions ... ... ... access-specifier: data and functions } object-list;

Functions declared within the class are known as member functions and variables are called data members. The members that have been declared as private must be accessed only within the class. The public members can be accessed from out side the class. A class is a logical abstraction, but an object has physical existence. The access-specifier can be: public: Allow functions or data to be accessible to other parts of the program. private: May be accessed only by other members of the class. protected: Used when inheritance is involved

Example:

To define a class, to store and retrieve data.

#include < isotream.h> # include < string.h> class student // student class declaration { char name [20] ; // data members int rollno; Public: void getdata ( char n[20] , int rn) ; // member functions or methods void putdata(void); };

void student : : getdata( char n[20], int rn ) { strcpy ( name , n) ; rollno = rn; } void student : : putdata ( void) { cout<< name is : <<name<<\n ; cout << roll number:<<rollno<<\n; } void main() { student s; cout << enter the details of students; s. getdata (ABC, 8508); s.putdata0; }

//creating objects //call member functions

INLINE FUNCTIONS It is a function that is expanded in line when it is invoked. That is the complier replaces the function call with the corresponding function code. The inline function is defined as follows.
inline return-type function_ name (arguments) { Function body; }

FRIEND FUNCTIONS A non-member function shared by classes, which can access the private data of a class, is called friend function. This function must be declared either with the key word friend or using the scope resolution operator. A function can be declared as friend in any number of classes. CONSTRUCTORS A constructor is a special member function whose task is to initialize the objects of its class name. It constructs the value of data members of the class. Example: A constructor is declared & defined as follows:
class integer { int m ,n; public: integer ( void ) }; integer : : integer (void) { m=0; n= 0; }

// constructor declared // constructor defined.

Constructor characteristics: 1) They should be declared in the public sections. 2) They are invoked automatically when the objects are created 3) Like other C++ function constructors can also have default arguments.
8

4) Constructors cannot be virtual. 5) They make implicit calls to the operators new and delete when memory allocation is required. DESTRUCTOR The destructor is used to destroy the objects created by the constructor. For Example, the destructor for the class integer can be defined as
~integer ( ) { }

FUNCTION OVERLOADING Overloading refers to the use of same thing for different purposes. C++ permits overloading of functions. This means that we can use the same function name to create functions that performs a variety of different tasks. This is known as function polymorphism in OOP. We can design a family of function with one function name but with different argument lists. The function would perform different operations depending on the argument lists. Example: To write a program to overload the function.
#include <iostream.h> int volume(int); double volume(double,int); long volume(long,int,int); main( ) { cout<<volume( 10 ); cout <<volume(2.5,8); cout<<volume(100L,75,15); } int volume(int s) { returns(s * s * s); } double volume(double r, int h ) { return(3.14519*r*r*h); } long volume(long 1,int b, int h ) { return(1*b*h); }

OPERATOR OVERLOADING In a statement like a = b+c, we say that the variable a, b, c contain single value type int, float, char etc. In C++ they may also be objects of some class provided that the operator + is overloaded. The complier does not know how to add two objects of your defined type.So you can tell the procedure to the complier by overloading the operator. This is what is called operator overloading. The following is the syntax for over loading an operator.

return type operator operator_symbol( Type arg1 , Type arg 2,) { // Body of the function }

Example: To overload + operator to add two objects.


#include<iostream.h> class my _ class { private : int x ; public: my _class(int a=0) {x=a;} //constructor int operato + (my_class&secobj) //overloaded operator function+ { cout<<the + operator is overloaded <<end1; return (x+secobj.x) } }; void main () { my _class fistobj(10),secobj (100); cout <<the sum is <<(fistobj + secobj); //this calls the overloaded function + }

INHERITANCE It is the capability of one class inheritance supports reusability of code & is able to simulate the transitive nature of real life objects Types of inheritance: 1) Single Inheritance. 2) Multiple Inheritance 3) Hierarchical Inheritance 4) Multilevel Inheritance 5) Hybrid Inheritance A class from which another class is inheriting its properties is called base class and the class inheriting properties is called subclass or derived class. A subclass can derive itself publicly, privately or protected. A class may contain objects of another class inside it. This situation is called nesting of classes. The general form of defining a derived class is
class derived_class_name: visibility_mode base_class_name { // // members of derived class // };

10

CHAPTER 2

Introduction to Templates
TEMPLATE A template can be considered as a kind macro. When an object of a specific type is defined for actual use, the template definition for that class is substituted with the required data type. Therefore templates are called as parameterized classes or functions. CLASS TEMPLATES
template <class T> class class_name { // . //class member specification //with anonymous type T //where ever appropriate //. };

Example:
template <class T > class vector { T *V; //type T vector int size; public: vector( int m) { v=new T[ size = m]; for(int i = 0;i<size; i++) v[i ]=0; } vector(T *a ) { for(int i = 0;i <size;i++) v[ i ]=A[ i ]; } T operator * ( vector & y ) { T sum =0; for (int i = 0;I<size;i++) sum = sum + this -> v[i]*y.v[i]; return(sum); } };

A class created from a template is called a template class. The syntax for defining a template class object is
class-name <type> object-name (argument list);

11

FUNCTION TEMPLATES Function template can be used to create a family of function with different argument types. The general format of a function template is
template <class T> return type function name (argument of type T ) { //.. //body of function //with type T //where ever appropriate //.. }

Example:
template <class T> void swap(T & x, T &y) { T temp=x; x= y; y=temp; }

we can apply the swap ( ) function as follows


void f(int n, int m , float a, float b) { swap(n,m);// swap two integer values swap(a,b);// swap two float values }

12

EXERCISE NO. 1

Stack and Queue ADT Using an Array


Aim: To write C++ programs to implement the following using an array. a) Stack ADT b) Queue ADT Theory: STACK A stack is an adaptor that provides a restricted subset of container functionality: it provides insertion, removal, and inspection of the element at the top of the stack. Stack is a "last in first out" (LIFO) data structure: the element at the top of a stack is the one that was most recently added. Stack does not allow iteration through its elements. Always remember stack of plates to understand stacks. Syntax:
template<class T> class stack { private: t *s; int top, size;; public: ____________ ____________ ____________ ____________

};

Functions/Operations: 1. 2. 3. 4. 5. isempty( ) check whether stack contain elements or not isfull( ) check for stack contain its maximum elements push( ) insert new element to the top of stack pop( ) remove top element from the stack display( ) list out the elements of stack

QUEUE A queue is a particular kind of collection in which the entities in the collection are kept in order and the principal (or only) operations on the collection are the addition of entities to the rear terminal position and removal of entities from the front terminal position. This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that whenever an element is added, all elements that were added before have to be removed before the new element can be invoked. A queue is an example of a linear data structure.

13

Queues provide services in computer science, transport and operations research where various entities such as data, objects, persons, or events are stored and held to be processed later. In these contexts, the queue performs the function of a buffer. Syntax:
template<class T> class queue { private: t *a; int f,r, size; public: ____________ ____________ ____________ ____________

};

Functions/Operations: 1. 2. 3. 4. 5. 6. 7. isempty( ) check for queue contain null elements isfull( ) check for queue contain elements to its capacity insert( ) insert new element to the rear end of queue remove( ) remove the element from front end first( ) display first element of queue display( ) display all elements of queue last( ) display last element of queue

14

EXERCISE NO. 2

Stack and Queue ADT Using Linked List


Aim: To write C++ programs to implement the following using singly linked list: a) Stack ADT b) Queue ADT

Theory: In computer science, a linked list is one of the fundamental data structures, and can be used to implement other data structures. It consists of a sequence of nodes, each containing arbitrary data fields and one or two references ("links") pointing to the next and/or previous nodes. The principal benefit of a linked list over a conventional array is that the order of the linked items may be different from the order that the data items are stored in memory or on disk, allowing the list of items to be traversed in a different order. A linked list is a self-referential data type because it contains a pointer or link to another datum of the same type. Linked lists permit insertion and removal of nodes at any point in the list in constant time, but do not allow random access. Several different types of linked list exist: singly-linked lists, doubly-linked lists, and circularly-linked lists. Stack ADT using linked list Representation: Syntax:
template<class T> class node { T data; node *link; friend class stack<t>; }; template<class T> class stack { private: node<t>*top; public: }; __________ __________ __________

Functions/Operations: Same operations specified as in exercise 1 except stackfull()

15

Queue ADT using Linked list Representation: Syntax: template<class T>


class node { T node; node *link; friend class queue<T>; }; template<class T> class queue { private: node<T> *f, *r, size; public: ____________ ____________ ____________ ____________

};

Functions/Operations: Same operations specified as in exercise 1 except queuefull()

16

EXERCISE NO. 3

Deque Using an Array and a DLL


Aim: To write C++ programs to implement the deque (double ended queue) ADT using doubly linked list and an array. Theory: DEQUE A deque (short for double-ended queue, usually pronounced deck) is an abstract data structure for which elements can be added to or removed from the front or back. This differs from a normal queue, where elements can only be added to one end and removed from the other. An input-restricted deque is one where deletion can be made from both ends, but input can only be made at one end. An output-restricted deque is one where input can be made at both ends, but output can be made from one end only. Both queues and stacks can be considered specializations of deques, and can be implemented using deques.

DOUBLY LINKED LIST A more sophisticated kind of linked list is a doubly-linked list or two-way linked list. Each node has two links: one points to the previous node, or points to a null value or empty list if it is the first node; and one points to the next, or points to a null value or empty list if it is the final node.
A doubly-linked list containing three integer values: the value, the link forward to the next node, and the link backward to the previous node

In some very low level languages, XOR-linking offers a way to implement doubly-linked lists using a single word for both links, although the use of this technique is usually discouraged. Deque ADT using doubly linked list representation: Syntax: template<class T>
class node { node *link; T data; node *rlink; friend class dq<T>; };

17

template<class T> class dq { private: node<T> *f, *r, size; public: }; __________ __________ ___________

Functions/Operations: 1. 2. 3. 4. 5. 6. isempty ( ) insert ( ) del ( ) first ( ) display ( ) last ( )

18

EXERCISE NO. 4

Binary Search Tree Operations


Aim: To write a C++ program to perform the following operations: a) Insert an element into a binary search tree. b) Delete an element from a binary search tree. c) Search for a key element in a binary search tree.

Theory: TREE It is a collection of nodes originating from root node. For every tree there is exactly one root node. BINARY TREE Each node in a Binary tree can have maximum two children i.e. either 0,1 or 2 children(s). Binary search tree T, is either empty (or) i) ii) iii) iv) T has a special node called the root node. T has two sets of nodes, Lt and Rt, called the left subtree and right subtree of T, respectively. The key in the root node is larger than every key in the left subtree and smaller than every key in the right subtree; and Lt and Rt are binary search trees. or A binary search tree (BST) is a binary tree data structure which has the following properties:

each node (item in the tree) has a value; a total order (linear order) is defined on these values; the left subtree of a node contains only values less than the node's value; the right subtree of a node contains only values greater than or equal to the node's value.

The major advantage of binary search trees over other data structures is that the related sorting algorithms and search algorithms such as in-order traversal can be very efficient. Binary search trees can choose to allow or disallow duplicate values, depending on the implementation. Binary search trees are a fundamental data structure used to construct more abstract data structures such as sets, multisets, and associative arrays.
19

The following class defines a binary search tree as an ADT by extending the definition of the binary tree: Syntax:
template<class elemType> class bSearchTreeType: public binaryTreeType <elamType> { public: bool search(const elemType& searchItem); void insert(const elemType& insertItem); void deleteNode(const elemType& deleteItem); private: void deletteFromTree(nodeType <elemType>* &p); };

20

EXERCISE NO. 5

Traversal of a Binary Tree


Aim: To write C++ programs that use non-recursive functions to traverse the given binary tree in a) Preorder b) inorder, and c) postorder. Theory: Non-Recursive Binary Tree Traversal Algorithms Traverse a Binary Tree using the Preorder, inorder, and postorder methods. PREORDER To traverse a non-empty binary tree in preorder, we perform the following operations. 1) Visit the root. 2) Traverse the left subtree in preorder. 3) Traverse the right subtree in preorder. Syntax:
#include stack.cpp template<class T> void Tree<T>::preorder( ) { node<T>*p; stack<node<T>*>s; ________________ ________________ ________________ ________________ }

INORDER
To traverse a non-empty binary tree in Inorder, we perform the following operations. 1) Traverse the left subtree in inorder. 2) Visit the root. 3) Traverse the right subtree in inorder.

21

Syntax:
#include stack.cpp template<class T> void Tree <T>::inorder( ) { node<T>*p; stack<node<T>*>s; __________________ __________________ __________________ __________________ }

POSTORDER
To traverse a non-empty binary tree in postorder, we perform the following operations. 1) Traverse the left subtree in postorder. 2) Traverse the right subtree in postorder. 3) Visit the root. Syntax:
#include stack.cpp template<class T> void Tree<T>::Postorder( ) { node<T>:*p,:*temp; stack<node<T>*>s; ________________ ________________ ________________ ________________ }

22

EXERCISE NO. 6

BFS and DFS of a Graph


Aim: To write C++ programs for the implementation of bfs and dfs for a given graph. Theory: GRAPH TRAVERSALS 1) Depth first Traversal (DFS) 2) Breadth first Traversal (BFS) 1) Depth First Traversal Algorithm starts at a specific vertex S in G, which becomes current vertex. Then algorithm traverse graph by any edge (u, v) incident to the current vertex u. If the edge (u, v) leads to an already visited vertex v, then we backtrack to current vertex u. If, on other hand, edge (u, v) leads to an unvisited vertex v, then we go to v and v becomes our current vertex. We proceed in this manner until we reach to "deadend". At this point we start back tracking. The process terminates when backtracking leads back to the start vertex. Edges leads to new vertex are called discovery or tree edges and edges lead to already visited are called back edges.

Algorithm: DEPTH FIRST SEARCH (G, v)


Input: A graph G and a vertex v. Output: Edges labeled as discovery and back edges in the connected component. For all edges e incident on v do If edge e is unexplored then w opposite (v, e) // return the end point of e distant to v If vertex w is unexplained then - mark e as a discovery edge - Recursively call DSF (G, w) else - mark e as a back edge

23

The depth first traversal is similar to the preorder traversal of a binary tree. 1. The function to perform the depth first traversal of the entire graph.
template <class vType, int size> void graphType<vType, size>::dft(vType v, bool visited[ ]) { ________________________ ________________________ }

2. The function to perform the depth first traversal of the graph at a node specified by the parameter vertex. template<class vType, int size>
void graphType<vType, size>::dftAtVertex(vType vertex) { ________________________ ________________________ }

2) Breadth First Traversal BFS starts at a given vertex, which is at level 0. In the first stage, we visit all vertices at level 1. In the second stage, we visit all vertices at second level. These new vertices, which are adjacent to level 1 vertices, and so on. The BFS traversal terminates when every vertex has been visited. Algorithm: BREADTH FIRST SEARCH (G, S)
Input: A graph G and a vertex. Output: Edges labeled as discovery and cross edges in the connected component. Create a Queue Q. ENQUEUE (Q, S) // Insert S into Q. While Q is not empty do for each vertex v in Q do for all edges e incident on v do if edge e is unexplored then let w be the other endpoint of e. if vertex w is unexpected then - mark e as a discovery edge - insert w into Q else mark e as a cross edge

The Breadth first traversal of a graph is similarto traversing a binary tree level. By level the function to perform the Breadth first traversal of the entire graph is given below:. template <class vType, int size>
void graphType<vType, size>::breadthFirsttraversal( ) { _____________________ _____________________
24

_____________________ }

The Definition of the Class graphType is: template<class vType, int size> class graphType { public: bool isEmpty( ); void createGraph( ); void ClearGraph( ); void printGraph( ); void depthFirsttraversal( ); void BreadthFirstTraversal( ); graphType( ); ~graphType( );
protected: int maxsize; int gsize; linkedListgraph<vType>graph[size]; private: void dft(vType v, bool visited[ ]); };

25

26

EXERCISE NO. 7

Sorting Methods
Aim:
To write C++ programs for implementing the following sorting methods: a) Merge sort b) Heap sort

Theory: SORTING ALGORITHMS Sorting is the process of arranging a set of similar information into an increasing (or) decreasing order. MergeSort The sorting algorithm Mergesort produces a sorted sequence by sorting its two halves and merging them. With a time complexity of O(n log(n)) Mergesort is optimal. Similar to Quicksort, the Mergesort algorithm is based on a divide and conquers strategy. First, the sequence to be sorted is decomposed into two halves (Divide). Each half is sorted independently (Conquer). Then the two sorted halves are merged to a sorted sequence (Combine) (See the figure shown below).

27

Syntax: template<class T>


void mergesort(T a[ ], T b[ ],int l, int m, int n) { ___________ ___________ ___________ } main( ) { ___________ ___________ ___________ }

Heap Sort The heap sort is the slowest of the O(n log n) sorting algorithms, but unlike the merge and quick sorts it doesn't require massive recursion or multiple arrays to work. This makes it the most attractive option for very large data sets of millions of items. The heap sort works as it name suggests - it begins by building a heap out of the data set, and then removing the largest item and placing it at the end of the sorted array. After removing the largest item, it reconstructs the heap and removes the largest remaining item and places it in the next open position from the end of the sorted array. This is repeated until there are no items left in the heap and the sorted array is full. Elementary implementations require two arrays - one to hold the heap and the other to hold the sorted elements. To do an in-place sort and save the space the second array would require, the algorithm below "cheats" by using the same array to store both the heap and the sorted array. Whenever an item is removed from the heap, it frees up a space at the end of the array that the removed item can be placed in. Pros: In-place and non-recursive, making it a good choice for extremely large data sets. Cons: Slower than the merge and quick sorts. Syntax: template<class T>
void adjust(T a[ ], int i, int n) { ________ ________ ________ } template<class T> void heapsort(T a[ ], int n) { ________ ________ ________ } main( ) { ________ ________ }
28

EXERCISE NO. 8

B-Tree Operations
Aim:
To write a C++ program to perform the following operations a) Insertion into a B-tree b) Deletion from a B-tree

Theory: B-TREES: BALANCED TREE DATA STRUCTURES Introduction Tree structures support various basic dynamic set operations including Search, Predecessor, Successor, Minimum, Maximum, Insert, and Delete in time proportional to the height of the tree. Ideally, a tree will be balanced and the height will be log n where n is the number of nodes in the tree. To ensure that the height of the tree is as small as possible and therefore provide the best running time, a balanced tree structure like a red-black tree, AVL tree, or b-tree must be used. When working with large sets of data, it is often not possible or desirable to maintain the entire structure in primary storage (RAM). Instead, a relatively small portion of the data structure is maintained in primary storage, and additional data is read from secondary storage as needed. Unfortunately, a magnetic disk, the most common form of secondary storage, is significantly slower than random access memory (RAM). In fact, the system often spends more time retrieving data than actually processing data. B-trees are balanced trees that are optimized for situations when part or the entire tree must be maintained in secondary storage such as a magnetic disk. Since disk accesses are expensive (time consuming) operations, a b-tree tries to minimize the number of disk accesses. For example, a b-tree with a height of 2 and a branching factor of 1001 can store over one billion keys but requires at most two disk accesses to search for any node. The Structure of B-Trees Unlike a binary-tree, each node of a b-tree may have a variable number of keys and children. The keys are stored in non-decreasing order. Each key has an associated child that is the root of a subtree containing all nodes with keys less than or equal to the key but greater than the proceeding key. A node also has an additional rightmost child that is the root for a subtree containing all keys greater than any keys in the node. A b-tree has a minimum number of allowable children for each node known as the minimization factor. If t is this minimization factor, every node must have at least t - 1 keys. Under certain circumstances, the root node is allowed to violate this property by having fewer than t - 1 keys. Every node may have at most 2t - 1 keys or, equivalently, 2t children.

29

Since each node tends to have a large branching factor (a large number of children), it is typically necessary to traverse relatively few nodes before locating the desired key. If access to each node requires a disk access, then a b-tree will minimize the number of disk accesses required. The minimization factor is usually chosen so that the total size of each node corresponds to a multiple of the block size of the underlying storage device. This choice simplifies and optimizes disk access. Consequently, a b-tree is an ideal data structure for situations where all data cannot reside in primary storage and accesses to secondary storage are comparatively expensive (or time consuming). Operations on B-Trees The algorithms for the search, create, and insert operations are shown below. Note that these algorithms are single pass; in other words, they do not traverse back up the tree. Since b-trees strive to minimize disk accesses and the nodes are usually stored on disk, this single-pass approach will reduce the number of node visits and thus the number of disk accesses. Simpler double-pass approaches that move back up the tree to fix violations are possible. B-Tree-Search(x, k)
i <- 1 while i <= n[x] and k > keyi[x] do i <- i + 1 if i <= n[x] and k = keyi[x] then return (x, i) if leaf[x] then return NIL else Disk-Read(ci[x]) return B-Tree-Search(ci[x], k)

B-Tree-Create(T)
x <- Allocate-Node() leaf[x] <- TRUE n[x] <- 0 Disk-Write(x) root[T] <- x

B-Tree-Split-Child(x, i, y)
z <- Allocate-Node() leaf[z] <- leaf[y] n[z] <- t - 1 for j <- 1 to t - 1 do keyj[z] <- keyj+t[y] if not leaf[y] then for j <- 1 to t do cj[z] <- cj+t[y] n[y] <- t - 1 for j <- n[x] + 1 downto i + 1 do cj+1[x] <- cj[x] ci+1 <- z for j <- n[x] downto i do keyj+1[x] <- keyj[x] keyi[x] <- keyt[y] n[x] <- n[x] + 1 Disk-Write(y)
30

Disk-Write(z) Disk-Write(x)

B-Tree-Insert(T, k)
r <- root[T] if n[r] = 2t - 1 then s <- Allocate-Node() root[T] <- s leaf[s] <- FALSE n[s] <- 0 c1 <- r B-Tree-Split-Child(s, 1, r) B-Tree-Insert-Nonfull(s, k) else B-Tree-Insert-Nonfull(r, k)

B-Tree-Insert-Nonfull(x, k)
i <- n[x] if leaf[x] then while i >= 1 and k < keyi[x] do keyi+1[x] <- keyi[x] i <- i - 1 keyi+1[x] <- k n[x] <- n[x] + 1 Disk-Write(x) else while i >= and k < keyi[x] do i <- i - 1 i <- i + 1 Disk-Read(ci[x]) if n[ci[x]] = 2t - 1 then B-Tree-Split-Child(x, i, ci[x]) if k > keyi[x] then i <- i + 1 B-Tree-Insert-Nonfull(ci[x], k)

31

32

EXERCISE NO. 9

AVL-Tree Operations
Aim: To write a C++ program to perform the following operations: a) Insertion into an AVL-tree b) Deletion from an AVL-tree Theory: AVL TREES An AVL tree is a self-balancing binary search tree, and it is the first such data structure to be invented. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; therefore, it is also said to be height-balanced. Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases, where n is the number of nodes in the tree prior to the operation. Insertions and deletions may require the tree to be rebalanced by one or more tree rotations. The AVL tree is named after its two inventors, G.M. Adelson-Velsky and E.M. Landis, who published it in their 1962 paper "An algorithm for the organization of information." The balance factor of a node is the height of its right subtree minus the height of its left subtree. A node with balance factor 1, 0, or -1 is considered balanced. A node with any other balance factor is considered unbalanced and requires rebalancing the tree. The balance factor is either stored directly at each node or computed from the heights of the subtrees. A Binary tree is called a AVL tree, if the following two properties are satisfied. 1) All values in the left sub tree must be less than root and all values in the right subtree. 2) Balance factor of a node is defined as (Hl Hr). 3) BF can be +1, -1, (or) 0. Operations The basic operations of an AVL tree generally involve carrying out the same actions as would be carried out on an unbalanced binary search tree, but preceded or followed by one or more operations called tree rotations, which help to restore the height balance of the subtrees. Insertion Insertion into an AVL tree may be carried out by inserting the given value into the tree as if it were an unbalanced binary search tree, and then retracing one's steps toward the root updating the balance factor of the nodes. If the balance factor becomes -1, 0, or 1 then the tree is still in AVL form, and no rotations are necessary.
33

If the balance factor becomes 2 or -2 then the tree rooted at this node is unbalanced, and a tree rotation is needed. At most a single or double rotation will be needed to balance the tree. Deletion If the node is a leaf, remove it. If the node is not a leaf, replace it with either the largest in its left subtree (inorder predecessor) or the smallest in its right subtree (inorder successor) , and remove that node. The node that was found as replacement has at most one subtree. After deletion retrace the path back up the tree (parent of the replacement) to the root, adjusting the balance factors as needed. Lookup Lookup in an AVL tree is performed exactly as in an unbalanced binary search tree. Because of the height-balancing of the tree, a lookup takes O(log n) time. No special provisions need to be taken, and the tree's structure is not modified by lookups. (This is in contrast to splay tree lookups, which do modify their tree's structure.) Syntax:
template<class T> class node { node *lchild; T data; node *rchild; int bf; friend class AVLTree<T>; }; template<class T class AVLtree { private: node<T> *root; public: ___________ ___________ };

34

EXERCISE NO. 10

Kruskals Algorithm
Aim:
To write a C++ program to implement Kruskals algorithm to generate a minimum cost spanning tree.

Theory: MINIMAL SPANNING TREE A tree is defined to be an undirected, acyclic and connected graph (or more simply, a graph in which there is only one path connecting each pair of vertices). Assume there is an undirected, connected graph G. A spanning tree is a subgraph of G, is a tree, and contains all the vertices of G. A minimum spanning tree is a spanning tree, but has weights or lengths associated with the edges, and the total weight of the tree (the sum of the weights of its edges) is at a minimum. There are two well-known algorithms, Prims algorithm and Kruskals algorithm, to find the minimal spanning tree of a graph. KRUSKALS ALGORITHM This is a greedy algorithm. A greedy algorithm chooses some local optimum (ie. picking an edge with the least weight in a MST). Kruskal's algorithm works as follows: Take a graph with 'n' vertices, keep adding the shortest (least cost) edge, while avoiding the creation of cycles, until (n - 1) edges have been added. (NOTE: Sometimes two or more edges may have the same cost. The order in which the edges are chosen, in this case, does not matter. Different MSTs may result, but they will all have the same total cost, which will always be the minimum cost).
bool kruskal(weightdEdge<T> *spanningTreeEdges) { _________ _________ _________ }

35

36

EXERCISE NO. 11

Prims Algorithm
Aim: To write a C++ program to implement Prims algorithm to generate a minimum cost spanning tree.

Theory: PRIMS ALGORITHM This algorithm builds the MST one vertex at a time. It starts at any vertex in a graph (vertex A, for example), and finds the least cost vertex (vertex B, for example) connected to the start vertex. Now, from either 'A' or 'B', it will find the next least costly vertex connection, without creating a cycle (vertex C, for example). Now, from either 'A', 'B', or 'C', it will find the next least costly vertex connection, without creating a cycle, and so on it goes. Eventually, all the vertices will be connected, without any cycles, and an MST will be the result. (NOTE: Two or more edges may have the same cost, so when there is a choice by two or more vertices that is exactly the same, then one will be chosen, and an MST will still result) Syntax: template<class T>
class msTreetype:public graphType<vType, size> { public: void creatSpanningGraph( ); void minimalSpanning(vType sVertex); void printTreeAndWeight( ); protected: vType source; double weight[size][size]; int edges[size]; double edgeWeights[size]; };

37

38

EXERCISE NO. 12

Dictionary ADT Using Hashing


Aim: To write a C++ program to implement all the functions of a dictionary (ADT) using hashing.

Theory:
HASHING Hashing is used for random accessing of records in the file or database. When choosing a hash function, the main objectives are to: a) Choose a hash function that is easy to compute. b) Minimize the number of collisions. Hashing Functions Mid-Square: In this method, the hash function, h, is computed by squaring the identifier, and then using the appropriate number of bits from the middle of the square to obtain the bucket address. Because the middle bits of a square usually depend on all the characters, it is expected that different keys will yield different hash addresses with high probability, even if some of the characters are the same. Folding: In folding the key X is partitioned into parts such that all the parts except possibly the last parts are of equal length. The parts are then added, in some convenient way to obtain the hash address.

Division (Modular arithmetic): In this method, the key X is converted into an integer I x. This integer is divided by the size of the hash table to get the remainder, giving the address of X in HT. i.e
H(X)=Ix %HTSize;

39

DICTIONARY DATA STRUCTURE A dictionary data structure is one which is capable of storing objects in sorted order based on key such as a string or an integer. For instance, say you have several hundred Base objects which consist of the name of the Base, the latitude and longitude at which it is located, and its armaments. One way of storing these cities is to sort them by name; another is to store them in decreasing order by armaments; yet another is in increasing order by latitude. Primarily, the dictionary component will store objects based on some sort of a string key, such as the name of a Base or Target. The main purpose of the dictionary is to provide us with an easy way to see what data points we have already entered into our SoftWar database. The term data dictionary will be used in class to refer to this component, or to a collection of components having this role. An Introduction to Dictionaries In our investigation of vectors and arrays, we've seen structures that collect objects in which the objects can be indexed by numbers. Are there other ways to index collections? Yes, we could index collections by strings. We could also index by other objects (Strings, Integers, Points, whatever). Collections of objects indexed by objects are called dictionaries. o Some people also refer to them as keyed tables. o Other people refer to them as maps Some more terminology: o The objects we're using as indices are called keys. o The objects we're looking up are called values. Like a priority queue, a dictionary is a container of key-element pairs. Nevertheless, although a total order relation on the keys is always required for a priority queue, it is optional for a dictionary. Indeed, the simplest form of a dictionary assumes only that we can determine whether two keys are equal. When the total order relation on the keys is defined, then we can talk about an ordered dictionary, and we specify additional ADT functions that refer to the ordering of the keys. A dictionary ADT stores key-element pairs (k,e) which we call items, where k is the key and e is the element. No two pairs in a dictionary have the same. In an unordered dictionary we can use an equality tester object to test whether two keys, k1 and k2, are equal with function isEqualTo(k1, k2). The Dictionary ADT Definition A dictionary is an ordered or unordered list of key-element pairs, where keys are used to locate elements in the list. Example: consider a data structure that stores bank accounts; it can be viewed as a dictionary, where account numbers serve as keys for identification of account objects. Operations (methods) on dictionaries: size () empty () findItem (key) Returns the size of the dictionary Returns true is the dictionary is empty Locates the item with the specified key. If no such key exists,
40

findAllItems (key) removeItem (key) removeAllItems (key) insertItem (key, element)

sentinel value NO_SUCH_KEY is returned. If more than one item with the specified key exists, an arbitrary item is returned. Locates all items with the specified key. If no such key exists, sentinel value NO_SUCH_KEY is returned. Removes the item with the specified key Removes all items with the specified key Inserts a new key-element pair

Additional methods for ordered dictionaries: closestKeyBefore (key) closestElemBefore (key) closestKeyAfter (key) closestElemAfter (key) Returns the key of the item with largest key less than or equal to key Returns the element for the item with largest key less than or equal to key Returns the key of the item with smallest key greater than or equal to key Returns the element for the item with smallest key greater than or equal to key

Sentinel value NO_SUCH_KEY is always returned if no item in the dictionary satisfies the query. The Abstract Data Type:
AbstractDataType Dictionary { instances collection of pairs with distinct keys operations empty( ): return true iff the dictionary is empty; size( ) : return the number of pairs in the dictionary; find( ) ; return the pair with key k; insert(p) : insert the pair p into the dictionary; erase(k) : delete the pair with key k; }

41

42

BIBLIOGRAPHY
1. 2. 3. 4.
5. 6. 7. 8.

Data Structures and Algorithms in C++, Third Edition, Adam Drozdek, Thomson. Data Structures using C++, D.S. Malik, Thomson Donald Knuth. The Art of Computer Programming, Volume 1: Fundamental Algorithms, Third Edition. Addison-Wesley, 1997 Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001 www.en.wikipedia.org/wiki/ www.cppreference.com/ www.eli.sdsu.edu/ cslibrary.stanford.edu/

43