Sunteți pe pagina 1din 68

Course Structure

B.Sc (Computer Science)


SRI KRISHNADEVARAYA UNIVERSITY : ANANTHAPURAMU
Revised Common Framework of CBCS for Colleges in Andhra Pradesh
(A.P. State Council of Higher Education) 2016-17
Page 11 of 43
II YEAR - IV SEMESTER
Paper-IV : DATA STRUCTURES

UNIT I
Concept of Abstract Data Types (ADTs)- Data Types, Data Structures, Storage Structures, and File Structures,
Primitive and Non-primitive Data Structures, Linear and Non-linear Data Structures. Linear Lists – ADT, Array and
Linked representations. Arrays – ADT, Mappings, Representations, Sparse Matrices, Sets – ADT, Operations Linked
Lists: Single Linked List, Double Linked List, Circular Linked List , applications

UNIT II
Stacks: Definition, ADT, Array and Linked representations, Implementations and Applications Queues: Definition,
ADT, Array and Linked representations, Circular Queues, Dequeues, Priority Queues, Implementations and
Applications.

UNIT III
Trees: Binary Tree, Definition, Properties, ADT, Array and Linked representations, Implementations and
Applications. Binary Search Trees (BST) – Definition, ADT, Operations and Implementations, BST Applications.
Threaded Binary Trees, Heap trees.

UNIT IV
Graphs – Graph and its Representation, Graph Traversals, Connected Components, Basic Searching Techniques,
Minimal Spanning Trees

UNIT- V
Sorting and Searching: Selection, Insertion, Bubble, Merge, Quick, Heap sort, Sequential and Binary Searching.
UNIT I
Concept of Abstract Data Types (ADTs)
--------------------------------------------------------
Data Types in Java:
Data types in java specify the size and type of values that can be stored in an identifier.

There are two types of Data Types in Java.


1. Primitive Data Type
2. Non-primitive Data Type
Primitive Data Type:
There are 8 primitive data types such as byte, short, int, long, float, double, char, and boolean. Size of these 8
primitive data types wont change from one OS to other.

Non-primitive Data Type:


Non-primitive data types include Classes, Interfaces and Arrays.
TYPES OF Data Structures:
Data structure is a mechanism to organize the data in different ways and to perform possible operations on these
structures.
The data structure operations include insertion, deletion, modification, searching, sorting and etc.
Data structure also measures the efficiency of the algorithm in terms of their time-complexity and space complexity
of the program.
One way of classifying the data structures is either linear or non-linear. Linked lists come under linear data
structures and trees and graphs come under non-linear.
Every data structure combines its own advantages and disadvantages.
Data structures can be of many types.
Arrays Linked list
Stack queue
Trees graphs, etc
Storage Structures:
Data in its raw form is like a chunk of ore from which the diamond is yet to be extracted. It is hard to process the raw
data so the need for structured data arises, which is when data structures become handy. In computer science, a data
structure is a particular way of organising data in a computer so that it can be used efficiently. Searching a particular
book from millions of books on Amazon or sorting through the endless choice of mobile phones on the basis of price
are all done with low cost and low complexity algorithms, which work on structured data.

Figure 1: Different data structures


Data can be structured in many ways but the commonly used practices are given below:
Arrays: This is a data structure that can store a fixed-size sequential collection of elements of the same type. Elements
are accessed using indexes.
Linked List: This data structure consists of a group of nodes, which together represent a sequence. Under the simplest
form, each node is composed of data and a pointer to the next node. This structure allows for efficient insertion or
removal of elements from any position in the sequence.
Stack: This is a data structure that implements the LIFO (Last In First Out) paradigm. It is like a pile of plates — the
one put in last is removed first. It can be implemented both through arrays and Linked List. Push and Pop are the two
main operations on the stack. This is basically used in recursion.
Queue: This is a FIFO (First In First Out) data structure. It is similar to a queue of people at a movie ticket counter.
The first element added to the queue will be the first one to be removed.
Tree: A tree consists of a set of elements (nodes) which can be linked to other elements, sometimes hierarchically and
sometimes not. A tree data structure can be defined recursively (locally) as a collection of nodes (starting at a root
node), where each node is a data structure consisting of a value, together with a list of references to nodes (the
‘children’), with the constraint that no reference is duplicated or points to the root.
File Structure or naming conventions:
A Java source code file normally has the following structure:
1. package statement
2. import statements
3. class definition
Naming Convention: Naming conventions are a set of rules to be followed to give names to the variables, classes and
other tokens of a Java program. It is a better programming practice to follow the naming conventions. One can easily
differentiate or understand what are classes, what are methods and etc. It facilitates easy reference i.e. when one refers
back to a program, one can easily understand what is what. These are not mandatory but suggested. The following are
the naming conventions suggested in Java.
1. Package names in java are written in all small letters
Example: java.awt, java.io, javax.swing , java.mypackage
2. Each word of a class name and interface name starts with a capital letter.
Example: String, DataInputStream, ActionListener
3. Method names starts with a small letter, then each word that follows starts with a capital letter.
Example: readLine( ) , toCharArray()
4. Variable names also follow the above rules as those of methods; words may be separated by underscores (_).
Example: age, empName, employee_Net_Sal;
5. Constants should be written using all capital letters, words are separated by underscores (_) .
Example: PI, MAX_VALUE
6. All key words should be written in all small letters.
Example: public, static, void, main etc…
Types of DataStructures:
Data type specifies the type of data stored in a variable. The data type can be classified into two types Primitive data
type and Non-Primitive data type.
PRIMITIVE DATATYPE
The primitive data types are the basic data types that are available in most of the programming languages. The
primitive data types are used to represent single values.
Integer: This is used to represent a number without decimal point.
Eg: 12, 90
Float and Double: This is used to represent a number with decimal point.
Eg: 45.1, 67.3
Character : This is used to represent single character
Eg: ‘C’, ‘a’
String: This is used to represent group of characters.
Eg: "Sri Jaffa Degree College"
Boolean: This is used represent logical values either true or false.
NON PRIMITIVE DATATYPES:
The data types that are derived from primary data types are known as non-Primitive data types. These datatypes
are used to store group of values.
The non-primitive data types are
Arrays
Structure
Union
linked list
Stacks
Queue Etc.

Abstract Data Types (ADTs):


When an application requires a special kind of data which is not available as built in data type then it is programmers
burden to implement his own kind of data. Here the programmer has to give more effort regarding how to store value
for that data, what are tha operations that meaningfully manipulate variables of that kind of data, amount of memory
require to store for a variable. The programmer has to decide all these things and accordingly implement it.
Programmers own datatype is termed as abstract datatype. Abstract datatype is also called as userdefined datatype.

Advantages and disadvantages of Arrays:


Array is set of values of similar type. Array elements share common name and array elements are stored in
sequential memory locations. To refer to the elements of the array, we use indexes accordingly. Array indexing
starts from “zero”. Predetermination of an array size is a must except in the case of giving values at the time of
declaring an array.
Advantages of arrays:
It is capable of storing many elements at a time
It allows random accessing of elements i.e. any element of the array can be randomly accessed using indexes.
Disadvantages:
Predetermining the size of the array is a must.
There is a chance of memory wastage or shortage.
To delete one element in the array, we need to traverse throughout the array.
To delete one element in the array, we need to traverse throughout the array.
Creation of an array may involve three steps:
1. Declaring the array
2. Creating memory
3. Putting value into memory
Arrays can be of many types: single dimensional, multidimensional. Single dimensional array can represent
either one row or one column of values. Whereas, multi-dimensional array can represent information in the
form of rows and columns.
In Java, Single dimensional array can be declared as follows.
int a[ ] =new int[5];
Or int [] a= new int[5];
Or int a[] ={3,4,5,6,7};
In the above expressions, for the array ‘a’ 20 bytes of memory will be allocated and array ‘a’ can hold
maximum of 5 integers at a time.
Array initialization can be done as follows:
int a[]={10,20,30,40,50}
to access the third element of the array we may use a[2] i.e. the index of third element in the array is 2.
To print the array, we may write:
for (i=0;i<5;i++)
System.out.println(a[i]);
Or, using for-each we may directly access the values
for(int x : a)
System.out.println(x);
In java, all arrays store the allocated size in a variable named length. We can obtain the length of the array a
using a.length.
Multidimensional arrays:
Multidimensional arrays can represent the data in the form of rows and columns i.e. by taking two or more
indexes.
Multidimensional arrays include double dimensional and 3 dimensional arrays.

Double Dimensional Arrays: Double dimensional arrays can represent the data in the form of rows and columns i.e.
by taking two indexes. Generally, these are used to work with matrices.
In java, a double dimensional array can be declared as follows:
int a[][] = new int[5][5];
Or int [][]a = new int[5][5];
Or int []a[] = new int[5][5]
Or int a[][]={ {1,2,3}, {4,5,6}, {7,8,9}};
Array initialization can be done as follows:
int a[][]={ {10,20,30}, {40,50,60}, {70,80,90} };
to access the second element of second row in the array we use a[1][1]
To print the array, we may write:
for(int i=0;i<3;i++)
{
System.out.println(“”);
for(int j=0;j<3;j++)
{
System.out.print (“\t”+a[i][j]);
}
}

Three-dimensional arrays: Three-dimensional array uses three indexes to refer to its elements. It represents a
set of matrices.
Ex: int a[][][] = new int [2][2][2];
In the above expression, for the array ‘a’ 32 bytes of memory would be allocated and array ‘a’ can hold
maximum of 8 integers at a time. This can be treated as two-2X2 matrices.
Array initialization can be done as follows:
int a[][][]={ {10,20,30},{40,50,60}, {70,80,90},{11,22,33} };
a[0][0][0] fetches the first element of the array
To print the above array, we may write:
for (i=0;i<2;i++)
{ System.out.println(“”);
for(j=0;j<2;j++)
{
System.out.println(“”);
for(k=0;k<3;k++)
System.out.print (“\t”+a[i][j][k]);
}
}
Operations on Array:

Following operations can be performed on arrays:


1. Traversing
2. Searching
3. Insertion
4. Deletion
5. Sorting
6. Merging
1. Traversing: It is used to access each data item exactly once so that it can be processed.
E.g.
We have linear array A as below:
1 2 3 4 5

10 20 30 40 50
Here we will start from beginning and will go till last element and during this process we will access value of each
element exactly once as below:
A [1] = 10
A [2] = 20
A [3] = 30
A [4] = 40
A [5] = 50
2. Searching: It is used to find out the location of the data item if it exists in the given collection of data items.
E.g.
We have linear array A as below:
1 2 3 4 5

15 50 35 20 25
Suppose item to be searched is 20. We will start from beginning and will compare 20 with each element. This process
will continue until element is found or array is finished. Here:
1) Compare 20 with 15
20 # 15, go to next element.
2) Compare 20 with 50
20 # 50, go to next element.
3) Compare 20 with 35
20 #35, go to next element.
4) Compare 20 with 20
20 = 20, so 20 is found and its location is 4.
3. Insertion: It is used to add a new data item in the given collection of data items.
E.g.
We have linear array A as below:

1 2 3 4 5

10 20 50 30 15
New element to be inserted is 100 and location for insertion is 3. So shift the elements from 5th location to 3rd
location downwards by 1 place. And then insert 100 at 3rd location. It is shown below:

4. Deletion: It is used to delete an existing data item from the given collection of data items.
E.g.
We have linear array A as below:

1 2 3 4 5

10 20 50 40 25 60
The element to be deleted is 50 which is at 3rd location. So shift the elements from 4th to 6th location upwards by 1
place. It is shown below:

After deletion the array will be:


1 2 3 4 5 6

10 20 40 25 60
5. Sorting: It is used to arrange the data items in some order i.e. in ascending or descending order in case of numerical
data and in dictionary order in case of alphanumeric data.
E.g.
We have linear array A as below:

