DS NOTES
DS NOTES
In other words we can store and retrieve data easily depends on the user application
Integer, Floating-point number, Character constants, string constants, pointers etc, fall in
this category.
Linked List, Stack, Queue, Tree, Graph are example of non-primitive data structures.
The design of an efficient data structure must take operations to be performed on the data
structure.
Traversing
Searching
Inserting
Deleting
Sorting
Merging
Traversing- It is used to access each data item exactly once so that it can be processed.
Searching- It is used to find out the location of the data item if it exists in the given collection of
data items.
Inserting- It is used to add a new data item in the given collection of data items.
Deleting- It is used to delete an existing data item from the given collection of data items.
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.
Merging- It is used to combine the data items of two sorted files into single file in the sorted
form.
Abstract Data Types
The definition of ADT only mentions what operations are to be performed but not how
these operations will be implemented.
It does not specify how data will be organized in memory and what algorithms will be
used for implementing the operations.
It is called “abstract” because it gives an implementation independent view.
The process of providing only the essentials and hiding the details is known as
abstraction. For example, we have been using int, float, char data types only with the
knowledge with values that can take and operations that can be performed on them
without any idea of how these types are implemented. So a user only needs to know what
a data type can do but not how it will do it.
Stack ADT
All operations takes place at a single end that is top of the stack and following operations
can be performed:
push() – Insert an element at one end of the stack called top.
pop () – Remove and return the element at the top of the stack, if it is not empty.
Queue ADT
Operations takes place at both ends, insertion is done at end and deletion is done at front.
Following operations can be performed:
enqueue() – Insert an element at the end of the queue.
dequeue() – Remove and return the first element of queue, if the queue is not empty.
What is an algorithm?
An algorithm is a step by step procedure to solve a problem. In normal language, algorithm is
defined as a sequence of statements which are used to perform a task. In computer science, an
algorithm can be defined as follows...
Binary search
Quick sort
Merge sort
Dynamic Programming
Recursive algorithm for Fibonacci Series is an example of dynamic programming.
Greedy Method
A greedy algorithm works recursively creating a group of objects from the smallest possible
component parts. Recursion is a procedure to solve a problem in which the solution to a specific
problem is dependent on the solution of the smaller instance of that problem.
Backtracking
Can be defined as a general algorithmic technique that considers searching every possible
combination in order to solve a computational problem.
Recursive Algorithm
A recursive algorithm is an algorithm which calls itself with "smaller (or simpler)" input
values, and which obtains the result for the current input by applying simple operations to the
returned value for the smaller (or simpler) input.
In general, recursive computer programs require more memory and computation compared with
iterative algorithms, but they are simpler and for many cases a natural way of thinking about the
problem.
Factorial
Factorial is an important mathematical function. A recursive definition of factorial is as follows.
What is Search?
Search is a process of finding a value in a list of values. In other words, searching is the process
of locating given value position in a list of values.
#include<stdio.h>
#include<conio.h>
void main()
{
int i,j,a[100],key,n,flag=0;
clrscr();
printf("Number of elements in the array\n");
scanf("%d",&n);
printf("\n Enter %d elements\n",n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
printf("Element to be searched\n");
scanf("%d",&key);
for(i=0;i<n;i++)
{
if(key==a[i])
{
flag=1;
break;
}
}
if(flag==1)
printf("key value found at %d index",i);
else
printf("key value not found");
getch();
}
Binary Search Algorithm
What is Search?
Search is a process of finding a value in a list of values. In other words, searching is the process
of locating given value position in a list of values.
Binary search algorithm finds a given element in a list of elements with O(log n) time
complexity where n is total number of elements in the list. The binary search algorithm can be
used with only sorted list of elements. That means, binary search is used only with list of
elements that are already arraged in an order. The binary search can not be used for list of
elements arranged in random order. This search process starts comparing the search element with
the middle element in the list. If both are matched, then the result is "element found". Otherwise,
we check whether the search element is smaller or larger than the middle element in the list. If
the search element is smaller, then we repeat the same process for left sublist of the middle
element. If the search element is larger, then we repeat the same process for right sublist of the
middle element. We repeat this process until we find the search element in the list or until we left
with a sublist of only one element. And if that element also doesn't match with the search
element, then the result is " Element not found in the list".
Step 6 - If the search element is smaller than middle element, repeat steps 2, 3, 4 and 5
for the left sublist of the middle element.
Step 7 - If the search element is larger than middle element, repeat steps 2, 3, 4 and 5 for
the right sublist of the middle element.
Step 8 - Repeat the same process until we find the search element in the list or until
sublist contains only one element.
Step 9 - If that element also doesn't match with the search element, then display "Element
is not found in the list!!!" and terminate the function.
Example
Consider the following list of elements and the element to be searched...
Binary Search Program
//AIM: write a program to search the given element by using binary search
#include<stdio.h>
#include<conio.h>
void main()
{
int a[100],i,key,n,flag=0,high,mid,low=0;
clrscr();
printf("enter the size array:");
scanf("%d",&n);
printf("enter an element of array:");
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
printf("enter the key value:");
scanf("%d",&key);
high=n-1;
while(low<=high)
{
mid=(low+high)/2;
if(key==a[mid])
{
flag=1;
break;
}
else if(key<a[mid])
high=mid-1;
else if(key>a[mid])
low=mid+1;
}
if(flag==0)
printf("key value notfound:");
else
printf("key value found at %d index:",mid);
getch();
}
Bubble Sort
Bubble sort is a simple sorting algorithm. This sorting algorithm is comparison-based algorithm
in which each pair of adjacent elements is compared and the elements are swapped if they are
not in order. This algorithm is not suitable for large data sets as its average and worst case
complexity are of Ο(n2) where n is the number of items.
How Bubble Sort Works?
We take an unsorted array for our example. Bubble sort takes Ο(n2) time so we're keeping it
short and precise.
Bubble sort starts with very first two elements, comparing them to check which one is greater.
In this case, value 33 is greater than 14, so it is already in sorted locations. Next, we compare 33
with 27.
We find that 27 is smaller than 33 and these two values must be swapped.
Next we compare 33 and 35. We find that both are in already sorted positions.
Then we move to the next two values, 35 and 10.
We know then that 10 is smaller 35. Hence they are not sorted.
We swap these values. We find that we have reached the end of the array. After one iteration,
the array should look like this −
To be precise, we are now showing how an array should look like after each iteration. After the
second iteration, it should look like this −
Notice that after each iteration, at least one value moves at the end.
And when there's no swap required, bubble sorts learns that an array is completely sorted.
#include <stdio.h>
#include <conio.h>
void main()
{
int a[100], n, i, j, s;
clrscr();
printf("Enter number of elements\n");
scanf("%d", &n);
printf("Enter %d integers\n", n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i= 0 ; i < ( n - 1 ); i++)
{
for (j = 0 ; j < n - i- 1; j++)
{
if (a[j] > a[j+1])
{
s= a[j];
a[j] = a[j+1];
a[j+1] = s;
}
}
}
Insertion sort algorithm arranges a list of elements in a particular order. In insertion sort
algorithm, every iteration moves an element from unsorted portion to sorted portion until all the
elements are sorted in the list.
Step 1 - Assume that first element in the list is in sorted portion and all the remaining
elements are in unsorted portion.
Step 2: Take first element from the unsorted portion and insert that element into the
sorted portion in the order specified.
Step 3: Repeat the above process until all the elements from the unsorted portion are
moved into the sorted portion
EXAMPLE
Insertion sort program
#include<stdio.h>
void main()
{
int i,j,t,a[10],n,p=0;
clrscr();
printf("enter the range of array:");
scanf("%d",&n);
printf("enter elements into array:");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=1;i<n;i++)
{
t=a[i];
for(p=i;p>0 && a[p-1]>t;p--)
a[p]=a[p-1];
a[p]=t;
}
printf("the sorted order is:");
for(i=0;i<n;i++)
printf("\t%d",a[i]);
getch();
}
Quick Sort
Quick Sort is a Divide and Conquer algorithm. It picks an element as pivot and partitions the
given array around the picked pivot.
There are many different versions of quick Sort that pick pivot in different ways.
1. Always pick first element as pivot.
2. Always pick last element as pivot (implemented below)
3. Pick a random element as pivot.
4. Pick median as pivot.
The key process in quick Sort is partition().
Target of partitions is, given an array and an element x of array as pivot, put x at its correct
position in sorted array and put all smaller elements (smaller than x) before x, and put all greater
elements (greater than x) after x.
Illustration of partition() :
#include<stdio.h>
#include<conio.h>
void quicksort(int[],int,int);
void main()
{
int x[20],i,n;
clrscr();
printf("Enter size of list:");
scanf("%d",&n);
printf("Enter %d elements:",n);
for(i=0;i<n;i++)
scanf("%d",&x[i]);
quicksort(x,0,n);
printf("Sorted List:");
for(i=0;i<n;i++)
printf("%d\t",x[i]);
getch();
}
void quicksort(int x[20],int first,int last)
{
int pivot,i,j,t;
if(first<last)
{
pivot=first;
i=first;
j=last;
while(i<j)
{
while(x[i]<=x[pivot] && i<last)
i++;
while(x[j]>x[pivot])
j--;
if(i<j)
{
t=x[i];
x[i]=x[j];
x[j]=t;
}
}
t=x[pivot];
x[pivot]=x[j];
x[j]=t;
quicksort(x,first,j-1);
quicksort(x,j+1,last);
}}
Merge Sort
Like QuickSort, Merge Sort is a Divide and Conquer algorithm. It divides input array in two
halves, calls itself for the two halves and then merges the two sorted halves.
The merge() function is used for merging two halves.
The merge(arr, l, m, r) is key process that assumes that arr[l..m] and arr[m+1..r] are sorted and
merges the two sorted sub-arrays into one.
MergeSort(arr[], l, r)
If r > l
1. Find the middle point to divide the array into two halves:
middle m = (l+r)/2
2. Call mergeSort for first half:
Call mergeSort(arr, l, m)
3. Call mergeSort for second half:
Call mergeSort(arr, m+1, r)
4. Merge the two halves sorted in step 2 and 3:
Call merge(arr, l, m, r)
Example array {38, 27, 43, 3, 9, 82, 10}. If we take a closer look at the diagram, we can see that
the array is recursively divided in two halves till the size becomes 1. Once the size becomes 1,
the merge processes comes into action and starts merging arrays back till the complete array is
merged.
Merge Sort program:
#include <stdio.h>
#include <stdlib.h>
void merge(int a[],int,int,int);
void mergesort(int a[],int,int);
void main()
{
int a[20],i,n;
clrscr();
printf("Enter size of List:");
scanf("%d",&n);
printf("\nEnter %d elements: \n",n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
mergesort(a,0,n-1);
printf("Sorted List:");
for(i=0;i<n;i++)
printf("%d\t",a[i]);
getch();
}
void mergesort(int a[],int beg,int end)
{
int mid;
if(beg<end)
{
mid=(beg+end)/2;
mergesort(a, beg, mid);
mergesort(a, mid + 1, end);
merge(a,beg,mid,end);
}
}
void merge(int a[],int beg,int mid,int end)
{
int i=beg,j = mid+1,index=beg,temp[20],k;
while((i<=mid)&&(j<=end))
{
if(a[i]<a[j])
{
temp[index]=a[i];
i++;
}
else
{
temp[index]=a[j];
j++;
}
index++;
}
if(i>mid)
{
while(j<=end)
{
temp[index]=a[j];
j++;
index++;
}
}
else
{
while(i<=mid)
{
temp[index]=a[i];
i++;
index++;
}
}
for(k=beg;k<index;k++)
a[k]=temp[k];
}
Stack is a linear data structure in which the insertion and deletion operations are performed at
only one end. In a stack, adding and removing of elements are performed at single position
which is known as "top". That means, new element is added at top of the stack and an element is
removed from the top of the stack. In stack, the insertion and deletion operations are performed
In a stack, the insertion operation is performed using a function called "push" and deletion
operation is performed using a function called "pop".
In the figure, PUSH and POP operations are performed at top position in the stack. That means,
both the insertion and deletion operations are performed at one end (i.e., at Top)
Stack is a linear data structure in which the operations are performed based on LIFO
principle.
Stack can also be defined as
"A Collection of similar data items in which both insertion and deletion operations are
Example
If we want to create a stack by inserting 10,45,12,16,35 and 50. Then 10 becomes the bottom
most element and 50 is the top most element. The last inserted element 50 is at Top of the stack
Operations on a Stack
1. Using Array
When stack is implemented using array, that stack can organize only limited number of elements.
When stack is implemented using linked list, that stack can organize unlimited number of
elements.
A stack data structure can be implemented using one dimensional array. But stack implemented
using array stores only fixed number of data values. This implementation is very simple. Just
define a one dimensional array of specific size and insert or delete the values into that array by
using LIFO principle with the help of a variable called 'top'. Initially top is set to -1. Whenever
we want to insert a value into the stack, increment the top value by one and then insert.
Whenever we want to delete a value from the stack, then delete the top value and decrement the
Before implementing actual operations, first follow the below steps to create an empty stack.
Step1 - Include all the header files which are used in the program and define a
constant 'SIZE' with specific value.
Step 2 - Declare all the functions used in stack implementation.
Step 3 - Create a one dimensional array with fixed size (int stack[SIZE])
Step 4 - Define a integer variable 'top' and initialize with '-1'. (int top = -1)
Step 5 - In main method, display menu with list of operations and make suitable function
calls to perform operation selected by the user on the stack.
Push (value) - Inserting value into the stack
In a stack, push() is a function used to insert an element into the stack. In a stack, the new
element is always inserted at top position. Push function takes one integer value as parameter
and inserts that value into the stack. We can use the following steps to push an element on to the
stack...
In a stack, pop() is a function used to delete an element from the stack. In a stack, the element is
always deleted from top position. Pop function does not take any value as parameter. We can use
What is an Expression?
In any programming language, if we want to perform any calculation or to frame a condition etc.,
we use a set of symbols to perform the task. These set of symbols makes an expression.
Operands are the values on which the operators can perform the task. Here operand can be a
direct value or variable or address of memory location.
Expression Types
Based on the operator position, expressions are divided into THREE types. They are as follows...
1. Infix Expression
2. Postfix Expression
3. Prefix Expression
Infix Expression
In infix expression, operator is used in between the operands.
Example
Postfix Expression
In postfix expression, operator is used after operands. We can say that "Operator follows the
Operands".
Example
To convert Infix Expression into Postfix Expression using a stack data structure, We can use the
following steps...
1. Read all the symbols one by one from left to right in the given Infix Expression.
2. If the reading symbol is operand, then directly print it to the result (Output).
3. If the reading symbol is left parenthesis '(', then Push it on to the Stack.
4. If the reading symbol is right parenthesis ')', then Pop all the contents of stack until
respective left parenthesis is popped and print each popped symbol to the result.
5. If the reading symbol is operator (+, - , *, / etc.,), then Push it on to the Stack. However,
first pop the operators which are already on the stack that have higher or equal
Example
An infix expression is difficult for the machine to know and keep track of precedence of
operators. On the other hand, a postfix expression itself determines the precedence of operators
(as the placement of operators in a postfix expression depends upon its precedence).Therefore,
for the machine it is easier to carry out a postfix expression than an infix expression.
Postfix Expression Evaluation
A postfix expression is a collection of operators and operands in which the operator is placed
after the operands. That means, in a postfix expression the operator follows the operands.
Example
A postfix expression can be evaluated using the Stack data structure. To evaluate a postfix
expression using Stack data structure we can use the following steps...
1. Read all the symbols one by one from left to right in the given Postfix Expression
3. If the reading symbol is operator (+ , - , * , / etc.,), then perform TWO pop operations and
store the two popped oparands in two different variables (operand1 and operand2). Then
perform reading symbol operation using operand1 and operand2 and push result back on
to the Stack.
4. Finally! perform a pop operation and display the popped value as final result.
Queue
What is a Queue?
Queue is a linear data structure in which the insertion and deletion operations are performed at
two different ends. In a queue data structure, adding and removing of elements are performed at
two different positions. The insertion is performed at one end and deletion is performed at other
end. In a queue data structure, the insertion operation is performed at a position which is known
as 'rear' and the deletion operation is performed at a position which is known as 'front'. In queue
data structure, the insertion and deletion operations are performed based on FIFO (First In First
Out) principle.
In a queue data structure, the insertion operation is performed using a function called
Queue data structure is a linear data structure in which the operations are performed
"Queue data structure is a collection of similar data items in which insertion and deletion
Operations on a Queue
Queue data structure can be implemented in two ways. They are as follows...
1. Using Array
When a queue is implemented using array, that queue can organize only limited number of
elements. When a queue is implemented using linked list, that queue can organize unlimited
number of elements.
A queue data structure can be implemented using one dimensional array. The queue
implemented using array stores only fixed number of data values. The implementation of queue
data structure using array is very simple. Just define a one dimensional array of specific size and
insert or delete the values into that array by using FIFO (First In First Out) principlewith the
help of variables 'front' and 'rear'. Initially both 'front' and 'rear' are set to -1. Whenever, we
want to insert a new value into the queue, increment 'rear' value by one and then insert at that
position. Whenever we want to delete a value from the queue, then delete the element which is at
Before we implement actual operations, first follow the below steps to create an empty queue.
Step 1 - Include all the header files which are used in the program and define a
constant 'SIZE' with specific value.
Step 2 - Declare all the user defined functions which are used in queue implementation.
Step 3 - Create a one dimensional array with above defined SIZE (int queue[SIZE])
Step 4 - Define two integer variables 'front' and 'rear' and initialize both with '-1'. (int
front = -1, rear = -1)
Step 5 - Then implement main method by displaying menu of operations list and make
suitable function calls to perform operation selected by the user on queue.
In a queue data structure, enQueue() is a function used to insert a new element into the queue. In
a queue, the new element is always inserted at rear position. The enQueue() function takes one
integer value as parameter and inserts that value into the queue. We can use the following steps
In a queue data structure, deQueue() is a function used to delete an element from the queue. In a
queue, the element is always deleted from front position. The deQueue() function does not take
any value as parameter. We can use the following steps to delete an element from the queue...
Step 1 - Check whether queue is EMPTY. (front == rear)
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!! Deletion is not
possible!!!" and terminate the function.
Step 3 - If it is NOT EMPTY, then increment the front value by one (front ++). Then
display queue[front] as deleted element. Then check whether both front and rear are
equal (front == rear), if it TRUE, then set both front and rear to '-1'(front = rear = -1).
Now consider the following situation after deleting three elements from the queue...
This situation also says that Queue is Full and we cannot insert the new element because, 'rear'
is still at last position. In above situation, even though we have empty positions in the queue we
can not make use of them to insert new element. This is the major problem in normal queue data
Circular Queue is a linear data structure in which the operations are performed based on
FIFO (First In First Out) principle and the last position is connected back to the first
Priority Queue
Priority Queue is more specialized data structure than Queue. Like ordinary queue, priority
queue has same method but with a major difference. In Priority queue items are ordered by key
value so that item with the lowest value of key is at front and item with the highest value of key
is at rear or vice versa. So we're assigned priority to item based on its key value. Lower the
value, higher the priority. Following are the principal methods of a Priority Queue.
Applications of Queue Data Structure
Queue is used when things don’t have to be processed immediately, but have to be processed
in First In First Out order like Breadth First Search. This property of Queue makes it also useful
in following kind of scenarios.
1) When a resource is shared among multiple consumers. Examples include CPU scheduling,
Disk Scheduling.
2) When data is transferred asynchronously (data not necessarily received at same rate as sent)
between two processes. Examples include IO Buffers, pipes, file IO, etc.
See this for more detailed applications of Queue and Stack.
References:
UNIT -III
• The size of the array is fixed. Most often this size is specified at compile time. This makes the
programmers to allocate arrays, which seems "large enough" than required.
•Inserting new elements at the front is potentially expensive because existing elements need to be
shifted over to make room.
• Deleting an element from an array is not possible. Linked lists have their own strengths and
weaknesses, but they happen to be strong where arrays are weak. Generally array's allocates the
memory for all its elements in one block whereas linked lists use
An entirely different strategy. Linked lists allocate memory for each element separately and
only when necessary.
Linked lists have many advantages. Some of the very important advantages are:
1. Linked lists are dynamic data structures. i.e., they can grow or shrink during the execution of a
program.
2. Linked lists have efficient memory utilization. Here, memory is not preallocated. Memory is
allocated whenever it is required and it is de-allocated (removed) when it is no longer needed.
3. Insertion and Deletions are easier and efficient. Linked lists provide flexibility in inserting a
data item at a specified position and deletion of the data item from the given position.
4. Many complex applications can be easily carried out with linked lists.
1. It consumes more space because every node requires a additional pointer to store address of the next
node.
Basically we can put linked lists into the following four items:
When we want to work with unknown number of data values, we use a linked list data structure
to organize that data. Linked list is a linear data structure that contains sequence of elements such
that each element links to its next element in the sequence. Each element in a linked list is called
as "Node".
Simply a list is a sequence of data, and linked list is a sequence of data linked with each other.
Single linked list is a sequence of elements in which every element has link to its next
In any single linked list, the individual element is called as "Node". Every "Node" contains two
fields, data field and next field. The data field is used to store actual value of the node and next
Insertion
Deletion
Display
Before we implement actual operations, first we need to setup empty list. First perform the
following steps before implementing actual operations.
Step 1 - Include all the header files which are used in the program.
Step 2 - Declare all the user defined functions.
Step 3 - Define a Node structure with two members data and next
Step 4 - Define a Node pointer 'start' and set it to NULL.
Step 5 - Implement the main method by displaying operations menu and make suitable
function calls in the main method to perform user selected operation.
Insertion
In a single linked list, the insertion operation can be performed in three ways. They are as
follows...
Step 1 - Create a newNode with given value and newNode → next as NULL.
Step 2 - Check whether list is Empty (start = = NULL).
Step 3 - If it is Empty then, set start = newNode.
Step 4 - If it is Not Empty then, define a node pointer temp and initialize with start.
Step 5 - Keep moving the temp to its next node until it reaches to the last node in the list
(until temp → next is not equal to NULL).
Step 6 - Set temp → next = newNode.
Inserting at middle of the list
Step 3 - If it is Empty then, set newNode → next = NULL and start = newNode.
Step 4 - If it is Not Empty then, define a node pointer temp and initialize with start.
Step 5 - Keep moving the temp to its next node until it reaches to the node after which we
want to insert the newNode
Deletion
In a single linked list, the deletion operation can be performed in three ways. They are as follows...
Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the
function.
Step 3 - If it is Not Empty then, define a Node pointer 'temp' and initialize with start.
Step 4 - Check whether list is having only one node (temp → next == NULL)
Step 5 - If it is TRUE then set start= NULL and delete temp (Setting Empty list conditions)
Step 6 - If it is FALSE then set start= temp → next, and delete temp(free(temp)).
Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.
Step 3 - If it is Not Empty then, define two Node pointers 'temp' and 'temp1' and initialize 'temp'
with start.
Step 4 - Check whether list has only one Node (temp → next == NULL)
Step 5 - If it is TRUE. Then, set start = NULL and delete temp. And terminate the function.
(Setting Empty list condition)
Step 6 - If it is FALSE. Then, set 'temp1 = temp ' and move temp to its next node. Repeat the same until
it reaches to the last node in the list. (until temp1 → next == NULL)
Step 3 - If it is Empty then, set newNode → next = NULL and start = newNode.
Step 4 - If it is Not Empty then, define a node pointer temp and initialize with start.
Step 5 - Keep moving the temp to its next node until it reaches to the node after which we
want to delete the Node
Traversing
Polynomials and Sparse Matrix are two important applications of arrays and linked lists.
A polynomial is composed of different terms where each of them holds a coefficient and an
exponent.
Polynomial Representation:
Sparse Matrix Representation
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
In a single linked list, every node has link to its next node in the sequence. So, we can traverse
from one node to other node only in one direction and we cannot traverse back. We can solve
this kind of problem by using double linked list. Double linked list can be defined as follows...
Double linked list is a sequence of elements in which every element has links to its previous
In double linked list, every node has link to its previous node and next node. So, we can traverse
forward by using next field and can traverse backward by using previous field. Every node in a
double linked list contains three fields and they are shown in the following figure...
Here, 'link1' field is used to store the address of the previous node in the sequence, 'link2' field
is used to store the address of the next node in the sequence and 'data' field is used to store the
Example
1. Insertion
2. Deletion
3. Display
Insertion
In a double linked list, the insertion operation can be performed in three ways as follows...
Step 1 - Create a newNode with given value and newNode → previous as NULL.
Step 2 - Check whether list is Empty (start = = NULL)
Step 3 - If it is Empty then, assign NULL to newNode → next and newNode to start.
Step 4 - If it is not Empty then, assign start to newNode → next and newNode to start.
Inserting At End of the list
We can use the following steps to insert a new node at end of the double linked list...
Step 1 - Create a newNode with given value and newNode → next as NULL.
Step 2 - Check whether list is Empty (start = = NULL)
Step3- If it is Empty, then assign NULL to newNode→previous and newNode to head.
Step 4 - If it is not Empty, then, define a node pointer temp and initialize with head.
Step 5 - Keep moving the temp to its next node until it reaches to the last node in the list
(until temp → next is equal to NULL).
Step 6 - Assign newNode to temp → next and temp to newNode → previous.
Inserting At Specific location in the list (After a Node)
We can use the following steps to insert a new node after a node in the double linked list...
Example
Terminology
In a tree data structure, we use the following terminology...
1. Root
In a tree data structure, the first node is called as Root Node. Every tree
must have root node. We can say that root node is the origin of tree data
structure. In any tree, there must be only one root node. We never have
multiple root nodes in a tree.
2. Edge
In a tree data structure, the connecting link between any two nodes is
called as EDGE. In a tree with 'N' number of nodes there will be a maximum
of 'N-1' number of edges.
3. Parent
In a tree data structure, the node which is predecessor of any node is called
as PARENT NODE. In simple words, the node which has branch from it to any
other node is called as parent node. Parent node can also be defined as
"The node which has child / children".
4. Child
In a tree data structure, the node which is descendant of any node is called
as CHILD Node. In simple words, the node which has a link from its parent
node is called as child node. In a tree, any parent node can have any
number of child nodes. In a tree, all the nodes except root are child nodes.
5. Siblings
In a tree data structure, nodes which belong to same Parent are called
as SIBLINGS. In simple words, the nodes with same parent are called as
Sibling nodes.
6. Leaf
In a tree data structure, the node which does not have a child is called
as LEAF Node. In simple words, a leaf is a node with no child.
In a tree data structure, the leaf nodes are also called as External Nodes.
External node is also a node with no child. In a tree, leaf node is also called
as 'Terminal' node.
7. Internal Nodes
In a tree data structure, the node which has atleast one child is called
as INTERNAL Node. In simple words, an internal node is a node with atleast
one child.
In a tree data structure, nodes other than leaf nodes are called as Internal
Nodes. The root node is also said to be Internal Node if the tree has more
than one node. Internal nodes are also called as 'Non-Terminal' nodes.
8. Degree
In a tree data structure, the total number of children of a node is called
as DEGREE of that Node. In simple words, the Degree of a node is total
number of children it has. The highest degree of a node among all the nodes
in a tree is called as 'Degree of Tree'
9. Level
In a tree data structure, the root node is said to be at Level 0 and the
children of root node are at Level 1 and the children of the nodes which are
at Level 1 will be at Level 2 and so on... In simple words, in a tree each step
from top to bottom is called as a Level and the Level count starts with '0'
and incremented by one at each level (Step).
10. Height
In a tree data structure, the total number of egdes from leaf node to a
particular node in the longest path is called as HEIGHT of that Node. In a
tree, height of the root node is said to be height of the tree. In a
tree, height of all leaf nodes is '0'.
11. Depth
In a tree data structure, the total number of egdes from root node to a
particular node is called as DEPTH of that Node. In a tree, the total number
of edges from root node to a leaf node in the longest path is said to
be Depth of the tree. In simple words, the highest depth of any leaf node in
a tree is said to be depth of that tree. In a tree, depth of the root node is
'0'.
12. Path
In a tree data structure, the sequence of Nodes and Edges from one node to
another node is called as PATH between that two Nodes. Length of a
Path is total number of nodes in that path. In below example the path A - B
- E - J has length 4.
In a normal tree, every node can have any number of children. Binary tree is
a special type of tree data structure in which every node can have
a maximum of 2 children. One is known as left child and the other is known
as right child.
Example
A binary tree in which every node has either two or zero number of
children is called Strictly Binary Tree
Strictly binary tree is also called as Full Binary Tree or Proper Binary
Tree or 2-Tree
Example
The full binary tree obtained by adding dummy nodes to a binary tree is
called as Extended Binary Tree.
In above figure, a normal binary tree is converted into full binary tree by
adding dummy nodes .
Binary Tree Representations
1. Array Representation
2. Linked List Representation
1. Array Representation
In array representation of binary tree, we use a one dimensional array (1-D
Array)to representation binarytree.
Consider the above example of binary tree and it is represented as follows...
1. In - Order Traversal
2. Pre - Order Traversal
3. Post - Order Traversal
In the above example of binary tree, first we try to visit left child of root
node 'A', but A's left child is a root node for left subtree. so we try to visit its
(B's) left child 'D' and again D is a root for subtree with nodes D, I and J. So
we try to visit its left child 'I' and it is the left most child. So first we
visit 'I' then go for its root node 'D' and later we visit D's right child 'J'. With
this we have completed the left part of node B. Then visit 'B' and next B's
right child 'F' is visited. With this we have completed left part of node A.
Then visit root node 'A'. With this we have completed left and root parts of
node A. Then we go for right part of the node A. In right of A again there is
a subtree with root C. So go for left child of C and again it is a subtree with
root G. But G does not have left part so we visit 'G' and then visit G's right
child K. With this we have completed the left part of node C. Then visit root
node 'C' and next visit C's right child 'H' which is the right most child in the
tree so we stop the process.
I-D-J-B-F-A-G-K-C-H
2. Pre - Order Traversal ( root - leftChild - rightChild )
In Pre-Order traversal, the root node is visited before left child and right
child nodes. In this traversal, the root node is visited first, then its left child
and later its right child. This pre-order traversal is applicable for every root
node of all subtrees in the tree.
In the above example of binary tree, first we visit root node 'A' then visit its
left child 'B' which is a root for D and F. So we visit B's left child 'D' and
again D is a root for I and J. So we visit D's left child 'I'which is the left most
child. So next we go for visiting D's right child 'J'. With this we have
completed root, left and right parts of node D and root, left parts of node B.
Next visit B's right child 'F'. With this we have completed root and left parts
of node A. So we go for A's right child 'C' which is a root node for G and H.
After visiting C, we go for its left child 'G' which is a root for node K. So next
we visit left of G, but it does not have left child so we go for G's right
child 'K'. With this we have completed node C's root and left parts. Next
visit C's right child 'H' which is the right most child in the tree. So we stop
the process.
That means here we have visited in the order of A-B-D-I-J-F-C-G-K-H using
Pre-Order Traversal.
A-B-D-I-J-F-C-G-K-H
2. Post - Order Traversal ( leftChild - rightChild - root )
In Post-Order traversal, the root node is visited after left child and right
child. In this traversal, left child node is visited first, then its right child and
then its root node. This is recursively performed until the right most node is
visited.
I-J-D-F-B-K-G-H-C–A
Networking
o Router algorithms -Network Routing, where the next path/route of the packet is
determined
o Social networking is the current buzzword in CS research. It goes without saying that
connections/relations are very naturally modeled using graphs. Often, trees are used to
represent/identify more interesting phenomena.
Representation
o Chemical formulas representation
o XML/Markup parsers use trees
o Producers/consumers often use a balanced tree implementation to store a document in
memory.
Workflow
o As a workflow for compositing digital images for visual effects.
Organizing Things
o Folders/ files in the Operating system
o HTML Document Object Model (DOM)
o Company Organisation Structures
o PDF is a tree-based format. It has a root node followed by a catalog node followed by a
pages node which has several child page nodes.
Faster Lookup
o Auto-correct applications and spell checker
o Syntax Tree in Compiler
Task Tracker
o Undo function in a text editor
Binary Search Tree
In a binary tree, every node can have maximum of two children but there is
no order of nodes based on their values. In binary tree, the elements are
arranged as they arrive to the tree, from top to bottom and left to right.
Binary Search Tree is a binary tree in which every node contains only
smaller values in its left subtree and only larger values in its right
subtree.
In a binary search tree, all the nodes in left subtree of any node contains
smaller values and all the nodes in right subtree of that contains larger
values as shown in following figure...
Example
The following tree is a Binary Search Tree. In this tree, left subtree of every
node contains nodes with smaller values and right subtree of every node
contains larger values.
NOTE: Every Binary Search Tree is a binary tree but all the Binary Trees
need not to be binary search trees.perations on a Binary
The following operations are performed on a binary Search tree...
1. Search
2. Insertion
3. Deletion
Example
Construct a Binary Search Tree by inserting the following sequence of
numbers...
10,12,5,4,20,8,7,15 and 13
Above elements are inserted into a Binary Search Tree as follows...
AVL Tree
AVL tree is a self balanced binary search tree. That means, an AVL tree is
also a binary search tree but it is a balanced tree. A binary tree is said to be
balanced, if the difference between the hieghts of left and right subtrees of
every node in the tree is either -1, 0 or +1. In other words, a binary tree is
said to be balanced if for every node, height of its children differ by at most
one. In an AVL tree, every node maintains a extra information known
as balance factor. The AVL tree was introduced in the year of 1962 by G.M.
Adelson-Velsky and E.M. Landis.
Balance factor of a node is the difference between the heights of left and
right subtrees of that node. The balance factor of a node is calculated
either height of left subtree - height of right subtree (OR) height of right
subtree - height of left subtree. In the following explanation, we are
calculating as follows...
Example
The above tree is a binary search tree and every node is satisfying balance
factor condition. So this tree is said to be an AVL tree.
Every AVL Tree is a binary search tree but all the Binary Search Trees
need not to be AVL trees.
There are four rotations and they are classified into two types.
1. Search
2. Insertion
3. Deletion
Step 1: Insert the new element into the tree using Binary Search Tree
insertion logic.
Step 2: After insertion, check the Balance Factor of every node.
Step 3: If the Balance Factor of every node is 0 or 1 or -1 then go for
next operation.
Step 4: If the Balance Factor of any node is other than 0 or 1 or -
1 then tree is said to be imbalanced. Then perform the
suitable Rotation to make it balanced. And go for next operation.
Example: Construct an AVL Tree by inserting numbers from 1 to 8.
Deletion Operation in AVL Tree
In an AVL Tree, the deletion operation is similar to deletion operation in
BST. But after every deletion operation we need to check with the Balance
Factor condition. If the tree is balanced after deletion then go for next
operation otherwise perform the suitable rotation to make the tree
Balanced.
B - Trees
In a binary search tree, AVL Tree, Red-Black tree etc., every node can have
only one value (key) and maximum of two children but there is another type
of search tree called B-Tree in which a node can store more than one value
(key) and it can have more than two children. B-Tree was developed in the
year of 1972 by Bayer and McCreight with the name Height Balanced m-
way Search Tree. Later it was named as B-Tree.
B-Tree can be defined as follows...
Here, number of keys in a node and number of children for a node is depend
on the order of the B-Tree. Every B-Tree has order.
B-Tree of Order m has the following properties...
Example
Opns on a B-Tree
The following operations are performed on a B-Tree...
1. Search
2. Insertion
3. Deletion
Example
Construct a B-Tree of Order 3 by inserting numbers from 1 to 10.
UNIT-V
Introduction to Graphs
Example
The following is a graph with 5 vertices and 6 edges.
This graph G can be defined as G = ( V , E )
Where V = {A,B,C,D,E} and E = {(A,B),(A,C)(A,D),(B,D),(C,D),(B,E),(E,D)}.
ph
Terminology
We use the following terms in graph data structure...
Vertex
A individual data element of a graph is called as Vertex. Vertex is also
known as node. In above example graph, A, B, C, D & E are known as
vertices.
Edge
An edge is a connecting link between two vertices. Edge is also known
as Arc. An edge is represented as (startingVertex, endingVertex). For
example, in above graph, the link between vertices A and B is represented
as (A,B). In above example graph, there are 7 edges (i.e., (A,B), (A,C),
(A,D), (B,D), (B,E), (C,D), (D,E)).
Undirected Graph
A graph with only undirected edges is said to be undirected graph.
Directed Graph
A graph with only directed edges is said to be directed graph.
Mixed Graph
A graph with undirected and directed edges is said to be mixed graph.
Destination
If an edge is directed, its first endpoint is said to be origin of it and the
other endpoint is said to be the destination of the edge.
Adjacent
If there is an edge between vertices A and B then both A and B are said to
be adjacent. In other words, Two vertices A and B are said to be adjacent if
there is an edge whose end vertices are A and B.
Incident
An edge is said to be incident on a vertex if the vertex is one of the
endpoints of that edge.
Outgoing Edge
A directed edge is said to be outgoing edge on its orign vertex.
Incoming Edge
A directed edge is said to be incoming edge on its destination vertex.
Degree
Total number of edges connected to a vertex is said to be degree of that
vertex.
Indegree
Total number of incoming edges connected to a vertex is said to be indegree
of that vertex.
Outdegree
Total number of outgoing edges connected to a vertex is said to be
outdegree of that vertex.
Self-loop
An edge (undirected or directed) is a self-loop if its two endpoints coincide.
Simple Graph
A graph is said to be simple if there are no parallel and self-loop edges.
Path
A path is a sequence of alternating vertices and edges that starts at a vertex
and ends at a vertex such that each edge is incident to its predecessor and
successor vertex.
Graph Representations
1. Adjacency Matrix
2. Adjacency List
Adjacency Matrix
In this representation, graph can be represented using a matrix of size total
number of vertices by total number of vertices. That means if a graph with 4
vertices can be represented using a matrix of 4X4 class. In this matrix, rows
and columns both represents vertices. This matrix is filled with either 1 or
0. Here, 1 represents there is an edge from row vertex to column vertex and
0 represents there is no edge from row vertex to column vertex.
Back tracking is coming back to the vertex from which we came to current
vertex
Graph Traversals - BFS
Graph traversal is technique used for searching a vertex in a graph. The
graph traversal is also used to decide the order of vertices to be visit in the
search process. A graph traversal finds the egdes to be used in the search
process without creating loops that means using graph traversal we visit all
verticces of graph without getting into looping path.
There are two graph traversal techniques and they are as follows...
Since they are powerful abstractions, graphs can be very important in modeling data. In fact,
many problems can be reduced to known graph problems. Here we outline just some of the many
applications of graphs.
1. Social network graphs: to tweet or not to tweet. Graphs that represent who knows whom, who
communicates with whom, who influences whom or other relationships in social structures. An
example is the twitter graph of who follows whom. These can be used to determine how
information flows, how topics become hot, how communities develop, or even who might be a
good match for who, or is that whom.
2. Transportation networks. In road networks vertices are intersections and edges are the road
segments between them, and for public transportation networks vertices are stops and edges are
the links between them. Such networks are used by many map programs such as Google maps,
Bing maps and now Apple IOS 6 maps (well perhaps without the public transport) to find the
best routes between locations. They are also used for studying traffic patterns, traffic light
timings, and many aspects of transportation.
3. Document link graphs. The best known example is the link graph of the web, where each web
page is a vertex, and each hyperlink a directed edge. Link graphs are used, for example, to
analyze relevance of web pages, the best sources of information, and good link sites.
4. Scene graphs. In graphics and computer games scene graphs represent the logical or spacial
relationships between objects in a scene. Such graphs are very important in the computer games
industry.
5. Robot planning. Vertices represent states the robot can be in and the edges the possible
transitions between the states. This requires approximating continuous motion as a sequence of
discrete steps. Such graph plans are used, for example, in planning paths for autonomous
vehicles.
6. Neural networks. Vertices represent neurons and edges the synapses between them. Neural
networks are used to understand how our brain works and how connections change when we
learn. The human brain has about 1011 neurons and close to 1015 synapses.
7. Graphs in compilers. Graphs are used extensively in compilers. They can be used for type
inference, for so called data flow analysis, register allocation and many other purposes. They are
also used in specialized compilers, such as query optimization in database languages.
8.Computer hardware: Compilers uses graph coloring algorithms for Register allocation to
variables , Calculate parallelism degree, Very useful in analytical modeling[3] , Addressing the
sequence of instruction execution , Resource allocation and Economizing the memory space(file
organization).
Hashing
In all search techniques like linear search, binary search and search trees,
the time required to search an element is depends on the total number of
element in that data structure. In all these search techniquies, as the
number of element are increased the time required to search an element
also increased linearly.
Here, hash key is a value which provides the index value where tha actual
data is likely to store in the datastructure.
In this datastructure, we use a concept called Hash table to store data. All
the data values are inserted into the hash table based on the hash key
value. Hash key value is used to map the data with index in the hash table.
And the hash key is generated for every data using a hash function. That
means every entry in the hash table is based on the key value generated
using a hash function.
Hash table is just an array which maps a key (data) into the
datastructure with the help of hash function such that insertion,
deletion and search operations can be performed with constant time
complexity (i.e. O(1)).
Hash tables are used to perform the operations like insertion, deletion and
search very quickly in a datastructure. Using hash table concept insertion,
deletion and search operations are accoplished in constant time. Generally,
every hash table make use of a function, which we'll call the hash
function to map the data into the hash table.
Basic concept of hashing and hash table is shown in the following figure...
1.Division Method:
h(X)=x mod M
h(5642)= 5642 % 97 = 16
Index value
0
Keys
1234 % 97 = 70
1234
5642 % 97 = 16
5642 16 5642
70 1234
100
In the mid-square method, the same r digits must be chosen from all the
keys. Therefore, the hash function can be given as
observe that the 3rd and 4th digit starting from the right are chosen.
3) Folding Method:
Step1: Divide the key value into a number of parts. (i.e.) k divided into
parts k1,k2,k3,…kn where each part has the same number of digits, except
the last part which may have lesser digits than the other parts.
Step2: Add the individual parts. i.e. obtain the sum of k1+k2+k3…+kn. The
has value is produced by ignoring the last carry if any.
Example: Given a hash table of 100 locations, calculate the hash value
using folding method for keys 5678,321, and 34567.
Solution:
Collisions occur when the hash function maps two different key to the same
location. Obviously, two records cannot be stored in the same location.
1. Open addressing
2. Chaining
i) Linear probing
ii) Quadratic probing
iii) Double hashing
iv) Rehashing
Linear probing: In this, we linearly probe for next slot. The following
hash function is used to resolve the collision.
ex: I = 1,2,3….
Example: consider has functions as key mod 7 and sequence of keys are
50,700,76,85,92,73,101.
We look for i2 th slot in the hash table. The following hash function is used
to resolve the collection.
h(k,i)=(h,1k)+i2) mod m.
i=1,2,3 …..
iii) Double hashing: We use another hash function has2(x) and look for
i*hash2(x). slot in the rotation. The following hash function is used to
resolve the collision.
h(k,i)=h(k)+i*hash2(x) mod m.
Where I =1,2,3…
Iv)Rehashing: When the hash table becomes nearly full, the number of
collisions increases, thereby degrading the performance of insertion and
search operations. In such cases, a better option is to create a new hash
table with size double of the original hash table.
All the entries in the original hash table will then have to be moved to the
new hash table. This is done by taking each entry, computing its new hash
table. This is done by taking each entry, computing its new hash value, and
then inserting it in the new hash table.
The following Fig. show how the key values are mapped to a location in the
hash table and stored in a linked list that corresponds to that location.
0 18 36 54
1 11
2 NULL
3 NULL
4 NULL
5 NULL
6 24
7 7 52