1 2 3 4 5

10 50 40 20 30

After arranging the elements in increasing order by using a sorting technique, the array will be:

1 2 3 4 5

10 20 30 40 50
6. Merging: It is used to combine the data items of two sorted files into single file in the sorted form
We have sorted linear array A as below:

1 2 3 4 5 6

10 40 50 80 95 100
And sorted linear array B as below:

1 2 3 4

20 35 45 90
After merging merged array C is as below:

1 2 3 4 5 6 7 8 9 10

10 20 35 40 45 50 80 90 95 100
Sparse matrix:
A matrix is a two-dimensional data object made of m rows and n columns, therefore having total m x n values. If most
of the elements of the matrix have 0 value, then it is called a sparse matrix.
Why to use Sparse Matrix instead of simple matrix ?
 Storage: There are lesser non-zero elements than zeros and thus lesser memory can be used to store only
those elements.
 Computing time: Computing time can be saved by logically designing a data structure traversing only non-
zero elements..
Example:
00304
00570
00000
02600
Representing a sparse matrix by a 2D array leads to wastage of lots of memory as zeroes in the matrix are of no use in
most of the cases. So, instead of storing zeroes with non-zero elements, we only store non-zero elements. This means
storing non-zero elements with triples- (Row, Column, value).
Sparse Matrix Representations can be done in many ways following are two common representations:
1. Array representation
2. Linked list representation
Method 1: Using Arrays
2D array is used to represent a sparse matrix in which there are three rows named as
 Row: Index of row, where non-zero element is located
 Column: Index of column, where non-zero element is located
 Value: Value of the non zero element located at index – (row,column)

Method 2: Using Linked Lists


In linked list, each node has four fields. These four fields are defined as:
 Row: Index of row, where non-zero element is located
 Column: Index of column, where non-zero element is located
 Value: Value of the non zero element located at index – (row,column)
 Next node: Address of the next node

Linked lists, its advantages & Types of Linked lists:

Linked list is a type of data structure that holds set of nodes linked to each other. Evrey node consists of
information parts and reference parts. Linked list allows many operations like insertion, deletion, searching etc.
on it.
Advantages:
In, Linked lists there will not be any memory wastage or shortage.
To insert an element in the linked list, we need not traverse throughout the list.
To delete an element in the linked list, we need not traverse throughout the list.
Predetermining of number of data elements is not necessary.
Disadvantages:
No direct access to a particular element in the list.
Linked lists are mainly of 4 types:
1) Single linear linked list:
In a single linear linked list, every node consists of two parts: information and reference parts. Reference
part holds the reference of the next node. It allows one-way and linear traversal in the list. End of the list it
points to NULL.

2) Single circular linked list:


In a single circular linked list, every node consists of two parts: information and reference parts. Reference
part holds the reference of the next node. It allows one-way and circular traversal in the list. There is no end-
node in a list, because the last node is linked to the first node.

3) Double linear linked list:


In a double linear linked list, every node consists of three parts: information and two reference parts. First
reference part holds the reference of the previous node and the other holds the reference of the next node. It
allows two-way and linear traversal in the list.

4) Double circular linked list:


In a double circular linked list, every node consists of three parts: information and two reference parts. First
reference part holds the reference of the previous node and the other holds the reference of the next node. It
allows two-way and circular traversal in the list.

Operations on linked list ADT:

Linked list is a type of data structure that holds nodes linked to each other, where every node consists of information
and reference parts.
Linked list allows many operations on it. The following list displays some of the possible operations.
Creating a list: It involves allocating memory and reading data for the nodes and linking them as per
Polynomial addition the mechanism
Destroying a list: It involves deallocating memory for all the nodes of a linked list.
Counting the length the list: Length of the node is counting number of nodes in the linked list
Finding kth element in the list: It returns the element at the given position
Searching for an element in the list: It returns the position of the element if the searching element exists
otherwise an error indicating value.
Concatenating two lists: Appending two different linked lists to form a single list.
Inserting an element in the list: Inserting a new node in the linked list at a given position or after a value.
Deleting an element in the list: Deleting an existing node in the linked list at a given position or given value.
Printing the list: Displaying all the elements of the list
Applications of linked lists:

Polynomial addition and multiplication: polynomial equations can be added and multiplied easily using
linked lists. Linked lists hold each coefficient and exponential part as information parts.
Radix sort: Radix sorts, and address calculation sort use linked lists to perform sorting.
To perform arithmetic operations on large numbers: Using existing data types such as int, long, it is not
possible to perform arithmetic operations large numbers. Linked lists solve this problem by splitting them
into digits and holding them in nodes.
Implementation of stacks and queues: The data structures such as stacks and queues are implementations of
linked list.
Memory management. The unused nodes can be maintained as a free list. There will not be any memory
wastage in linked list.
Symbol table implementation: Symbol tables are used in compiler construction and related fields. These can
be used as dictionary for the values. Linked list are best suited for implementing
Creation, insertion and deletion of nodes in a single linked list:
Linked list is a type of data structure that holds nodes linked to each other. Every node consists of information
parts and reference parts. Linked list allows many operations like creation, insertion, deletion etc. on it.
In a single linear linked list, every node consists of two parts: information and address parts. Reference part
holds the reference of the next node. It allows one-way and linear traversal in the list. End of the list points to
null.
Creating node:
1. Create an ADT named “node” that contains “reference”(a node) and “information” parts.
2. Allocate memory for “first” node, treat the same as “last” and make its reference part null.
3. Allocate memory for a temporary node, read information and link it to the last node in the list and call it “last”
4. Repeat the process i.e. the previous step till user wishes to continue.

Inserting node:
1. Take a reference named “ptr” and move it from first node to a node that is prior to the inserting position.
2. Allocate memory for a temporary node, read information
3. Establish links as shown below (temp.next = ptr.next, then ptr.next=temp)
4. link it to the address of “ptr” node as shown
5. handle it differently if it is insertion at last node

Deleting node:
1. Take a reference named “ptr” and move it from first node to a node that is prior to the deleting position.
2. Make the link as shown in the diagram (ptr.next = ptr.next.next)
3. handle it differently when deleting the last node

Program to insert, create and delete elements in a single linked list:


import java.io.*;
class node
{
public int x;
public node next;
}
class LinkedList
{
public node first;
LinkedList()
{
first=new node();
first.next=null;
}
void add (int v)
{
node temp=new node();
temp.x=v;
temp.next=null;
node ptr=first;
while(ptr.next!=null)
ptr=ptr.next;
ptr.next=temp;
}
void insert(int p,int v)
{
node ptr=first,temp;
for(int i=1;i<=p-1;i++)
ptr=ptr.next;
temp=new node();
temp.x=v;
temp.next=ptr.next;
ptr.next=temp;
}
void del(int p)
{
node ptr=first,temp;
for(int i=1;i<=p-1;i++)
ptr=ptr.next;
temp=ptr.next;
ptr.next=ptr.next.next;
temp=null;
}
void show()
{
System.out.println("\nList Elements:");
for(node ptr=first.next;ptr!=null;ptr=ptr.next)
System.out.print("\t"+ptr.x);
}
}
class SListTest
{
public static void main(String as[]) throws Exception
{
String con="";
int x,op,p,v;
LinkedList l1=new LinkedList();
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("Enter elements to create");
do
{
x=Integer.parseInt(br.readLine());
l1.add(x);
System.out.print("Add more?(y,n):");
con=br.readLine();
}while(con.equals("y"));
l1.show();
do
{
System.out.println("\n 1.Insert\n 2.Delete \n 3.Display \n 4.Exit");
System.out.println("\nSelect an option:");
op=Integer.parseInt(br.readLine());
if(op==1)
{
System.out.println("Enter Position to insert:");
p= Integer.parseInt(br.readLine());
System.out.println("Enter Value to insert:");
v= Integer.parseInt(br.readLine());
l1.insert(p,v);
}
if(op==2)
{
System.out.println("Enter Position to delete:");
p= Integer.parseInt(br.readLine());
l1.del(p);
}
l1.show();
}while(op<4);
}
}
/* Output: */
Enter elements to create
30
Add more?(y,n):y
40
Add more?(y,n):y
50
Add more?(y,n):n
List Elements:
30 40 50
1.Insert
2.Delete
3.Display
4.Exit
Select an option:1
Enter Position to insert:2
Enter Value to insert:99
List Elements:
30 99 40 50
1.Insert
2.Delete
3.Display
4.Exit
Select an option: 4
TextQ � l = �M 0�O eft:.5in'> }
l1.show();
}while(op<4);
}
}
/* Output: */
Enter elements to create
30
Add more?(y,n):y
40
Add more?(y,n):y
50
Add more?(y,n):n
List Elements:Left to Right
30 40 50
List Elements:Right to Left
50 40 30
1.Insert
2.Delete
3.Display
4.Exit
Select an option:1
Enter Position to insert:2
Enter Value to insert:99
List Elements:Left to Right
30 99 40 50
List Elements:Right to Left
50 40 99 30
1.Insert
2.Delete
3.Display
4.Exit
Select an option: 4

Process of creation, insertion and deletion in Double linked lists.:

Linked list is a type of data structure that holds nodes linked to each other. Every node consists of information parts
and reference parts. Linked list allows many operations like creation, insertion, deletion etc. on it.
In a double linear linked list, every node consists of three parts: information and two reference parts. First reference
part holds the reference of the previous node and the other holds the reference of the next node. It allows two-way
and linear traversal in the list.
Creating node:
1. Create an ADT named “node” that contains “two references”(two nodes) and “information” parts
2. Allocate memory for “first” node, treat the same as “last” and make its reference parts null.
3. Allocate memory for a temporary node, read information and link it to the last node (last.next=temp) in the list.
Link its “prev” part to “last” (temp.prev=last) and call it “last”
4. Repeat the process till user wishes to continue.

Inserting node:
1. Take a reference named “ptr” and move it from first node to a node that is prior to the inserting position.
2. Allocate memory for a temporary node, read information
3. Make links as shown in the diagram (temp.next=ptr.next, ptr.next.prev=temp then ptr.next =temp,temp.prev=ptr)
4. handle it differently if it is insertion at last node

Deleting node:
1. Take a reference named “ptr” and move it from first node to a node that is prior to the deleting position.
2. Make the links as shown below (ptr=ptr.next.next and ptr.next.prev=ptr)
3. handle it differently if it is deletion at last node

Program to insert, create and delete elements in a double linked list:


import java.io.*;
class node
{
public int x;
public node next;
public node prev;
}
class DoubleLinkedList
{
public node first;
public node last;
DoubleLinkedList()
{
first=new node();
first.next=null;
first.prev=null;
last=first;
}
void add (int v)
{
node temp=new node();
temp.x=v;
temp.next=null;
last.next=temp;
temp.prev=last;
last=temp;
}
void insert(int p,int v)
{
node ptr=first,temp;
for(int i=1;i<=p-1;i++)
ptr=ptr.next;
if(ptr.next==null)
add(v);
else
{
temp=new node();
temp.x=v;
temp.next=ptr.next;
ptr.next.prev=temp;
ptr.next=temp;
temp.prev=ptr;
}
}
void del(int p)
{
node ptr=first,temp;
for(int i=1;i<=p-1;i++)
ptr=ptr.next;
if(ptr.next.next==null)
{
temp=last;
last=last.prev;
last.next=null;
}
else
{
temp=ptr.next;
ptr.next=ptr.next.next;
ptr.next.prev=ptr;
}
temp=null;
}
void show()
{
System.out.println("\nList Elements:Left to Right");
for(node ptr=first.next;ptr!=null;ptr=ptr.next)
System.out.print("\t"+ptr.x);
System.out.println("\nList Elements:Right to Left");
for(node ptr=last;ptr.prev!=null;ptr=ptr.prev)
System.out.print("\t"+ptr.x);
}
}
Class DListTest
{
public static void main(String as[]) throws Exception
{
String con="";
int x,op,p,v;
DoubleLinkedList l1=new DoubleLinkedList();
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("Enter elements to create");
do
{
x=Integer.parseInt(br.readLine());
l1.add(x);
System.out.print("Add more?(y,n):");
con=br.readLine();
}while(con.equals("y"));
l1.show();
do
{
System.out.println("\n 1.Insert\n 2.Delete \n 3.Display \n 4.Exit");
System.out.println("\nSelect an option:");
op=Integer.parseInt(br.readLine());
if(op==1)
{
System.out.println("Enter Position to insert:");
p= Integer.parseInt(br.readLine());
System.out.println("Enter Value to insert:");
v= Integer.parseInt(br.readLine());
l1.insert(p,v);
}
if(op==2)
{
System.out.println("Enter Position to delete:");
p= Integer.parseInt(br.readLine());
l1.del(p);
}
l1.show();
}while(op<4);
}
}
/* Output: */
Enter elements to create
30
Add more?(y,n):y
40
Add more?(y,n):y
50
Add more?(y,n):n
List Elements:Left to Right
30 40 50
List Elements:Right to Left
50 40 30
1.Insert
2.Delete
3.Display
4.Exit
Select an option:1
Enter Position to insert:2
Enter Value to insert:99
List Elements:Left to Right
30 99 40 50
List Elements:Right to Left
50 40 99 30
1.Insert
2.Delete
3.Display
4.Exit
Select an option: 4

Compare arrays and linked lists.:

Arrays:
It is a set of values of same type
Predetermination of the size is a must.
Array elements are sequentially stored
Direct access to any element of the array is possible.
To insert and delete elements we need to traverse throughout the array.
Index plays key role.
There is a chance for Memory wastage or shortage.

Linked lists:
It is a set of nodes linked together, where node has information and reference parts.
No predetermination of number of nodes, because it uses dynamic memory allocation.
Nodes of the list are not stored sequentially.
Direct access to any element of the list is not possible.
To insert and delete elements we need not traverse throughout the list.
References play key role.
There is no Memory wastage.
Time Complexity:
It is the amount of time which is needed by the algorithm (program) to run to completion. We can measure the time by
finding out the compilation time and run time.

The compilation time is the time which is taken by the compiler to compile the program. This time is not under the
control of programmer. It depends on the compiler and differs from compiler to compiler. One compiler can take less
time than other compiler to compile the same program. So we ignore the compilation time and only consider the run
time. The run time is the time which is taken to execute the program. We can measure the run time on the basis of
number of instructions in the algorithm.

E.g.

Suppose we want to add two integer numbers. To solve this problem we have following two algorithms:

Suppose 1 second is required to execute one instruction. So the first algorithm will take 4 seconds and the second
algorithm will take 3 seconds for execution. So we will choose the second one as it will take less time.
Space Complexity
It is the amount of memory which is needed by the algorithm (program) to run to completion. We can measure the
space by finding out that how much memory will be consumed by the instructions and by the variables used.
E.g.
Suppose we want to add two integer numbers. To solve this problem we have following two algorithms:

Both algorithms will produce the same result. But first takes 6 bytes and second takes 4 bytes (2 bytes for each
integer). And first has more instructions than the second one. So we will choose the second one as it takes less space
than the first one.

UNIT-2
STACKS AND QUEUES
Stack:

Stack in simple terms can be explained as a heap of objects placed one over the other.
The last placed object can be accessed first, and it is said to be “Last-in-First- out” (LIFO) method.
Stack can be implemented using both arrays and linked lists.
In a stack, the elements are added at the top and removed from the top i.e. both insertion and deletion take
place at one end.
Some common words associated with stack:
Push: Inserting an element into the stack.
Pop: accessing and removing the element from the top of the stack
Over-flow: when number of elements exceed the maximum size of the stack, it is said to be “ stack over flow”
Under-flow: when a stack is empty, and the user is still trying to pop elements, it is called as “stack
underflow”
Top: refers to the top element of the stack
Applications:
Parenthesis matching: Stacks can be used to find out whether the given equation is balanced or not. It mainly checks
for the matching of left and right parenthesis in an equation.
Example:
(A + B (x+ y) +C is not a valid expression because it misses right parenthesis
(A + B (x+ y) +C) is a valid expression
Convert from infix to postfix: Stacks can be used to convert an infix expression to post fix expression
Example:
Infix expression: (a+b)*c
Postfix expression: a b + c *
Prefix expression: * + a b c
To evaluate postfix expression: To evaluate postfix expression, stacks can be used. While evaluating, operands are
stored onto a stack and popped when an operator occurs.
Example:
The postfix expression “2 3 * 6 +” gets executed as “12”
Recursion: Computer program implementing recursion uses to remember the pending jobs to be performed. It handles
all the pending jobs in a stack manner.
Example:
A “Fact(n)” function written in a recursive method gets executed as follows:
Fact(3) = 3 * Fact (2)
= 3 * 2 * Fact (1)
=3*2*1
=3*2
=6
Keeping track of function calls: To keep track of function calls, stack can be used to return to the point where it is
called after executing the function. When a function is called the control shifts to the function and executes all the
statements there. But, to return to the point where it is called a stack is used to remember the address of the calling
statement.
Example:

Computer applications that use undo features: Undo is to cancel the previous action. To remember these in LIFO order
computer applications use stack.
Solving Hanoi Towers problem: Stack solves the most popular Hanoi towers problem in which an ordered set of disks
must be moved into another needle that holds disks in the same order using an auxiliary needle as per some rules.
Stack is shown in the figure below:

Stack & different ways of implementing a stack:


Stack in simple terms can be explained as a heap of objects placed one over the other.
The last placed object can be accessed first, and it is said to be “Last-in-First- out” (LIFO) method.
In a stack, the elements are added at the top and removed from the top.
Stack can be implemented mainly in two ways
1. Using arrays
2. Using Linked lists.
In both the ways, we need to write the “push()” and “pop()” functions.
1. Program to implement PUSH and POP operations on Stack using array method.
class Stack
{
private static int MAX=10;
private int a[]=new int[MAX];
int top;
Stack()
{
top=0;
}
public void push(int v)
{
if(top<max)</max)
a[top++]=v;
else
System.out.println("Overflow");
}
public int pop()
{
if(top>0)
return a[--top];
else
{
System.out.println("Underflow");
return -1;
}
}
}
class StackArray
{
public static void main(String as[])
{
Stack s1=new Stack();
s1.push(30);
s1.push(40);
s1.push(50);
System.out.println(s1.pop());
System.out.println(s1.pop());
System.out.println(s1.pop());
}
}
/* Output */
50
40
30
2. Program to implement PUSH and POP operations on Stack using Linked list method.
class node
{
public int x;
public node next;
}
class Stack
{
public node top;
Stack()
{
top=null;
}
void push (int v)
{
node temp=new node();
temp.x=v;
temp.next=top;
top=temp;
}
int pop()
{
int v=top.x;
node temp=top;
top=top.next;
temp=null;
return v;
}
}
class StackList
{
public static void main(String as[])
{
Stack s1=new Stack();
s1.push(30);
s1.push(40);
s1.push(50);
System.out.println(s1.pop());
System.out.println(s1.pop());
System.out.println(s1.pop());
}
}
/* Output */
50
40
30
Operations on stack ADT:

Stack operations include the following:


push: pushing is to add elements on the top of the stack.
pop: popping is removing elements from the top of the stack.
isfull: isfull finds out whether the stack is full or not
isempty: isempty finds out whether the stack is empty or not
create: to create an empty stack
top: to return the top element of a stack.
Applications of Stack:
Applications of stack are discussed below:
Parenthesis matching: Stacks can be used to find out whether the given equation is balanced or not. It mainly checks
for the matching of left and right parenthesis in an equation.
Example:
(A + B (x+ y) +C is not a valid expression because it misses right parenthesis
(A + B (x+ y) +C) is a valid expression
Convert from infix to postfix: Stacks can be used to convert an infix expression to post fix expression
Example:
Infix expression: (a+b)*c
Postfix expression: a b + c *
Prefix expression: * + a b c
To evaluate postfix expression: To evaluate postfix expression, stacks can be used. While evaluating, operands are
stored onto a stack and popped when an operator occurs.
Example:
The postfix expression “2 3 * 6 +” gets executed as “12”
Recursion: Computer program implementing recursion uses to remember the pending jobs to be performed. It handles
all the pending jobs in a stack manner.
Example:
A “Fact(n)” function written in a recursive method gets executed as follows:
Fact(3) = 3 * Fact (2)
= 3 * 2 * Fact (1)
=3*2*1
=3*2
=6
Keeping track of function calls: To keep track of function calls, stack can be used to return to the point where it is
called after executing the function. When a function is called the control shifts to the function and executes all the
statements there. But, to return to the point where it is called a stack is used to remember the address of the calling
statement.
main()
{
fun();
……
……
}
fun()
{
……
……
}
Example:

Computer applications that use undo features: Undo is to cancel the previous action. To remember these in LIFO
order computer applications use stack.
Solving Hanoi Towers problem: Stack solves the most popular Hanoi towers problem in which an ordered set of
disks must be moved into another needle that holds disks in the same order using an auxiliary needle as per some
rules.
Process of converting “infix expression” to “postfix expression”.:

Infix expression uses operator between the operands and it requires parenthesis for proper evaluation and one has to
know the priorities of the operators while evaluating it Ex: Infix expression: (a + b)*c + d * e
Postfix expression uses operator after the operands and it does not include parenthesis. You need not know the
priorities of the operators while evaluating it
Ex: Postfix expression: a b + c * d e * +
Process of converting “infix expression” to “postfix expression”:
Initially stack must be empty and the infix expression is scanned from left to right one at a time.
1. When an operand is encountered, it is directly printed
2. When a left parenthesis ‘(‘ is encountered, it is pushed onto stack.
3. When a right parenthesis ‘)’ is encountered, pop all till matching left parenthesis is encountered. Print all the
popped elements excluding parenthesis.
4. When an operator is encountered and the operator at the top of the stack has lower priority, push it onto the
stack.
5. Otherwise, pop all the elements till an element with lower priority is found or till the stack is empty and print
them.
6. Now, push the operator onto the stack.
7. Repeat the same process till the end of the equation
8. Finally pop all the elements of the stack and print them.
Consider the infix expression “ (a + b) * c + d * e ”
Symbol output Stack contents
Start Empty
( (
a a (
+ a (+
b ab (+
) ab+ Empty
* ab+ *
c ab+c *
+ ab+c* +
d ab+c*d +
* ab+c*d +*
e ab+c*de +*
End a b + c * d e * + Empty
Process of evaluating postfix expression:
Postfix expression uses operator after the operands and it does not include parenthesis. You need not know the
priorities of the operators while evaluating it
Ex: Infix Expression: “(2+3) * 4 + 5 * 2”
Postfix expression: “2 3 + 4 * 5 2 * +”
Result after evaluation: “30”
Process of evaluating “postfix expression”:
Initially stack must be empty and the postfix expression is scanned from left to right one at a time.
1. If a number is encountered it is pushed onto the stack
2. If an operator is encountered, pop two operands from the stack and compute the result by using operator on
the two operands. Push the result onto the stack.
3. Repeat the same process till the end of the equation
4. Finally, pop the result from stack and print it.
Consider the Postfix expression “2 3 + 4 * 5 2 * +”. The evaluation of the expression is tabulated below.
Symbol Computation Stack Contents
2 2
3 2, 3
+ 2+3 5
4 5, 4
* 5*4 20
5 20, 5
2 20, 5, 2
* 5*2 20, 10
+ 20 + 10 30
Program to evaluate postfix expression:
import java.io.*;
import java.util.*;
class Stack
{
private int a[]=new int[20];
int top;
Stack()
{
top=0;
}
public void push(int v)
{
if(top<20)
a[top++]=v;
else
System.out.println("Overflow");
}
public int pop()
{
if(top>0)
return a[--top];
else
{
System.out.println("Underflow");
return -1;
}
}
public boolean isEmpty()
{
return top==0;
}
}
class Postfix
{
public static void main(String as[]) throws Exception
{
int a,b,c=0;
String str,s;
Stack s1=new Stack();
int i;
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.print("Enter Postfix expression:");
str=br.readLine();
StringTokenizer st=new StringTokenizer(str,",");
while(st.hasMoreTokens())
{
s=st.nextToken();
if(("+-*/").indexOf(s)>=0)
{
b=s1.pop();
a=s1.pop();
switch(s.charAt(0))
{
case '+':c=a+b;
break;
case '-':c=a-b;
break;
case '*':c=a*b;
break;
case '/':c=a/b;
break;
}
s1.push(c);
}
else
s1.push(Integer.parseInt(s));
}
System.out.println("Result="+s1.pop());
}
}
/* Output */
/* you may give postfix of (2*3)+(4/2) Note: Separate operands and operators with commas*/
Enter Postfix expression:2,3,*,4,2,/,+
Result=8
Queues:
A queue is a linear data structure in which elements are added at the “rear” and deleted from the “front”.
A queue is a type of abstract data type that can be implemented as a linear list.
A queue has a front and a rear.
Insertion occurs at the rear of the list, and deletion occurs at the front of the list. This is called as “first-in-first-out”
(FIFO) method.
The some common words associated with queue:
Enqueue (qstore): adding an element to the queue at its rear
Dequeue (qdelete): deleting an element at the front of a queue.
Empty: status of queue being empty
Full: status of a queue being full.
Front: Refers to the first element of the queue.
Rear: Refers to the last element of the queue.
Advantages:
Queue is used in many applications such as printing documents
Queue is used in reservation-oriented information systems.
Graphical representation of Queue is shown in below:
Queue & different ways to implement a queue:
A queue is a linear data structure in which elements are added at the “rear” and deleted from the “front”.
A queue is a type of abstract data type that can be implemented as a linear list.
A queue has a front and a rear.
Insertion occurs at the rear of the list, and deletion occurs at the front of the list. This is called as “first-in-first-out”
(FIFO) method.
Queue can be implemented mainly in two ways
1. Using arrays 2. Using Linked lists.
In both the ways, we need to write functions such as “qstore()” and “qdelete()” functions.
Program to implement insert and delete operations on Queue using array method.
class Queue
{
private static int MAX=10;
private int a[]=new int[MAX];
int front,rear;
Queue()
{
front=rear=0;
}
public void insert(int v)
{
if(rear<max)</max)
a[rear++]=v;
else
System.out.println("Overflow");
}
public int del()
{
if(front!=rear)
return a[front++];
else
{
System.out.println("Underflow");
return -1;
}
}
}
class QArray
{
public static void main(String as[])
{
Queue q1=new Queue();
q1.insert(30);
q1.insert(40);
q1.insert(50);
System.out.println(q1.del());
System.out.println(q1.del());
System.out.println(q1.del());
}
}
/* Output */
30
40
50
Program to implement insert and delete operations on Queue using linked list method
class node
{
public int x;
public node next;
}
class Queue
{
public node front,rear;
Queue()
{
front=rear=null;
}
void insert (int v)
{
node temp=new node();
temp.x=v;
temp.next=null;
if(front==null)
front=rear=temp;
else
{
rear.next=temp;
rear=temp;
}
}
int del()
{
int v=front.x;
node temp=front;
front=front.next;
temp=null;
return v;
}
}
class QueueList
{
public static void main(String as[])
{
Queue q1=new Queue();
q1.insert(30);
q1.insert(40);
q1.insert(50);
System.out.println(q1.del());
System.out.println(q1.del());
System.out.println(q1.del());
}
}
/* Output */
30
40
50
operations on queue ADT:

In a queue, insertion occurs at the rear of the list, and deletion occurs at the front of the list. This is called as
“first-in-first-out” (FIFO) method.
The operations on queue are discussed below:
qstore: adding elements at the end of a queue.
qdelete: removing elements from the front of the queue.
isfull: isfull finds out whether the queue is full
isempty: isempty finds out whether the queue is empty
create: to create an empty queue
first: to return the first element of a queue
last: to return the last element of a queue
Applications of queues:
In a queue, insertion occurs at the rear of the list, and deletion occurs at the front of the list. This is called as
“first-in-first-out” (FIFO) method.
Printing using the computer: When several documents are to be printed, the printer prints the documents in FIFO
method only. For this, i.e. to keep track of documents in FIFO order, a queue is used.
Reservation system: Reservations such as air ticket or railway reservation systems, use queue to issue
reservations on first come first serve basis i.e. FIFO method.
Computer networks: Computer networks use queues to give access from server to several clients, who are
connected to the server in FIFO method.
Railroad car rearrangement, wire routing, machine shop simulation
The other applications of queue include Railroad car rearrangement, wire routing (path finding), and machine
shop simulation.
Different types of queues:
A queue is a type of abstract data type that can be implemented as a linear or circular list.
A queue has a front and a rear.
Queue can be of four types:
1. Simple Queue
2. Circular Queue
3. Priority Queue
4. Dequeue (Double Ended queue)
1. Simple Queue: In Simple queue Insertion occurs at the rear of the list, and deletion occurs at the front of the
list.

2. Circular Queue : A circular queue is a queue in which all nodes are treated as circular such that the first node
follows the last node.

3. Priority Queue: A priority queue is a queue that contains items that have some preset priority. When an element has
to be removed from a priority queue, the item with the highest priority is removed first

4. Dequeue (Double Ended queue): In dequeue(double ended queue) Insertion and Deletion occur at both the ends i.e.
front and rear of the queue.

Circular queues:
A circular queue is a queue in which all nodes are treated as circular such that the first node follows the last node.
In a linked list circular queue, the rear node always has the reference of the front node. Even if the front node is
removed the rear node has the reference of the new front node.
The common operations on circular queue are:
qstore(: adding elements at the end of a circular queue.
qdelete: removing elements from the front of the circular queue.
isfull: isfull finds out whether the circular queue is full
isempty: isempty finds out whether the circular queue is empty
create: to create an empty circular queue
Applications of circular queue:
Adding large integers: Large integers can be added very easily using circular queues. Here the rightmost digit is
placed in the front node and leftmost digit is placed in the rear node.
Memory management: The unused memory locations in the case of ordinary queues can be utilized in circular queues.
Computer controlled traffic system: In computer controlled traffic system, circular queues are used to switch on
the traffic lights one by one repeatedly as per the time set.

priority queue:
A priority queue is a queue that contains items that have some preset priority.
When an element has to be removed from a priority queue, the item with the highest priority is removed first.
Priority queue can be represented using heaps. A heap node contains the value in every node is at least as
large as the values in the child nodes.

In a priority queue, elements are inserted as per their priority levels. And when removing a node with highest priority
must be removed first. When all elements with highest priority are removed, it removes nodes with next highest
priority.
Applications of priority queue:
To find kth largest element: To find kth biggest element in a list of elements a priority queue can be maintained to
indicate their corresponding degree. And the required element can be directly picked up.
Sorting: Some sorting techniques use priority queues for implementing sorting.
Event simulation: A simulation consists of processing events. The events with priority levels are loaded onto a priority
queue and processed as per their priorities.
Job scheduling: In the application of job scheduling, jobs with priority are entered into the queue and the job with
highest priority is chosen for execution.
Program to implement a priority queue:
class PriorityQ
{
private int a[]=new int[10];
private int n;
PriorityQ()
{
n = 0;
}
public void insert(int v)
{
int i;
if (n==0)
a[n++]=v;
else
{
for (i=n-1;i>=0;i--)
{
if (v>a[i]) // if new v is larger,
a[i+1] = a[i]; // shift upward
else
break;
}
a[i+1] = v;
n++;
}
}
public int delete()
{
return a[--n];
}
public void show()
{
System.out.println("\n Priority Queue Elements:");
for(int i=0;i<n;i++)</n;i++)
System.out.print("\t"+a[i]);
}
}
class PQueueTest
{
public static void main(String[] args)
{
PriorityQ p1 = new PriorityQ();
p1.insert(30);
p1.insert(20);
p1.insert(50);
p1.insert(10);
p1.show();
p1.delete();
System.out.println("\n After Deleting:");
p1.show();
}
}
/* Output */
Priority Queue Elements:
50 30 20 10
After Deleting:
Priority Queue Elements:
50 30 20
Dequeue:
DEQueue stands for double ended queue. In this, insertion and Deletion occur at both the ends i.e. front and rear
of the queue.

The common operations on dequeue are:


insertFirst(): to add element at the end of a dequeue.
insertLast(): to delete element at the beginning of a dequeue.
deleteFirst(): to remove first element of the dequeue.
deleteLast(): to remove last element of the dequeue.
isFull(): isfull finds out whether the dequeue is full.
isEmpty(): isempty finds out whether the circular dequeue is empty.
create(): to create an empty dequeue
Program to implement a dequeue:
class node
{
public int x;
public node next;
}
class DEQueue
{
public node front,rear;
DEQueue()
{
front=rear=null;
}
void addFront (int v)
{
node temp=new node();
temp.x=v;
if(front==null)
front=rear=temp;
else
{
temp.next=front;
front=temp;
}
}
void addRear (int v)
{
node temp=new node();
temp.x=v;
temp.next=null;
if(front==null)
front=rear=temp;
else
{
rear.next=temp;
rear=temp;
}
}
int delFront()
{
int v=front.x;
node temp=front;
front=front.next;
temp=null;
return v;
}
int delRear()
{
int v=rear.x;
node ptr=front,temp;
while(ptr.next!=rear)
ptr=ptr.next;
temp=rear;
rear=ptr;
rear.next=null;
temp=null;
return v;
}
void show()
{
System.out.println("\nDouble Ended Queue Elements:");
for(node ptr=front;ptr!=rear;ptr=ptr.next)
System.out.println("\t"+ptr.x);
System.out.println("\t"+rear.x);
}
}
class DEQueueTest
{
public static void main(String as[])
{
DEQueue q1=new DEQueue();
q1.addFront(30);
q1.addFront(40);
q1.addRear(88);
q1.addRear(99);
q1.show();
q1.delFront();
q1.delRear();
q1.show();
}
}
/* Output */
Double Ended Queue Elements:
40
30
88
99
Double Ended Queue Elements:
30
88
Differences between Stacks queues:
Stacks Queues
Stack is a heap of elements placed Queue is a set of elements placed
one over the other one after the other
Stack mechanism is known as Last Queue mechanism is known as First
In First Out (LIFO) In First Out (FIFO)
Stack is mainly characterized by Queue is mainly characterized by
“top” which refers to its top “front” and rear, which refers to its
element. first and last elements.
Both insertion and deletion occur at Insertion and deletion occur at two
the same end i.e. at the top. different ends i.e. insertion at rear
and deletion at front.
If there is no value at the top, stack If front=rear, queue is said to be
is said to be empty. empty.
The common terms associated with The common terms associated with
stack are : push, pop, top, isfull, queue are: qinsert, qdelete, front,
isempty. rear, isfull, isempty.

Stack can be implemented using Queue also can be implemented


both arrays and linked lists. using both arrays and linked lists.
This has no sub types It has sub types such as: simple
queue, circular queue, double-ended
queue, priority queue.

UNIT-3
Tree Data Structure

Tree is a non-linear data structure which organizes data in a hierarchical structure and this is a recursive definition.
OR
A tree is a connected graph without any circuits.
Example-

Properties of Tree Data Structure-


There is one and only one path between every pair of vertices in a tree.
A tree with n vertices has exactly n-1 edges.
A graph is a tree if and only if it is minimally connected.
Any connected graph with n vertices and n-1 edges is a tree.
Tree terminology:
Properties of Tree Data Structure-
There is one and only one path between every pair of vertices in a tree.
A tree with n vertices has exactly n-1 edges.
A graph is a tree if and only if it is minimally connected.
Any connected graph with n vertices and n-1 edges is a tree.
Tree Data Structure Terminology-
Following are few important terms related to tree data structure-
1. Root-
Root node is the origin of tree data structure. It is the first node.
In any tree, there must be only one root node. We can never have multiple root nodes in a tree data structure.
Example-

2. Edge-
The connecting link between any two nodes is called as an edge.
In a tree with n number of nodes, there are exactly n-1 number of edges.
Example-

3. Parent-
The node which has a branch from it to any other node is called as a parent node.
In other words, the node which has a child / children is called as a parent node.
Example-

Here, A, B, C, E and G are parent nodes.


4. Child-
The node which is descendant of any node is called as a child node.
All the nodes except root node are child nodes.
In a tree, any parent node can have any number of child nodes.
Example-

Here,
B and C are children of A
G and H are children of C
K is the only child of G etc
5. Siblings-
Nodes which belong to the same parent are called as siblings.
In other words, nodes with the same parent are called as sibling nodes.
Example-
Here,
B and C are siblings
D, E and F are siblings
G and H are siblings
I and J are siblings

6. Degree-
The total number of children associated with a node is called as degree of that node.
Degree of the tree = Highest degree of a node among all the nodes in the tree
Example-

Here,
Degree of node B = 3
Degree of node A = 2
Degree of node F = 0 etc

7. Internal node-
The node which has at least one child is called as an internal node.
Internal nodes are also called as non-terminal nodes.
Every non-leaf node is an internal node.
Example-

Here, A, B, C, E and G are internal nodes.

8. Leaf node-
The node which does not have any child is called as a leaf node.
Leaf nodes are also called as external nodes or terminal nodes.
Example-
Here, D, I, J, F, K and H are leaf nodes.

9. Level-
In a tree, each step from top to bottom is called as a level.
The level count starts with 0 and increments by 1 at each level or step
Example-

10. Height-
The total number of edges that lies on the longest path from any leaf node to a particular node is called as height of
that node.
Height of the tree = Height of the root node
Height of all leaf nodes = 0
Example-

Here,
Height of node K = 0
Height of node B = 2
Height of node A = 3
Height of node G = 1
Height of node H = 0 etc

11. Depth-
The total number of edges from root node to a particular node is called as depth of that node.
Depth of the tree = Total number of edges from root node to a leaf node in the longest path
Depth of the root node = 0
The terms “level” and “depth” are sometimes used interchangeably.
Example-
Here,
Depth of node A = 0
Depth of node B = 1
Depth of node K = 3 etc

12. Subtree-
In a tree, each child from a node forms a sub tree recursively.
Every child node forms a sub tree on its parent node.
Example-

13. Forest-
A forest is a set of disjoint trees.
Example-

Binary Tree-
Binary tree is a special type of Tree Data Structure in which every node can have at most 2 children.
Thus, In a binary tree, each node has either 0 child or 1 child or 2 children.
Example-
Types of Binary Tree-
1. Rooted Binary Tree
2. Full / Strictly Binary Tree
3. Complete / Perfect Binary Tree
4. Almost Complete Binary Tree
5. Skewed Binary Tree

1. Rooted Binary Tree-


Rooted binary tree is a binary tree that has a root node and every node has at most 2 children.

Example-

2. Full / Strictly Binary Tree-


 A full / strictly binary tree is a binary tree in which every node has either 0 or 2 children.
Example-

3. Complete / Perfect Binary Tree-


 A complete / perfect binary tree is a binary tree in which every internal node has exactly 2 children and all the leaf
nodes are at the same level.
Example-
4. Almost Complete Binary Tree-
 An almost complete binary tree is a binary tree in which all the levels are completely filled except possibly the last
level and the last level must be strictly filled from left to right.
Example-

5. Skewed Binary Tree-


 A skewed binary tree is a binary tree where all the nodes except one node have one and only one child. The
remaining node has no child.
OR

 A skewed binary tree is a binary tree of n nodes such that its depth is n-1.
Example-

properties of Binary Tree:


There are several properties of Binary Tree which are listed below as formulas. These formulas are extremely
important from the examination point of view.
Formula-01:

Minimum number of nodes in a binary tree of height H = H + 1

Example-
To construct a binary tree of height = 4, we need at least 4 + 1 = 5 nodes.
Formula-02:

Maximum number of nodes in a binary tree of height H = 2H+1 – 1

Example-
Maximum number of nodes in a binary tree of height = 3
= 23+1 – 1
= 16 – 1
= 15

We can not insert more number of nodes in this binary tree.


Thus, in a binary tree of height =3, maximum number of nodes that can be inserted = 15.

Formula-03:

Total Number of leaf nodes in a Binary Tree = Total Number of Degree-2 nodes + 1

It is very important to note from here that the number of leaf nodes in any binary tree depends only on the number of
degree-2 nodes.

Example-
Consider the following binary tree-

Here,
 Number of leaf nodes = 3
 Number of degree-2 nodes = 2
This verifies the above relation.

Formula-04:

Maximum number of nodes at any level ‘L’ in a binary tree = 2L

Example-
Maximum number of nodes at level = 2 in a binary tree
= 22
=4
Thus, in a binary tree, maximum number of nodes that we can have at level-2 = 4.
Applications of Binary Tree:
Applications of binary trees
 Binary Search Tree - Used in many search applications where data is constantly entering/leaving, such as
the map and set objects in many languages' libraries.
 Binary Space Partition - Used in almost every 3D video game to determine what objects need to be rendered.
 Binary Tries - Used in almost every high-bandwidth router for storing router-tables.
 Hash Trees - used in p2p programs and specialized image-signatures in which a hash needs to be verified, but
the whole file is not available.
 Heaps - Used in implementing efficient priority-queues, which in turn are used for scheduling processes in
many operating systems, Quality-of-Service in routers, and A* (path-finding algorithm used in AI applications,
including robotics and video games). Also used in heap-sort.
 Huffman Coding Tree (Chip Uni) - used in compression algorithms, such as those used by the .jpeg and .mp3
file-formats.
 GGM Trees - Used in cryptographic applications to generate a tree of pseudo-random numbers.
 Syntax Tree - Constructed by compilers and (implicitly) calculators to parse expressions.
 Treap - Randomized data structure used in wireless networking and memory allocation.
 T-tree - Though most databases use some form of B-tree to store data on the drive, databases which keep all
(most) their data in memory often use T-trees to do so.
Advantages of binary tree:
Searching in a binary tree becomes faster
Binary tree provides 6 traversals.
Two of six traversals give sorted order of the elements
Maximum and minimum elements can be directly picked up
It is used for graph traversals and to convert an expression to postfix and prefix forms.
Representing Binary Tree in memory
Let T be a Binary Tree. There are two ways of representing Tin the memory as follow
1. Sequential Representation of Binary Tree.
2. Link Representation of Binary Tree.
1) Linked Representation of Binary Tree
Consider a Binary Tree T. T will be maintained in memory by means of a linked list representation which uses three
parallel arrays; INFO, LEFT, and RIGHT pointer variable ROOT as follows. In Binary Tree each node N of T will
correspond to a location k such that
1. LEFT [k] contains the location of the left child of node N.
2. INFO [k] contains the data at the node N.
3. RIGHT [k] contains the location of right child of node N.
Representation of a node:

In this representation of binary tree root will contain the location of the root R of T. If any one of the subtree is empty,
then the corresponding pointer will contain the null value if the tree T itself is empty, the ROOT will contain the null
value.
Example
Consider the binary tree T in the figure. A schematic diagram of the linked list representation of T appears in the
following figure. Observe that each node is pictured with its three fields, and that the empty subtree is pictured by
using x for null entries.
Binary Tree

Linked Representation of the Binary Tree

2) Sequential representation of Binary Tree


Let us consider that we have a tree T. let our tree T is a binary tree that us complete binary tree. Then there is an
efficient way of representing T in the memory called the sequential representation or array representation of T. This
representation uses only a linear array TREE as follows:
1. The root N of T is stored in TREE [1].
2. If a node occupies TREE [k] then its left child is stored in TREE [2 * k] and its right child is stored
into TREE [2 * k + 1].
For Example:
Consider the following Tree:

Its sequential representation is as follow:

“in order”, “post order” and “pre order” traversals in binary trees:

A Binary tree is a non-linear data structure in which each node has maximum of two child nodes. The tree
connections can be called as branches.
Binary tree provides six traversals, which include “in order”, “post order” and “pre order” traversals.
Inorder gives us elements in ascending order
In order traversal:
In order traversal follows LVR method (L- left, V- visit vertex, R- right). This order gives sorted order in ascending
order.
1. Move to the root node of a tree.
2. Move to its left node if exists.
3. Repeat the second step till a node with no left encounters.
4. Now, print the element and move to right if it exists.
5. Repeat the above three steps for all pending-nodes in a stack manner.
LVR(tree t)
{
if(t)
{
LVR(t.left);
System.out.println(t.x);
LVR(t.right);
}
}
Pre order traversal:
Pre order traversal follows VLR method
1. Move to the root node of a tree.
2. Print the element and move to left node if exists.
3. Repeat the second step till a node with no left encounters.
4. Now, move to right if it exists and print element.
5. Repeat the above three steps for all pending-nodes in a stack manner.
VLR(tree t)
{
if(t)
{
System.out.println(t.x);
VLR(t.left);
VLR(t.right);
}
}
Post order traversal:
Post order traversal follows LRV method
1. Move to the root node of a tree.
2. Move to left node if exists.
3. Repeat the second step till a node with no left encounters.
4. Now, move to right if it exists.
5. Repeat from step 2 till a node with no left and no right encounters.
6. Print the element.
7. Repeat the above three steps for all pending-nodes in a stack manner.
LRV(tree t)
{ if(t)
{
LRV(t.left);
LRV(t.right);
System.out.println(t.x);
}
}
40
56
48
35

28
Binary Tree

Example:
A binary tree is shown for the elements
40, 56, 35, 48, 22, 65, 28
In order (LVR): 22, 28, 35, 40, 48, 56, 65
Pre order (VLR): 40, 35, 22, 28, 56, 48, 65
Post Order (LRV): 28, 22, 35, 48, 65, 56, 40

creation, insertion and deletion of nodes in a binary tree.:


Creation:
1. To create a binary tree, first create an empty node whose left and right are set to null.
2. For the first time, create a node and read a value and take it as root
3. Next onwards, check whether the element is less than or greater than the root element.
4. If the element is less than root element continue with left sub tree
5. If the element is greater than root element continue with right sub tree
6. Repeat the steps 4 and 5 accordingly till an empty node encounters
7. Create a node and insert it there.
Insertion:
1. First, check whether the element is less than or greater than the root element.
2. If the element is less than root element continue with left sub tree
3. If the element is greater than root element continue with right sub tree
4. Repeat the steps 2 and 3 accordingly till an empty node encounters
5. Create a node and insert it there.
Deletion:
1. Consider three cases for deleting a node
Deleting a node with no children
Deleting a node with one child
Deleting a node with two children
2. If the tree is ‘null’, print the message accordingly.
3. First, search for that starting from the root. i.e. move to left if the element is less than the parent or right till the
element is encountered or an empty node is encountered
4. If empty node is encountered, print “no element”
5. Otherwise, if the node has no children, delete it directly.
6. If the node has only left child, parent node must be linked to its left before deleting the node
7. If the node has only right child, parent node must be linked to its right before deleting the node
8. If a node to be deleted has two children, find minimum value in its right sub-tree. Replace the element to be
deleted with the minimum element and repeat the same process to delete the node containing minimum element.
Program to create a binary tree and print the elements in different traversals
class tree
{
int x;
tree left;
tree right;
}
class BTree
{
private tree t;
BTree()
{
t=null;
}
public void insert(int v)
{
t=insert(t,v);
}
public void showLVR()
{
System.out.println("\nLVR :\n");
LVR(t);
}
public void showVLR()
{
System.out.println("\nVLR :\n");
VLR(t);
}
public void showLRV()
{
System.out.println("\nLRV :\n");
LRV(t);
}
tree insert(tree t, int v)
{
if(t==null)
{
t = new tree();
t.x = v;
t.left = null;
t.right = null;
}
else if(v<t.x)</t.x)
t.left = insert(t.left,v);
else if(v>t.x)
t.right = insert(t.right,v);
return t;
}
public void LVR(tree t)
{
if(t!=null)
{
LVR(t.left);
System.out.print("\t"+t.x);;
LVR(t.right);
}
}
public void LRV(tree t)
{
if(t!=null)
{
LRV(t.left);
LRV(t.right);
System.out.print("\t"+t.x);
}
}
public void VLR(tree t)
{
if(t!=null)
{
System.out.print("\t"+t.x);
VLR(t.left);
VLR(t.right);
}
}
}
class BTreeTest
{
public static void main(String as[])
{
BTree t1=new BTree();
t1.insert(30);
t1.insert(20);
t1.insert(10);
t1.insert(40);
t1.showLVR();
t1.showVLR();
t1.showLRV();
}
}
/* Output */
LVR :
10 20 30 40
VLR :
30 20 10 40
LRV :
10 20 40 30

Assume the elements to be:

Input: 45,20,80,15, 30,70, 90

45 80 70 15 30

Output :
LVR :
15 20 30 45 70 80 90
VLR :
45 20 15 30 80 70 90
LRV :
15 30 20 70 90 80 45
maximum and minimum values in a binary tree:

A Binary tree is a non-linear data structure in which each node has maximum of two child nodes. Finding out
maximum and minimum values in a binary tree is quite easy. We need not write any special logic to find out
maximum or minimum values. Because, the last node to the left of a root contains the minimum value and the last
node to the right of a root contains the maximum value.

The java method to find out the minimum value is shown below:
public int getMinimum(tree root)
{
tree p= root;
while(p.left!=null)
p=p.left;
return p.x;
}
Similarly, the java method to find out the maximum value is shown below:
public int getMaximum(tree root)
{
tree p= root;
while(p.right!=null)
p=p.right;
return p.x;
}
Binary search Tree (BST):
Binary search tree
First of all, binary search tree (BST) is a dynamic data structure, which means, that its size is only limited by
amount of free memory in the operating system and number of elements may vary during the program run. Main
advantage of binary search trees is rapid search, while addition is quite cheap. Let us see more formal definition
of BST.
Binary search tree is a data structure, which meets the following requirements:
 it is a binary tree;
 each node contains a value;
 a total order is defined on these values (every two values can be compared with each other);
 left subtree of a node contains only values lesser, than the node's value;
 right subtree of a node contains only values greater, than the node's value.
Notice, that definition above doesn't allow duplicates.
Example of a binary search tree

Binary search tree. Internal representation


Like any other dynamic data structure, BST requires storing of some additional auxiliary data, in order to keep its
structure. Each node of binary tree contains the following information:
 a value (user's data);
 a link to the left child (auxiliary data);
 a link to the right child (auxiliary data).
Depending on the size of user data, memory overhead may vary, but in general it is quite reasonable. In some
implementations, node may store a link to the parent, but it depends on algorithm, programmer want to apply to BST.
For basic operations, like addition, removal and search a link to the parent is not necessary. It is needed in order to
implement iterators.
With a view to internal representation, the sample from the overview changes:
Leaf nodes have links to the children, but they don't have children. In a programming language it means, that
corresponding links are set to NULL.
public class BSTNode {
private int value;
private BSTNode left;
private BSTNode right;

public BSTNode(int value) {


this.value = value;
left = null;
right = null;
}
}

public class BinarySearchTree {


private BSTNode root;

public BinarySearchTree() {
root = null;
}
}

Adding a value
Adding a value to BST can be divided into two stages:
 search for a place to put a new element;
 insert the new element to this place.
Let us see these stages in more detail.
Search for a place
At this stage analgorithm should follow binary search tree property. If a new value is less, than the current node's
value, go to the left subtree, else go to the right subtree. Following this simple rule, the algorithm reaches a node,
which has no left or right subtree. By the moment a place for insertion is found, we can say for sure, that a new
value has no duplicate in the tree. Initially, a new node has no children, so it is a leaf. Let us see it at the picture.
Gray circles indicate possible places for a new node.

Now, let's go down to algorithm itself. Here and in almost every operation on BST recursion is utilized. Starting
from the root,
1. check, whether value in current node and a new value are equal. If so, duplicate is found. Otherwise,
2. if a new value is less, than the node's value:
o if a current node has no left child, place for insertion has been found;
o otherwise, handle the left child with the same algorithm.
3. if a new value is greater, than the node's value:
o if a current node has no right child, place for insertion has been found;
o otherwise, handle the right child with the same algorithm.
Just before code snippets, let us have a look on the example, demonstrating a case of insertion in the binary search
tree.
Example
Insert 4 to the tree, shown above.
code:

public class BinarySearchTree {


public boolean add(int value) {


if (root == null) {
root = new BSTNode(value);
return true;
} else
return root.add(value);
}
}
public class BSTNode {

public boolean add(int value) {
if (value == this.value)
return false;
else if (value <this.value) {
if (left == null) {
left = new BSTNode(value);
return true;
} else
return left.add(value);
} else if (value > this.value) {
if (right == null) {
right = new BSTNode(value);
return true;
} else
return right.add(value);
}
return false;
}
}
Binary search tree. Removing a node
Remove operation on binary search tree is more complicated, than add and search. Basically, in can be divided
into two stages:
 search for a node to remove;
 if the node is found, run remove algorithm.
Remove algorithm in detail
There are three cases, which are described below.
1. Node to be removed has no children.
This case is quite simple. Algorithm sets corresponding link of the parent to NULL and disposes the
node.
Example. Remove -4 from a BST.

2. Node to be removed has one child.


It this case, node is cut from the tree and algorithm links single child (with it's subtree) directly to the
parent of the removed node.
Example. Remove 18 from a BST.

3. Node to be removed has two children.


This is the most complex case. To solve it, let us see one useful BST property first. We are going to use
the idea, that the same set of values may be represented as different binary-search trees. For example
those BSTs:

contains the same values {5, 19, 21, 25}. To transform first tree into second one, we can do follo wing:
o choose minimum element from the right subtree (19 in the example);
o replace 5 by 19;
o hang 5 as a left child.
The same approach can be utilized to remove a node, which has two children:
o find a minimum value in the right subtree;
o replace value of the node to be removed with found minimum. Now, right subtree contains a
duplicate!
o apply remove to the right subtree to remove a duplicate.
Notice, that the node with minimum value has no left child and, therefore, it's removal may result in first
or second cases only.
Example. Remove 12 from a BST.
Find minimum element in the right subtree of the node to be removed. In current example it is 19.

Replace 12 with 19. Notice, that only values are replaced, not nodes. Now we have two nodes with the
same value.

Remove 19 from the left subtree.

Code snippets

First, check first if root exists. If not, tree is empty, and, therefore, value, that should be removed, doesn't exist in
the tree. Then, check if root value is the one to be removed. It's a special case a nd there are several approaches to
solve it. We propose the dummy root method, when dummy root node is created and real root hanged to it as a
left child. When remove is done, set root link to the link to the left child of the dummy root.
Remove method in the BSTNode class should return not the boolean value, but the link to the disposed node and
free the memory in BinarySearchTree class.
public class BinarySearchTree {

public boolean remove(int value) {


if (root == null)
return false;
else {
if (root.getValue() == value) {
BSTNode auxRoot = new BSTNode(0);
auxRoot.setLeftChild(root);
boolean result = root.remove(value, auxRoot);
root = auxRoot.getLeft();
return result;
} else {
return root.remove(value, null);
}
}
}
}
public class BSTNode {

public boolean remove(int value, BSTNode parent) {
if (value < this.value) {
if (left != null)
return left.remove(value, this);
else
return false;
} else if (value > this.value) {
if (right != null)
return right.remove(value, this);
else
return false;
} else {
if (left != null && right != null) {
this.value = right.minValue();
right.remove(this.value, this);
} else if (parent.left == this) {
parent.left = (left != null) ? left : right;
} else if (parent.right == this) {
parent.right = (left != null) ? left : right;
}
return true;
}
}

public int minValue() {


if (left == null)
return value;
else
return left.minValue();
}
}
Applications of Binary Search Tree:
1. used to efficiently store data in sorted form in order to access and search stored elements quickly.
2. They can be used to represent arithmetic expressions.
3. BST used in Unix kernels for managing a set of virtual memory areas (VMAs).
Threaded binary trees:
What is Threaded Binary Tree??
A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor
of the node (if it exists), and all left child pointers that would normally be null point to the inorder predecessor of the
node.
 We have the pointers reference the next node in an inorder traversal; called threads
 We need to know if a pointer is an actual link or a thread, so we keep a boolean for each pointer
Why do we need Threaded Binary Tree?
 Binary trees have a lot of wasted space: the leaf nodes each have 2 null pointers. We can use these pointers to
help us in inorder traversals.
 Threaded binary tree makes the tree traversal faster since we do not need stack or recursion for traversal
Types of threaded binary trees:
 Single Threaded: each node is threaded towards either the in-order predecessor or successor (left orright)
means all right null pointers will point to inorder successor OR all left null pointers will point to inorder
predecessor.
 Double threaded: each node is threaded towards both the in-order predecessor and successor (left andright)
means all right null pointers will point to inorder successor AND all left null pointers will point to inorder
predecessor.

Single and Double threaded binary tree (1)

A heap, in the context of data structure, is a tree-based data structure that satisfies the heap property, where each
element is assigned a key value, or weighting. The lower value key always has a parent node with a higher-value key.
This is called a max-heap structure, and among all nodes, the root node has the highest key.

Sometimes, a tree-based structure has a reversed structure rule, where an element with a higher value key always has a
lower value key as a parent node. This is called a min-heap structure, and among all nodes, the root node has the
lowest key.
Heaps perform multiple operations, including:

 Find-max: Searches for the highest key node among a group of nodes
 Find-min: Searches for the lowest key node among a group of nodes
 Delete-max: Deletes the highest key node among a group of nodes
 Delete-min: Deletes the lowest key node among a group of nodes

Heaps also include functions that perform merging, insertion and key changes.
Example:

Min-heap Max-heap
UNIT-4
GRAPHS
Graphs & types of graphs.:
A graph is a collection of vertices or nodes, which are joined as pairs by lines or edges.
Formally, a graph G = (V, E) is an ordered pair of finite sets of Vertices and Edges.
Vertices are also called as nodes or points
Edges are called as lines or arcs.
Vertices are displayed as circles and edges are displayed as lines.
An edge with orientation () is directed edge while an edge with no orientation () is undirected edge.

The types of graphs are listed below:


1. Directed Graph: A graph in which each edge is directed is called Directed graph.
2. Undirected graph: A graph in which each edge is undirected is called an undirected graph.
3. Connected graph: A graph G is connected if there is a path between every pair of vertices.
4. Sub graph: sub graph is a graph in which vertex and edge sets are subsets of those of G.
5. Complete Graph: A graph G is said to be complete if every vertex V is adjacent to every other vertex V in G.
A complete graph with ‘n’ vertices has ‘n (n-1)/2’ edges.
6. Weighted graph: A graph ‘G’ is weighted, if each edge in G is assigned a nonnegative numerical value (cost or
weight).
7. Connected directed graph or strongly connected graph: A directed graph G is said to be connected, or
strongly connected, if for each pair of vertices (v1, v2) there is a path from v1 to v2 and v2 to v1.
Applications of graphs:
Graphs are used in many applications:
Analysis of electrical networks: Graphs, digraphs are used to analyze electrical networks. Electrical circuits
can be represented by graphs
Study of molecular structure: The study of molecular structure of chemical compounds requires graphs.
The representation of airlines routes: To represent airline route system airports are considered to be vertices
and flights to be edges and distance or ticket price to be weights
Networks: Computer networks or communication networks can be represented by graphs. Here, vertices are the
computers and edges are communications lines. Weight could be bandwidth.
Topological sort: Graphs are also used to sort elements using the topological sort algorithm
Finding shortest paths: Graphs can be used to find shortest path between any two vertices, when it has several
routes.

Representation of a Graph:
Graphs can be represented using matrices. Adjacency matrix is n x n matrix A. Each element of ‘A’ is either
zero or one.
Assumptions:
G Graph
V Set of Vertices
(i, j) Edge connecting Vi and Vj
A[i][j Adjacency matrix element
]
i,j Vertices

Example:
Consider the following graph.
The graph has 4 vertices and it is undirected graph. Write 1 to Number of vertices i.e. 1 to 4 as row and column
headers to represent it in adjacency matrix.
If edge exists between any two nodes (row, column headers indicate nodes) write it as 1 otherwise 0 in the
matrix.

Adjacency lists.:
Graphs can be represented as adjacency list that uses array of linked lists.
Consider the following graph. The graph has 4 vertices and it is undirected graph.

4
Vertices : { 1, 2, 3, 4 }
Nodes : (1,2), (1,3), (1,4)
: (2,1), (2,3)
: (3,1), (3,2), (3,4)
: (4,1), (4,3)
Undirected Graph

Adjacency list:
Here we maintain linked list for each adjacency list
In addition to these linked lists, we take an array to maintain all the lists.

Graph traversals. (OR) Depth first and Breadth First Search methods.:
There are mainly two methods for traversing in a graph, Depth first and Breadth first traversals.
Depth First Search (DFS): It uses a STACK to hold the nodes that are waiting to be processed. First we examine the
starting node ‘A’. Then we examine each node along a path ‘P’, which begins at A. i.e. we process one of the
neighbors of ‘A’ and continue along the path. After coming to the end of ‘P’, we back track on ‘P’ until we can
continue along another path.
Algorithm:
1. Initialize all nodes to ready state.
2. Start with a node and push the node ‘A’ in the stack.
3. POP the top node ‘X’ from the stack. Print X.
4. Push all its neighbors omitting the processed ones in to the Stack.
5. Repeat the above two steps till all the elements of graph are visited.
6. Pop all the elements of the stack and print them to complte DFS.
Pseudo code to implement DFS (Depth First Search)
void DepthFirst(Graph G)
{
boolean visited[MAX];
int v;
for(all v in G)
visited[v]=FALSE;
for(all v in G)
if(!visited[v])
traverse(v);
}
void traverse(int v)
{
int w;
visited[v]=TRUE;
visit(v);
for( all w adjacent to v)
if(!visited[w])
traverse(w);
}
2. Breadth First Search (BFS): It uses QUEUE to hold nodes that are waiting to be processed. First we
examine the starting node A. Then we examine all the neighbors of A. And, we continue with the other nodes in
the same manner. No node is processed more the once.
Algorithm:
1. Initialize all nodes to ready state.
2. Start with a node and place it in the Queue.
3. remove an element ‘X’ from queue and Print X.
4. Place all its neighbors omitting the processed ones in the Queue
5. Repeat the above two steps till all the elements of graph are visited.
6. Delete all the elements of the queue and print them to complte BFS.
Pseudo code to implement BFS (Breadth First Search):
void BreadthFirst(Graph G)
{
Queue q;
boolean visited[MAX];
int v,w;
for(all v in G)
visited[v]=FALSE;
initialize(q);
for(all v in G)
{
if(!visited[v])
{
addQueue(v,q);
do
{
deleteQueue(v,q);
visited[v]=TRUE;
visit(v);
for( all w adjacent to v)
if(!visited[w])
addQueue(w);
}while(!empty(q));
}
}
}
Differentiate Graph and Tree.:
Tree Graph
A tree is a data structure in A graph is a collection of vertices
which each node is attached to or nodes, which are joined as pairs
one or more nodes as children by lines or edges
This is a non linear data structure This is a non linear data structure
All the trees can be graphs All the graphs are not trees
It provides six different It provides two different traversals
traversals
It is undirected and connected It can be directed or undirected; can
be connected or not-connected
It cannot be cyclic It can be cyclic or acyclic

minimum spanning trees.:

Minimum Spanning Tree: A spanning tree is a subgraph of G (where G is undirected connected graph), is a tree, and
contains all the vertices of G. A minimum spanning tree is a spanning tree, but has weights associated with the edges,
and the total weight of the tree (the sum of the weights of its edges) is at a minimum. A minimum spanning tree
should satisfy the following conditions.

It should be a sub-graph of the main graph (say ‘G’)


It should have all the vertices of ‘G’
The number of edges should be n-1, and there should not be any cycle.
The total weight of the tree should be least.

For, example consider the following graphs


There are two different algorithms to find out minimum spanning tree:

1. Kruskal algorithm
2. Prim algorithm.

Kruskal's Algorithm: Take a graph with 'n' vertices, keep adding the shortest (least weight) edge, while avoiding the
creation of cycles, until (n - 1) edges have been added. The order, in which the edges are chosen, does not matter if
two or more edges may have the same cost. Different minimum spanning trees may result, but they will all have the
same total weight, which will always be the minimum weight.

Prim's Algorithm: It starts at any vertex in a graph (vertex A, for example), and finds the least weight vertex (vertex
B, for example) connected to the start vertex. Now, from either 'A' or 'B', it will find the next least weighted vertex
connection, without creating a cycle (vertex C, for example). Now, from either 'A', 'B', or 'C', it will find the next least
weighted vertex connection, without creating a cycle, and so on it goes. Eventually, all the vertices will be connected,
without any cycles, which results minimum spanning trees.

UNIT-5
SORTING AND SEARCHING
procedure for Selection sort:

Selection sort is a sorting algorithm is a type of comparison sort. It is inefficient on large lists, and generally performs
worse than insertion sort. Selection sort is noted for its simplicity, and also has performance advantages over more
complicated algorithms. The selection sort performs the same number of comparisons as the bubble sort: N*(N-1)/2.
For 10 data items, this is 45 comparisons. However, 10 items require fewer than 10 swaps. Thus, the selection sort
improves on the bubble sort by reducing the number of swaps necessary from O(N 2) to O(N). Unfortunately, the
number of comparisons remains O(N2).

Selection sort logic:

1. Find the maximum value in the list


2. Swap it with the value in the last position
3. Repeat the steps above for the remainder of the list

Complexity: The time complexity of Selection sort is shown below in three different cases and worst-case
space complexity.

Worst Case Best Case Average Case Worst case Space Complexity
O(n2) O(n2) O(n2) O(n) total, O(1) auxiliary

Example: The selection sort marks the last element (12). It then goes through the list to find the biggest number
(72). It swaps this with the last element and the biggest element is now in its correct position. It then marks the
last but one element (1) and looks through the remaining data (excluding the last element) for the next biggest
number (64). These two numbers are then swapped. This process continues until n-1 passes have been made.

Initial Data 27 11 64 →72 1 12


1st pass 27 11 →64 12 1 72
2nd pass →27 11 1 12 64 72
3rd pass →12 11 1 27 64 72
4th pass 1 →11 12 27 64 72
5th pass 1 11 12 27 64 72
Program: The following program uses Selection sort algorithm to sort the elements of the array.

import java.io.*;
class SelectionSort
{
public static void main(String as[]) throws Exception
{
int i,j,k,n,t,max,a[]=new int[10];
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("How many values:");
n=Integer.parseInt(br.readLine());
System.out.println("Enter Values:");
for(i=0;i<n;i++)</n;i++)
a[i]=Integer.parseInt(br.readLine());
for(i=0;i<n-1;i++)</n-1;i++)
{
max=a[0];
k=0;
for(j=1;j<n-i;j++)</n-i;j++)
{
if(a[j]>max)
{
max=a[j];
k=j;
}
}
t=max;
a[k]=a[n-i-1];
a[n-i-1]=t;
}
System.out.println("After Sorting");
for(i=0;i<n;i++)</n;i++)
System.out.print("\t"+a[i]);
}
}
Output:
How many values: 4
Enter Values:
45
36
87
12
After Sorting:
12 36 45 87
procedure for Insertion sort:

Insertion sort is a simple sorting algorithm, a comparison sort in which the sorted array is built one entry at a time. It is
much less efficient on large lists than more advanced algorithms such as quicksort, or heapsort. However, insertion
sort provides several advantages:

1. It is simple to implement
2. It is efficient for small data sets
3. It is efficient for data sets that are already nearly sorted.
4. Sorting can be done in place i.e. while reading the elements itself.
Insertion sort logic: Every iteration of insertion sort removes an element from the data, inserting it into the correct
position in the already-sorted list. This process is repeated until no elements remain. The choice of which element to
remove from the data is random.

Complexity: The time complexity of Insertion sort is shown below in three different cases and worst-case space
complexity.

Worst Case Best Case Average Case Worst case Space Complexity
O(n2) O(n) O(n2) O(n) total, O(1) auxiliary

Example: The insertion sort starts with the first two elements and creates a sorted sub-list, which in the example
contains 11 and 27. It then looks at the next element (1) and inserts it into the sub-list in its correct position (1, 11, 27).
Then, It takes the next element (72) and does the same, continuing until the sub-list contains all the data.

Initial Data 27 →11 1 72 64 12


1st pass 11 27 →1 72 64 12

2nd pass 1 11 27 →72 64 12

3rd pass 1 11 27 72 →64 12

4th pass 1 11 27 64 72 →12


5th pass 1 11 12 27 64 72

Program: The following program uses bubble sort algorithm to sort the elements of the array

import java.io.*;
class InsSort
{
public static void main(String as[]) throws Exception
{
int i,j,n,t,a[]=new int[10];
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("How many values:");
n=Integer.parseInt(br.readLine());
System.out.println("Enter Values:");
for(i=0;i<n;i++)</n;i++)
a[i]=Integer.parseInt(br.readLine());
for(i=1;i<n;i++)</n;i++)
{
t=a[i];
for(j=i;j>0;j--)
{
if(a[j-1]>t)
a[j]=a[j-1];
else
break;
}
a[j]=t;
}
System.out.println("After Sorting");
for(i=0;i<n;i++)</n;i++)
System.out.print("\t"+a[i]);
}
}
Output:
How many values: 4
Enter Values:
45
36
87
12
After Sorting:
12 36 45 87

procedure for Bubble sort:

Sorting is a technique used to arrange the elements of an array in either ascending order or descending order. To
implement “sorting”, there are several logics such as “bubble sort”, “selection sort” and etc.

Consider the example:

int a[]={20,13,40,50,2}

After sorting the elements of the array in ascending order the elements will be {2,13,20,40,50}

Bubble sort logic: The simplest and the best known sorting technique is the bubble sort. To sort a sequence of n
elements, bubble sort makes n - 1 passes thorough the data. In each pass two elements are compared and swapped if
necessary. For example, the first and the second elements of the set are compared and exchanged if the first element is
greater than the second element; next, the first and the third are compared and swapped if necessary and so on.

Complexity: The time complexity of bubble sort is shown below in three different cases and worst-case space
complexity.

Worst Case Best Case Average Case Worst case Space Complexity
O(n2) O(n) O(n2)
Example: The arrangement of elements is shown for each pass for a set of sample elements. It compares first element
with the second, first with the third and so on. And, in every comparison, It also interchanges if first element is greater
than the second. Notice that after the first pass the smallest element in the sequence has bubbled up into the first
position. In This fashion it sorts all other elements one by one in each pass. In every pass, the sorted elements are
ignored and comparison is continued for the remaining list.
The following example shows how bubble sort technique sorts elements in each pass.
Program:

The following program uses bubble sort algorithm to sort the elements of the array.
import java.io.*;
class BubbleSort
{
public static void main(String as[]) throws Exception
{
int i,j,n,t,a[]=new int[10];
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("How many values:");
n=Integer.parseInt(br.readLine());

System.out.println("Enter Values:");
for(i=0;i<n;i++)</n;i++)
a[i]=Integer.parseInt(br.readLine());
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if(a[i]>a[j])
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
System.out.println("After Sorting");
for(i=0;i<n;i++)</n;i++)
System.out.print("\t"+a[i]);
}
}
Output:
How many values: 4
Enter Values:
45
36
87
12
After Sorting:
12 36 45 87

Procedure for Merge sort:


Merge sort is a divide and conquer algorithm.
Steps to implement Merge Sort:

1) Divide the unsorted array into n partitions, each partition contains 1 element. Here the one element is considered as
sorted.
2) Repeatedly merge partitioned units to produce new sublists until there is only 1 sublist remaining. This will be the
sorted list at the end.
Merge sort is a fast, stable sorting routine with guaranteed O(n*log(n)) efficiency. When sorting arrays, merge sort
requires additional scratch space proportional to the size of the input array. Merge sort is relatively simple to code and
offers performance typically only slightly below that of quicksort

package com.java2novice.sorting;

public class MyMergeSort {

private int[] array;


private int[] tempMergArr;
private int length;

public static void main(String a[]){

int[] inputArr = {45,23,11,89,77,98,4,28,65,43};


MyMergeSort mms = new MyMergeSort();
mms.sort(inputArr);
for(int i:inputArr){
System.out.print(i);
System.out.print(" ");
}
}

public void sort(int inputArr[]) {


this.array = inputArr;
this.length = inputArr.length;
this.tempMergArr = new int[length];
doMergeSort(0, length - 1);
}
private void doMergeSort(int lowerIndex, int higherIndex) {

if (lowerIndex < higherIndex) {


int middle = lowerIndex + (higherIndex - lowerIndex) / 2;
// Below step sorts the left side of the array
doMergeSort(lowerIndex, middle);
// Below step sorts the right side of the array
doMergeSort(middle + 1, higherIndex);
// Now merge both sides
mergeParts(lowerIndex, middle, higherIndex);
}
}

private void mergeParts(int lowerIndex, int middle, int higherIndex) {

for (int i = lowerIndex; i <= higherIndex; i++) {


tempMergArr[i] = array[i];
}
int i = lowerIndex;
int j = middle + 1;
int k = lowerIndex;
while (i <= middle && j <= higherIndex) {
if (tempMergArr[i] <= tempMergArr[j]) {
array[k] = tempMergArr[i];
i++;
} else {
array[k] = tempMergArr[j];
j++;
}
k++;
}
while (i <= middle) {
array[k] = tempMergArr[i];
k++;
i++;
}

}
}

OUTPUT:
4 11 23 26 43 45 65 77 89 98

Procedure for Quicksort:

Quicksort is significantly faster than other algorithms like bubble sort, because its inner loop can be efficiently
implemented on most architectures. Quicksort sorts by employing a divide and conquer strategy to divide a list into
two sub-lists. Quicksort is also known as "partition-exchange sort".

Qucik-sort logic:

Pick an element, called a pivot, from the list (here we are choosing last element).
Reorder the list so that all elements which are less than the pivot come before the pivot and so that all
elements greater than the pivot come after it (equal values can go either way).
After this partitioning, the pivot is in its final (proper) position. This is called partitioning.
Recursively sort the sub-list of lesser elements and the sub-list of greater elements.
The base (terminating) case of the recursion are lists of size zero or one, which are always sorted.
Complexity: The time complexity of Selection sort is shown below in three different cases and worst-case
space complexity.

Worst Case Best Case Average Case Worst case Space Complexity
O(n2) O(nlogn) O(nlogn) O(n)

Example: The quick sort takes the last element (9) and places it such that all the numbers in the left sub-list are
smaller and all the numbers in the right sub-list are bigger. It then quick sorts the left sub-list ({1}) and then
quick sorts the right sub-list ({63,72,64,58,14,27}) in similar fashion. This is a recursive algorithm, since it is
defined in terms of itself. This reduces the complexity of programming.
Initial Data 27 63 1 72 64 58 14 9

1st pass 1 < 9 > 63 72 64 58 14 27

63 72 64 58 14 27

14 < 27 > 64 58 72 63

2nd pass 1 9 14 27 64 58 72 63

64 58 72 63
58 < 63 > 72 64

3rd pass 1 9 14 27 58 63 72 64

72 64
< 64 > 72
4th pass 1 9 14 27 58 63 64 72

Program: The following program uses Quick sort algorithm to sort the elements of the array.

import java.io.*;
class QuickSort
{
public static void qsort(int a[],int min,int max)
{
int t,v,i,j;
if (max > min)
{
v = a[max];
i=min;
j = max-1;
do
{
while ((a[i]
i++;
while ((a[j]>v) && j!=0)
j--;
t = a[i];
a[i] = a[j];
a[j] = t;
} while (j > i);
a[j] = a[i];
a[i] = a[max];
a[max] = t;
qsort(a,min,i-1);
qsort(a,i+1,max);
}
}
public static void main(String as[]) throws Exception
{
int i,n,a[]=new int[10];
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
System.out.println("How many values:");
n=Integer.parseInt(br.readLine());
System.out.println("Enter Values:");
for(i=0;i<n;i++)</n;i++)
a[i]=Integer.parseInt(br.readLine());
qsort(a,0,n-1);
System.out.println("After Sorting");
for(i=0;i<n;i++)</n;i++)
System.out.print("\t"+a[i]);
}
}
Output:
How many values: 4
Enter Values:
45
36
87
12
After Sorting:
12 36 45 87
Heap Sort :
Heap sort involves building a Heap data structure from the given array and then utilizing the Heap to sort the array.
Heap is a special tree-based data structure, that satisfies the following special heap properties:
1. Shape Property: Heap data structure is always a Complete Binary Tree, which means all levels of the tree are
fully filled.

2. Heap Property: All nodes are either greater than or equal to or less than or equal to each of its children. If
the parent nodes are greater than their child nodes, heap is called a Max-Heap, and if the parent nodes are
smaller than their child nodes, heap is called Min-Heap.
Heap sort algorithm is divided into two basic parts:
 Creating a Heap of the unsorted list/array.
 Then a sorted array is created by repeatedly removing the largest/smallest element from the heap, and
inserting it into the array. The heap is reconstructed after each removal.
Initially on receiving an unsorted list, the first step in heap sort is to create a Heap data structure(Max-Heap or Min-
Heap). Once heap is built, the first element of the Heap is either largest or smallest(depending upon Max-Heap or
Min-Heap), so we put the first element of the heap in our array. Then we again make heap using the remaining
elements, to again pick the first element of the heap and put it into the array. We keep on doing the same repeatedly
until we have the complete sorted list in our array.
Complexity Analysis of Heap Sort
Worst Case Time Complexity: O(n*log n)
Best Case Time Complexity: O(n*log n)
Average Time Complexity: O(n*log n)
Space Complexity : O(1)
 Heap sort is not a Stable sort, and requires a constant space for sorting a list.
 Heap Sort is very fast and is widely used for sorting.

import java.util.Scanner;

public class HeapSort{


private static int N;
public static void sort(int arr[]){
heapMethod(arr);
for (int i = N; i > 0; i--){
swap(arr,0, i);
N = N-1;
heap(arr, 0);
}
}
public static void heapMethod(int arr[]){
N = arr.length-1;
for (int i = N/2; i >= 0; i--)
heap(arr, i);
}
public static void heap(int arr[], int i){
int left = 2*i ;
int right = 2*i + 1;
int max = i;
if (left <= N && arr[left] > arr[i])
max = left;
if (right <= N && arr[right] > arr[max])
max = right;
if (max != i){
swap(arr, i, max);
heap(arr, max);
}
}
public static void swap(int arr[], int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
Scanner in = new Scanner( System.in );
int n;
System.out.println("Enter the number of elements to be sorted:");
n = in.nextInt();
int arr[] = new int[ n ];
System.out.println("Enter "+ n +" integer elements");
for (int i = 0; i < n; i++)
arr[i] = in.nextInt();
sort(arr);
System.out.println("After sorting ");
for (int i = 0; i < n; i++)
System.out.println(arr[i]+" ");
System.out.println();
}
}
Sample Output
Output is:
Enter the number of elements to be sorted:
6
Enter 6 integer elements
99
54
67
32
1
78
After sorting
1
32
54
67
78
99

Linear search or sequential search:

Linear search or sequential search is a method for finding a particular value in a list, that consists of checking every
one of its elements, one at a time and in sequence, until the desired one is found.

Linear search is the simplest search algorithm. For a list with n items, the best case is when the value is equal to the
first element of the list, in which case only one comparison is needed. The worst case is when the value is not in the
list (or occurs only once at the end of the list), in which case n comparisons are needed.
The worst case performance scenario for a linear search is that it has to loop through the entire collection, either
because the item is the last one, or because the item is not found. In other words, if you have N items in your
collection, the worst case scenario to find an item is N iterations. In Big O Notation it is O(N). The speed of search
grows linearly with the number of items within your collection.

Linear searches don't require the collection to be sorted.

Algorithm:

 Step 1: Traverse the array


 Step 2: Match the key element with array element
 Step 3: If key element is found, return the index position of the array element
 Step 4: If key element is not found, return -1

Let's see an example of linear search in java where we are going to search an element sequentially from an array.

public class LinearSearchExample


{
public static int linearSearch(int[] arr, int key)
{
for(int i=0;i<arr.length;i++)
{
if(arr[i] == key)
{
return i;
}
}
return -1;
}
public static void main(String a[]){
int[] a1= {10,20,30,50,70,90};
int key = 50;
System.out.println(key+" is found at index: "+linearSearch(a1, key));

Output:

50 is found at index: 3

Binary Search in Java

Binary search is used to search a key element from multiple elements. Binary search is faster than linear search.

In case of binary search, array elements must be in ascending order. If you have unsorted array, you can sort the array
using Arrays.sort(arr) method.

Binary Search Example in Java

Let's see an example of binary search in java.

class BinarySearchExample
{
public static void binarySearch(int arr[], int first, int last, int key)
{
int mid = (first + last)/2;
while( first <= last )
{
if ( arr[mid] < key )
{
first = mid + 1;
}
else if ( arr[mid] == key )
{
System.out.println("Element is found at index: " + mid);
break;
}
Else
{
last = mid - 1;
}
mid = (first + last)/2;
}
if ( first > last )
{
System.out.println("Element is not found!");
}
}
public static void main(String args[]){
int arr[] = {10,20,30,40,50};
int key = 30;
int last=arr.length-1;
binarySearch(arr,0,last,key);
} }
Output:
Element is found at index: 2

S-ar putea să vă placă și