Download as pdf or txt
Download as pdf or txt
You are on page 1of 77

DATA STRUCTURE

INTRODUCTION

Data Structure can be defined as the group of data elements which


provides an efficient way of storing and organising data in the computer so that
it can be used efficiently. Some examples of Data Structures are arrays, Linked
List, Stack, Queue, etc. Data Structures are widely used in almost every aspect
of Computer Science i.e. Operating System, Compiler Design, Artifical
intelligence, Graphics and many more.
Data Structures are the main part of many computer science algorithms as
they enable the programmers to handle the data in an efficient way. It plays a
vitle role in enhancing the performance of a software or a program as the main
function of the software is to store and retrieve the user's data as fast as possible

BasicTerminology

Data structures are the building blocks of any program or the software.
Choosing the appropriate data structure for a program is the most difficult task
for a programmer. Following terminology is used as far as data structures are
concerned

Data: Data can be defined as an elementary value or the collection of values,


for example, student's name and its id are the data about the student.
Group Items: Data items which have subordinate data items are called Group
item, for example, name of a student can have first name and the last name.
Record: Record can be defined as the collection of various data items, for
example, if we talk about the student entity, then its name, address, course and
marks can be grouped together to form the record for the student.
File: A File is a collection of various records of one type of entity, for example,
if there are 60 employees in the class, then there will be 20 records in the related
file where each record contains the data about each employee.
Attribute and Entity: An entity represents the class of certain objects. it
contains various attributes. Each attribute represents the particular property of
that entity.
Field: Field is a single elementary unit of information representing the attribute
of an entity.

Need ofDataStructures

As applications are getting complexed and amount of data is increasing


day by day, there may arrise the following problems:

 Processor speed: To handle very large amout of data, high speed


processing is required, but as the data is growing day by day to the
billions of files per entity, processor may fail to deal with that much
amount of data.
 Data Search: Consider an inventory size of 106 items in a store, If our
application needs to search for a particular item, it needs to traverse 106
items every time, results in slowing down the search process.
 Multiple requests: If thousands of users are searching the data
simultaneously on a web server, then there are the chances that a very
large server can be failed during that process in order to solve the above
problems, data structures are used. Data is organized to form a data
structure in such a way that all items are not required to be searched and
required data can be searched instantly.

Advantages ofDataStructures

 Efficiency: Efficiency of a program depends upon the choice of data


structures. For example: suppose, we have some data and we need to
perform the search for a perticular record. In that case, if we organize
our data in an array, we will have to search sequentially element by
element. hence, using array may not be very efficient here. There are
better data structures which can make the search process efficient like
ordered array, binary search tree or hash tables.
 Reusability: Data structures are reusable, i.e. once we have
implemented a particular data structure, we can use it at any other
place. Implementation of data structures can be compiled into libraries
which can be used by different clients.
 Abstraction: Data structure is specified by the ADT which provides a
level of abstraction. The client program uses the data structure through
interface only, without getting into the implementation details.

DataStructureClassification

LinearDataStructures

A data structure is called linear if all of its elements are arranged in the
linear order. In linear data structures, the elements are stored in non-hierarchical
way where each element has the successors and predecessors except the first
and last element.

Types of Linear Data Structures are given below:


Arrays: An array is a collection of similar type of data items and each data item
is called an element of the array. The data type of the element may be any valid
data type like char, int, float or double.
The elements of array share the same variable name but each one carries a
different index number known as subscript. The array can be one dimensional,
two dimensional or multidimensional.
The individual elements of the array age are:
age[0], age[1], age[2], age[3],......... age[98], age[99].

Linked List: Linked list is a linear data structure which is used to maintain a
list in the memory. It can be seen as the collection of nodes stored at non-
contiguous memory locations. Each node of the list contains a pointer to its
adjacent node.

Stack: Stack is a linear list in which insertion and deletions are allowed only at
one end, called top. A stack is an abstract data type (ADT), can be implemented
in most of the programming languages. It is named as stack because it behaves
like a real-world stack, for example: - piles of plates or deck of cards etc.

Queue: Queue is a linear list in which elements can be inserted only at one end
called rearand deleted only at the other end called front. It is an abstract data
structure, similar to stack. Queue is opened at both end therefore it follows
First-In-First-Out (FIFO) methodology for storing the data items.

Non LinearDataStructures

This data structure does not form a sequence i.e. each item or element is
connected with two or more other items in a non-linear arrangement. The data
elements are not arranged in sequential structure.

Types of Non Linear Data Structures are given below:


Trees: Trees are multilevel data structures with a hierarchical relationship
among its elements known as nodes. The bottommost nodes in the herierchy are
called leaf nodewhile the topmost node is called root node. Each node
contains pointers to point adjacent nodes.

Tree data structure is based on the parent-child relationship among the


nodes. Each node in the tree can have more than one children except the leaf
nodes whereas each node can have atmost one parent except the root node.

Graphs: Graphs can be defined as the pictorial representation of the set of


elements (represented by vertices) connected by the links known as edges. A
graph is different from tree in the sense that a graph can have cycle while the
tree can not have the one.

Operations on datastructure

 Traversing: Every data structure contains the set of data elements.


Traversing the data structure means visiting each element of the data
structure in order to perform some specific operation like searching or
sorting.

Example: If we need to calculate the average of the marks obtained by


a student in 6 different subject, we need to traverse the complete array
of marks and calculate the total sum, then we will devide that sum by
the number of subjects i.e. 6, in order to find the average.

 Insertion: Insertion can be defined as the process of adding the


elements to the data structure at any location.

If the size of data structure is n then we can only insert n-1 data
elements into it.
 Deletion: The process of removing an element from the data structure
is called Deletion. We can delete an element from the data structure at
any random location.

If we try to delete an element from an empty data structure


then underflowoccurs.

 Searching: The process of finding the location of an element within


the data structure is called Searching. There are two algorithms to
perform searching, Linear Search and Binary Search.
 Sorting: The process of arranging the data structure in a specific order
is known as Sorting. There are many algorithms that can be used to
perform sorting, for example, insertion sort, selection sort, bubble sort,
etc.
 Merging: When two lists List A and List B of size M and N
respectively, of similar type of elements, clubbed or joined to produce
the third list, List C of size (M+N), then this process is called merging

LINEAR SEARCH
Linear search is a very simple search algorithm. In this type of search, a
sequential search is made over all items one by one. Every item is checked and
if a match is found then that particular item is returned, otherwise the search
continues till the end of the data collection.
Algorithm
Linear Search ( Array A, Value x)
Step 1: Set i to 1
Step 2: if i > n then go to step 7
Step 3: if A[i] = x then go to step 6
Step 4: Set i to i + 1
Step 5: Go to Step 2
Step 6: Print Element x Found at index i and go to step 8
Step 7: Print element not found
Step 8: Exit

This algorithm has the benefit of simplicity; it is difficult to get wrong,


unlike other more sophisticated solutions. The above code follows the
convention of this article, they are as follows:
1. All search routines return a true/false boolean value for success or
failure.
2. The list will be either an array of integers or a linked list of integers
with a key
3. The found item will be saved in a reference to a pointer for use in client
code.
The algorithm itself is simple. A familiar 0 - n-1 loop to walk over every
item in the array, with a test to see if the current item in the list matches the
search key. The loop can terminate in one of two ways. If i reaches the end of
the list, the loop condition fails. If the current item in the list matches the key,
the loop is terminated early with a break statement. Then the algorithm tests the
index variable to see if it is less that size (thus the loop was terminated early
and the item was found), or not (and the item was not found).

Pseudocode
for(i = 0; i < size; i++)
{
if(num = = list[i])
{ printf("Element is found at %d index", i); break;
}
}
if(i == size)
printf("Given element is not found in the list!!!");

LinearSearchProgram
#include<stdio.h>
#include<conio.h>
int main()
{
int array[100], search, c, n;
printf("Enter the number of elements in array\n");
scanf("%d",&n);

printf("Enter %d integer(s)\n", n);


for (c = 0; c < n; c++)
scanf("%d", &array[c]);

printf("Enter the number to search\n");


scanf("%d", &search);
for (c = 0; c < n; c++)
{
if (array[c] == search) /* if required element found */
{
printf("%d is present at location %d.\n", search, c+1);
break;
}
}
if (c == n)
printf("%d is not present in array.\n", search);
return 0;
}

Linearsearchformultipleoccurrences

In the code below we will print all the locations at which required
element is found and also the number of times it occur in the list.
#include<stdio.h>
#include<conio.h>
int main()
{
int array[100], search, c, n, count = 0;
printf("Enter the number of elements in array\n");
scanf("%d", &n);

printf("Enter %d numbers\n", n);

for ( c = 0 ; c < n ; c++ )


scanf("%d", &array[c]);

printf("Enter the number to search\n");


scanf("%d", &search);

for (c = 0; c < n; c++) {


if (array[c] == search) {
printf("%d is present at location %d.\n", search, c+1);
count++;
}
}
if (count == 0)
printf("%d is not present in array.\n", search);
else
printf("%d is present %d times in array.\n", search, count);

return 0;
}

BINARY SEARCH
Binary search is a fast search algorithm with run-time complexity of
Ο(log n). This search algorithm works on the principle of divide and conquer.
For this algorithm to work properly, the data collection should be in the sorted
form.
Binary search looks for a particular item by comparing the middle most
item of the collection. If a match occurs, then the index of item is returned. If
the middle item is greater than the item, then the item is searched in the sub-
array to the left of the middle item. Otherwise, the item is searched for in the
sub-array to the right of the middle item. This process continues on the sub-
array as well until the size of the sub array reduces to zero.
How Binary Search Works?
For a binary search to work, it is mandatory for the target array to be
sorted. We shall learn the process of binary search with a pictorial example.
The following is our sorted array and let us assume that we need to search the
location of value 31 using binary search.

First, we shall determine half of the array by using this formula −

mid = low + (high - low) / 2

Here it is, 0 + (9 - 0 ) / 2 = 4 (integer value of 4.5). So, 4 is the mid of the array.

Now we compare the value stored at location 4, with the value being
searched, i.e. 31. We find that the value at location 4 is 27, which is not a
match. As the value is greater than 27 and we have a sorted array, so we also
know that the target value must be in the upper portion of the array.

We change our low to mid + 1 and find the new mid value again.

low = mid + 1
mid = low + (high - low) / 2
Our new mid is 7 now. We compare the value stored at location 7 with our
target value 31.

The value stored at location 7 is not a match, rather it is less than what
we are looking for. So, the value must be in the lower part from this location.

Hence, we calculate the mid again. This time it is 5.

We compare the value stored at location 5 with our target value. We find that it
is a match.

We conclude that the target value 31 is stored at location 5.


Binary search halves the searchable items and thus reduces the count of
comparisons to be made to very less numbers.

Pseudocode
The pseudocode of binary search algorithms should look like this −left = 0;
right = size - 1;
while (left <= right)
{
int middle = (left + right) / 2;
if (arr[middle] == value)
printf(“The element is found at position %d”, middle);
else if (arr[middle] > value)
right = middle - 1;
else
left = middle + 1;
}
if (left > right)
printf("Element Not found in the list.");

Binary search is a fast search algorithm with run-time complexity of


Ο(log n). This search algorithm works on the principle of divide and conquer.
For this algorithm to work properly, the data collection should be in a sorted
form.
ImplementationinC
#include<stdio.h>
#include<conio.h>
void main()
{
int first, last, middle, size, i, sElement, list[100];
clrscr();

printf("Enter the size of the list: ");


scanf("%d",&size);

printf("Enter %d integer values in Assending order\n", size);

for (i = 0; i < size; i++)


scanf("%d",&list[i]);

printf("Enter value to be search: ");


scanf("%d", &sElement);

first = 0;
last = size - 1;
middle = (first+last)/2;

while (first <= last)


{
if (list[middle] < sElement)
first = middle + 1;
else if (list[middle] == sElement)
{
printf("Element found at index %d.\n",middle);
break;
}
else
last = middle - 1;

middle = (first + last)/2;


}
if (first > last)
printf("Element Not found in the list.");
getch();
}

SORTING
Sorting refers to arranging data in a particular format. Sorting algorithm
specifies the way to arrange data in a particular order. Most common orders are
in numerical or lexicographical order.
The importance of sorting lies in the fact that data searching can be optimized
to a very high level, if data is stored in a sorted manner. Sorting is also used to
represent data in more readable formats. Following are some of the examples
of sorting in real-life scenarios −
 Telephone Directory− The telephone directory stores the telephone
numbers of people sorted by their names, so that the names can be
searched easily.
 Dictionary− The dictionary stores words in an alphabetical order so that
searching of any word becomes easy.

In-placeSortingandNot-in-placeSorting
Sorting algorithms may require some extra space for comparison and
temporary storage of few data elements. These algorithms do not require any
extra space and sorting is said to happen in-place, or for example, within the
array itself. This is called in-place sorting. Bubble sort is an example of in-
place sorting.
However, in some sorting algorithms, the program requires space which
is more than or equal to the elements being sorted. Sorting which uses equal or
more space is called not-in-place sorting. Merge-sort is an example of not-in-
place sorting.

StableandNotStableSorting
If a sorting algorithm, after sorting the contents, does not change the
sequence of similar content in which they appear, it is called stablesorting.

If a sorting algorithm, after sorting the contents, changes the sequence of


similar content in which they appear, it is called unstablesorting.

Stability of an algorithm matters when we wish to maintain the sequence


of original elements, like in a tuple for example.

AdaptiveandNon-AdaptiveSortingAlgorithm
A sorting algorithm is said to be adaptive, if it takes advantage of already
'sorted' elements in the list that is to be sorted. That is, while sorting if the
source list has some element already sorted, adaptive algorithms will take this
into account and will try not to re-order them.
A non-adaptive algorithm is one which does not take into account the
elements which are already sorted. They try to force every single element to be
re-ordered to confirm their sortedness.

ImportantTerms
Some terms are generally coined while discussing sorting techniques, here is a
brief introduction to them
1. Increasing Order
A sequence of values is said to be in increasingorder, if the successive
element is greater than the previous one. For example, 1, 3, 4, 6, 8, 9 are in
increasing order, as every next element is greater than the previous element.

2. Decreasing Order
A sequence of values is said to be in decreasingorder, if the successive
element is less than the current one. For example, 9, 8, 6, 4, 3, 1 are in
decreasing order, as every next element is less than the previous element.

3. Non-Increasing Order
A sequence of values is said to be in non-increasing order, if the
successive element is less than or equal to its previous element in the sequence.
This order occurs when the sequence contains duplicate values. For example, 9,
8, 6, 3, 3, 1 are in non-increasing order, as every next element is less than or
equal to (in case of 3) but not greater than any previous element.

4. Non-Decreasing Order
A sequence of values is said to be in non-decreasing order, if the
successive element is greater than or equal to its previous element in the
sequence. This order occurs when the sequence contains duplicate values. For
example, 1, 3, 3, 6, 8, 9 are in non-decreasing order, as every next element is
greater than or equal to (in case of 3) but not less than the previous one.

BUBBLE SORT ALGORITHM


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 nis 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.

The new array should look like this −

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.

Now we should look into some practical aspects of bubble sort.

Algorithm
We assume listis an array of nelements. We further assume
that swap function swaps the values of the given array elements.

begin BubbleSort(list)
for all elements of list
if list[i] > list[i+1]
swap(list[i], list[i+1])
end if
end for
return list
end BubbleSort

Pseudocode
We observe in algorithm that Bubble Sort compares each pair of array
element unless the whole array is completely sorted in an ascending order. This
may cause a few complexity issues like what if the array needs no more
swapping as all the elements are already ascending.

Pseudocode of BubbleSort algorithm can be written as follows –


for( i = 0; i < n; i++ )
{
for( j = 0; j < n-i-1; j++ )
{
if( A[j] > A[j+1])
{
temp = A[j];
A[j] = A[j+1];
A[j+1] = temp;
}
}
}

Implementation
One more issue we did not address in our original algorithm and its
improvised pseudocode, is that, after every iteration the highest values settles
down at the end of the array. Hence, the next iteration need not include already
sorted elements. For this purpose, in our implementation, we restrict the inner
loop to avoid already sorted values.
Implementation in C

#include <stdio.h>
#include <stdbool.h>
#define MAX 10
int list[MAX] = {1,8,4,6,0,3,5,2,7,9};
void display() {
int i;
printf("[");
// navigate through all items
for(i = 0; i < MAX; i++) {
printf("%d ",list[i]);
}
printf("]\n");
}
void bubbleSort() {
int temp;
int i,j;

bool swapped = false;


// loop through all numbers
for(i = 0; i < MAX-1; i++) {
swapped = false;
// loop through numbers falling ahead
for(j = 0; j < MAX-1-i; j++) {
printf(" Items compared: [ %d, %d ] ", list[j],list[j+1]);
// check if next number is lesser than current no
// swap the numbers.
// (Bubble up the highest number)

if(list[j] > list[j+1])


{
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
swapped = true;
printf(" => swapped [%d, %d]\n",list[j],list[j+1]);
}else {
printf(" => not swapped\n");
}
}
// if no number was swapped that means
// array is sorted now, break the loop.
if(!swapped)
break;
printf("Iteration %d#: ",(i+1));
display();
}
}
main() {
printf("Input Array: ");
display();
printf("\n");
bubbleSort();
printf("\nOutput Array: ");
display();
}
If we compile and run the above program, it will produce the following result −
Output

Input Array: [1 8 4 6 0 3 5 2 7 9 ]
Items compared: [ 1, 8 ] => not swapped
Items compared: [ 8, 4 ] => swapped [4, 8]
Items compared: [ 8, 6 ] => swapped [6, 8]
Items compared: [ 8, 0 ] => swapped [0, 8]
Items compared: [ 8, 3 ] => swapped [3, 8]
Items compared: [ 8, 5 ] => swapped [5, 8]
Items compared: [ 8, 2 ] => swapped [2, 8]
Items compared: [ 8, 7 ] => swapped [7, 8]
Items compared: [ 8, 9 ] => not swapped

Iteration 1#: [1 4 6 0 3 5 2 7 8 9 ]
Items compared: [ 1, 4 ] => not swapped
Items compared: [ 4, 6 ] => not swapped
Items compared: [ 6, 0 ] => swapped [0, 6]
Items compared: [ 6, 3 ] => swapped [3, 6]
Items compared: [ 6, 5 ] => swapped [5, 6]
Items compared: [ 6, 2 ] => swapped [2, 6]
Items compared: [ 6, 7 ] => not swapped
Items compared: [ 7, 8 ] => not swapped

Iteration 2#: [1 4 0 3 5 2 6 7 8 9 ]
Items compared: [ 1, 4 ] => not swapped
Items compared: [ 4, 0 ] => swapped [0, 4]
Items compared: [ 4, 3 ] => swapped [3, 4]
Items compared: [ 4, 5 ] => not swapped
Items compared: [ 5, 2 ] => swapped [2, 5]
Items compared: [ 5, 6 ] => not swapped
Items compared: [ 6, 7 ] => not swapped
Iteration 3#: [1 0 3 4 2 5 6 7 8 9 ]
Items compared: [ 1, 0 ] => swapped [0, 1]
Items compared: [ 1, 3 ] => not swapped
Items compared: [ 3, 4 ] => not swapped
Items compared: [ 4, 2 ] => swapped [2, 4]
Items compared: [ 4, 5 ] => not swapped
Items compared: [ 5, 6 ] => not swapped

Iteration 4#: [0 1 3 2 4 5 6 7 8 9 ]
Items compared: [ 0, 1 ] => not swapped
Items compared: [ 1, 3 ] => not swapped
Items compared: [ 3, 2 ] => swapped [2, 3]
Items compared: [ 3, 4 ] => not swapped
Items compared: [ 4, 5 ] => not swapped

Iteration 5#: [0 1 2 3 4 5 6 7 8 9 ]
Items compared: [ 0, 1 ] => not swapped
Items compared: [ 1, 2 ] => not swapped
Items compared: [ 2, 3 ] => not swapped
Items compared: [ 3, 4 ] => not swapped

Output Array: [0 1 2 3 4 5 6 7 8 9 ]

SELECTION SORT
Selection sort is a simple sorting algorithm. This sorting algorithm is an
in-place comparison-based algorithm in which the list is divided into two parts,
the sorted part at the left end and the unsorted part at the right end. Initially, the
sorted part is empty and the unsorted part is the entire list.
The smallest element is selected from the unsorted array and swapped
with the leftmost element, and that element becomes a part of the sorted array.
This process continues moving unsorted array boundary by one element to the
right.
This algorithm is not suitable for large data sets as its average and worst
case complexities are of Ο(n2), where nis the number of items.

How Selection Sort Works?


Consider the following depicted array as an example.

For the first position in the sorted list, the whole list is scanned sequentially.
The first position where 14 is stored presently, we search the whole list and
find that 10 is the lowest value.

So we replace 14 with 10. After one iteration 10, which happens to be the
minimum value in the list, appears in the first position of the sorted list.

For the second position, where 33 is residing, we start scanning the rest of the
list in a linear manner.

We find that 14 is the second lowest value in the list and it should appear at the
second place. We swap these values.

After two iterations, two least values are positioned at the beginning in a sorted
manner.
The same process is applied to the rest of the items in the array.

Following is a pictorial depiction of the entire sorting process –

Now, let us learn some programming aspects of selection sort.


Algorithm

Step 1 − Set MIN to location 0


Step 2 − Search the minimum element in the list
Step 3 − Swap with value at location MIN
Step 4 − Increment MIN to point to next element
Step 5 − Repeat until list is sorted

Pseudocode
for( i = 0; i < limit; i++ )
{
for( j = i+1; j < limit; j++ )
{
if( A[i] > A[j])
{
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
}

ImplementationinC

#include <stdio.h>
#include <stdbool.h>
#define MAX 7

int intArray[MAX] = {4,6,3,2,1,9,7};


void printline(int count)
{
int i;
for(i = 0;i <count-1;i++)
printf("=");
printf("=\n");
}
void display()
{
int i;
printf("[");
for(i = 0;i<MAX;i++)
printf("%d ", intArray[i]);
printf("]\n");
}
void selectionSort()
{
int indexMin,i,j;
for(i = 0; i < MAX-1; i++)
{
indexMin = i;
for(j = i+1;j<MAX;j++)
if(intArray[j] < intArray[indexMin])
indexMin = j;

if(indexMin != i)
{
printf("Items swapped: [ %d, %d ]\n" , intArray[i], intArray[indexMin]);

// swap the numbers


int temp = intArray[indexMin];
intArray[indexMin] = intArray[i];
intArray[i] = temp;
}
printf("Iteration %d#:",(i+1));
display();
}
}

main()
{
printf("Input Array: ");
display();
printline(50);
selectionSort();
printf("Output Array: ");
display();
printline(50);
}
If we compile and run the above program, it will produce the following result −
Output

Input Array: [4 6 3 2 1 9 7 ]
==================================================
Items swapped: [ 4, 1 ]
Iteration 1#:[1 6 3 2 4 9 7 ]
Items swapped: [ 6, 2 ]
Iteration 2#:[1 2 3 6 4 9 7 ]
Iteration 3#:[1 2 3 6 4 9 7 ]
Items swapped: [ 6, 4 ]
Iteration 4#:[1 2 3 4 6 9 7 ]
Iteration 5#:[1 2 3 4 6 9 7 ]
Items swapped: [ 9, 7 ]
Iteration 6#:[1 2 3 4 6 7 9 ]
Output Array: [1 2 3 4 6 7 9 ]
==================================================

HASH TABLE
Hash Table is a data structure which stores data in an associative manner.
In a hash table, data is stored in an array format, where each data value has its
own unique index value. Access of data becomes very fast if we know the
index of the desired data.
Thus, it becomes a data structure in which insertion and search
operations are very fast irrespective of the size of the data. Hash Table uses an
array as a storage medium and uses hash technique to generate an index where
an element is to be inserted or is to be located from.

Hashing
Hashing is a technique to convert a range of key values into a range of
indexes of an array. We're going to use modulo operator to get a range of key
values. Consider an example of hash table of size 20, and the following items
are to be stored. Item are in the (key,value) format.
There are many possibilities for representing the dictionary and one of the
best methods for representing is hashing. Hashing is a type of a solution which
can be used in almost all situations. Hashing is a technique which uses less key
comparisons and searches the element in O(n) time in the worst case and in an
average case it will be done in O(1) time. This method generally used the hash
functions to map the keys into a table, which is called a hashtable.

1) Hashtable
Hash table is a type of data structure which is used for storing and
accessing data very quickly. Insertion of data in a table is based on a key value.
Hence every entry in the hash table is defined with some key. By using this key
data can be searched in the hash table by few key comparisons and then
searching time is dependent upon the size of the hash table.

2) Hashfunction
Hash function is a function which is applied on a key by which it
produces an integer, which can be used as an address of hash table. Hence one
can use the same hash function for accessing the data from the hash table. In
this the integer returned by the hash function is called hash key.

Types ofhashfunction
There are various types of hash function which are used to place the data
in a hash table,
1. Division method
In this the hash function is dependent upon the remainder of a division.
For example:-if the record 52,68,99,84 is to be placed in a hash table and let us
take the table size is 10.
Then:
h(key)=record% table size.
2=52%10
8=68%10
9=99%10
4=84%10

2. Mid squaremethod
In this method firstly key is squared and then mid part of the result is
taken as the index. For example: consider that if we want to place a record of
3101 and the size of table is 1000. So 3101*3101=9616201 i.e. h (3101) = 162
(middle3 digit)

3. Digitfoldingmethod
In this method the key is divided into separate parts and by using some
simple operations these parts are combined to produce a hash key. For example:
consider a record of 12465512 then it will be divided into parts i.e. 124, 655, 12.
After dividing the parts combine these parts by adding it.
H(key)=124+655+12
=791

Characteristics ofgood hashingfunction


1. The hash function should generate different hash values for the similar
string.
2. The hash function is easy to understand and simple to compute.
3. The hash function should produce the keys which will get distributed,
uniformly over an array.
4. A number of collisions should be less while placing the data in the hash
table.
5. The hash function is a perfect hash function when it uses all the input
data.

Collision
It is a situation in which the hash function returns the same hash key for
more than one record, it is called as collision. Sometimes when we are going to
resolve the collision it may lead to a overflow condition and this overflow and
collision condition makes the poor hash function.

Collision resolution technique


If there is a problem of collision occurs then it can be handled by apply
some technique. These techniques are called as collision resolution techniques.
There are generally four techniques which are described below.
1) Chaining
It is a method in which additional field with data i.e. chain is introduced.
A chain is maintained at the home bucket. In this when a collision occurs then a
linked list is maintained for colliding data.
Example: Let us consider a hash table of size 10 and we apply a hash function
of H(key)=key % size of table. Let us take the keys to be inserted are
31,33,77,61. In the above diagram we can see at same bucket 1 there are two
records which are maintained by linked list or we can say by chaining method.

2) Linearprobing
It is very easy and simple method to resolve or to handle the collision. In
this collision can be solved by placing the second record linearly down,
whenever the empty place is found. In this method there is a problem of
clustering which means at some place block of a data is formed in a hash table.
Example: Let us consider a hash table of size 10 and hash function is defined as
H(key)=key % table size. Consider that following keys are to be inserted that
are 56,64,36,71.
In this diagram we can see that 56 and 36 need to be placed at same
bucket but by linear probing technique the records linearly placed downward if
place is empty i.e. it can be seen 36 is placed at index 7.

3) Quadraticprobing
This is a method in which solving of clustering problem is done. In this
method the hash function is defined by the H(key)=(H(key)+x*x)%table size.
Let us consider we have to insert following elements that are:-67, 90,55,17,49.
In this we can see if we insert 67, 90, and 55 it can be inserted easily but
at case of 17 hash function is used in such a manner that :-(17+0*0)%10=17
(when x=0 it provide the index value 7 only) by making the increment in value
of x. let x =1 so (17+1*1)%10=8.in this case bucket 8 is empty hence we will
place 17 at index 8.

4) Doublehashing
It is a technique in which two hash function are used when there is an
occurrence of collision. In this method 1 hash function is simple as same as
division method. But for the second hash function there are two important rules
which are
 It must never evaluate to zero.
 Must sure about the buckets, that they are probed.

The hash functions for this technique are:


H1(key)=key % table size
H2(key)=P-(key mod P)
Where, p is a prime number which should be taken smaller than the size of a
hash table.
Example: Let us consider we have to insert 67, 90,55,17,49.
In this we can see 67, 90 and 55 can be inserted in a hash table by using
first hash function but in case of 17 again the bucket is full and in this case we
have to use the second hash function which is H2(key)=P-(key mode P) here p
is a prime number which should be taken smaller than the hash table so value of
p will be the 7.
i.e. H2(17)=7-(17%7)=7-3=4 that means we have to take 4 jumps for
placing the 17. Therefore 17 will be placed at index 1.

STACK DATA STRUCTURE


A stack is an Abstract Data Type (ADT), commonly used in most
programming languages. It is named stack as it behaves like a real-world stack,
for example – a deck of cards or a pile of plates, etc.

A real-world stack allows operations at one end only. For example, we


can place or remove a card or plate from the top of the stack only. Likewise,
Stack ADT allows all data operations at one end only. At any given time, we
can only access the top element of a stack.
This feature makes it LIFO data structure. LIFO stands for Last-in-first-
out. Here, the element which is placed (inserted or added) last, is accessed first.
In stack terminology, insertion operation is called PUSH operation and
removal operation is called POP operation.

Stack Representation
The following diagram depicts a stack and its operations –
A stack can be implemented by means of Array, Structure, Pointer, and
Linked List. Stack can either be a fixed size one or it may have a sense of
dynamic resizing. Here, we are going to implement stack using arrays, which
makes it a fixed size stack implementation.

BasicOperations
Stack operations may involve initializing the stack, using it and then de-
initializing it. Apart from these basic stuffs, a stack is used for the following
two primary operations −
 push() − Pushing (storing) an element on the stack.
 pop() − Removing (accessing) an element from the stack.
When data is PUSHed onto stack.
To use a stack efficiently, we need to check the status of stack as well. For the
same purpose, the following functionality is added to stacks −
 peek() − get the top data element of the stack, without removing it.
 isFull() − check if stack is full.
 IsEmpty() - check if stack is empty.
At all times, we maintain a pointer to the last PUSHed data on the stack.
As this pointer always represents the top of the stack, hence named top.
The top pointer provides top value of the stack without actually removing it.

PUSH OPERATION
The process of putting a new data element onto stack is known as a Push
Operation. Push operation involves a series of steps −
 Step 1 − Checks if the stack is full.
 Step 2 − If the stack is full, produces an error and exit.
 Step 3 − If the stack is not full, increments top to point next empty
space.
 Step 4 − Adds data element to the stack location, where top is pointing.
 Step 5 − Returns success.

If the linked list is used to implement the stack, then in step 3, we need to
allocate space dynamically.
Algorithm forPUSH Operation
A simple algorithm for Push operation can be derived as follows −

begin procedure push: stack, data


if stack is full
return null
endif
top ← top + 1
stack[top] ← data

end procedure

Implementation of this algorithm in C, is very easy. See the following code −


Example
void push(int data)
{
if(!isFull())
{
top = top + 1;
stack[top] = data;
}
else
printf("Could not insert data, Stack is full.\n");
}

POP OPERATION
Accessing the content while removing it from the stack, is known as a
Pop Operation. In an array implementation of pop() operation, the data element
is not actually removed, instead top is decremented to a lower position in the
stack to point to the next value. But in linked-list implementation, pop()
actually removes data element and deallocates memory space.
A Pop operation may involve the following steps −
 Step 1 − Checks if the stack is empty.
 Step 2 − If the stack is empty, produces an error and exit.
 Step 3 − If the stack is not empty, accesses the data element at
which top is pointing.
 Step 4 − Decreases the value of top by 1.
 Step 5 − Returns success.

Algorithm forPop Operation


A simple algorithm for Pop operation can be derived as follows −
begin procedure pop: stack
if stack is empty
return null
endif

data ← stack[top]
top ← top - 1
return data
end procedure
Implementation of this algorithm in C, is as follows −
Example

int pop(int data)


{
if(!isempty())
{
data = stack[top];
top = top - 1;
return data;
} else
printf("Could not retrieve data, Stack is empty.\n");
}
peek()
Algorithm of peek() function −

begin procedure peek


return stack[top]
end procedure

Implementation ofpeek() in C language


int peek()
{
return stack[top];
}

isfull()
Algorithm of isfull() function −

BEGIN
if top equals to MAXSIZE
return true
else
return false
endif
end procedure

Implementation ofisfull() in C language


bool isfull()
{
if(top == MAXSIZE)
return true;
else
return false;
}

isempty()
Algorithm of isempty() function −

begin procedure isempty


if top less than 1
return true
else
return false
endif
end procedure
Implementation of isempty() function in C programming language is
slightly different. We initialize top at -1, as the index in array starts from 0. So
we check if the top is below zero or -1 to determine if the stack is empty.
Here's the code −

Implementation ofisempty() function in C


bool isempty()
{
if(top == -1)
return true;
else
return false;
}

ImplementationofStackusingArrayinC

#include <stdio.h>
int MAXSIZE = 8;
int stack[8];
int top = -1;
int isempty()
{
if(top == -1)
return 1;
else
return 0;
}

int isfull()
{
if(top == MAXSIZE)
return 1;
else
return 0;
}

int peek()
{
return stack[top];
}

int pop()
{
int data;

if(!isempty())
{
data = stack[top];
top = top - 1;
return data;
} else
printf("Could not retrieve data, Stack is empty.\n");
}

int push(int data)


{
if(!isfull())
{
top = top + 1;
stack[top] = data;
} else
printf("Could not insert data, Stack is full.\n");
}

int main()
{
// push items on to the stack
push(3);
push(5);
push(9);
push(1);
push(12);
push(15);

printf("Element at top of the stack: %d\n" ,peek());


printf("Elements: \n");

// print stack data


while(!isempty())
{
int data = pop();
printf("%d\n",data);
}
printf("Stack full: %s\n" , isfull()?"true":"false");
printf("Stack empty: %s\n" , isempty()?"true":"false");
return 0;
}

If we compile and run the above program, it will produce the following result −
Output

Element at top of the stack: 15


Elements:
15
12
1
9
5
3
Stack full: false
Stack empty: true

STACK USING LINKED LIST


The major problem with the stack implemented using array is, it works
only for fixed number of data values. That means the amount of data must be
specified at the beginning of the implementation itself. Stack implemented
using array is not suitable, when we don't know the size of data which we are
going to use. A stack data structure can be implemented by using linked list
data structure. The stack implemented using linked list can work for unlimited
number of values. That means, stack implemented using linked list works for
variable size of data. So, there is no need to fix the size at the beginning of the
implementation. The Stack implemented using linked list can organize as many
data values as we want.
In linked list implementation of a stack, every new element is inserted as
'top' element. That means every newly inserted element is pointed by 'top'.
Whenever we want to remove an element from the stack, simply remove the
node which is pointed by 'top' by moving 'top' to its next node in the list.
The nextfield of the first element must be always NULL.
Example
In above example, the last inserted node is 99 and the first inserted node
is 25. The order of elements inserted is 25, 32,50 and 99.
Operations
To implement stack using linked list, we need to set the following things
before implementing actual operations.
Step 1: Include alltheheader fileswhich are used in the program.
And declarealltheuserdefined functions.
Step 2: Definea'Node' structurewithtwomembersdataandnext.
Step 3: DefineaNodepointer'top' and setittoNULL.
Step 4: Implementthemainmethod by displaying Menu with listof
operations and make suitable function calls in
themainmethod.

push(value) - Insertingan elementintotheStack


We can use the following steps to insert a new node into the stack...
Step 1: CreateanewNodewithgiven value.
Step 2: CheckwhetherstackisEmpty(top == NULL)
Step 3: IfitisEmpty, then setnewNode→ next= NULL.
Step 4: IfitisNotEmpty, then setnewNode→ next= top.
Step 5: Finally, settop = newNode.

pop() - Deletingan Elementfrom aStack


Step 1: CheckwhetherstackisEmpty(top == NULL).
Step 2: IfitisEmpty, then display"Stack is Empty!!! Deletion is not
possible!!!" and terminatethefunction
Step 3: If itisNotEmpty, then define aNodepointer 'temp' and set
itto'top'.
Step 4: Then set'top = top → next'.
Step 7: Finally, delete'temp' (free(temp)).
display() - Displayingstackofelements
Step 1: CheckwhetherstackisEmpty(top == NULL).
Step 2: IfitisEmpty, then display'Stack is Empty!!!' and terminate
thefunction.
Step 3: If it isNot Empty, then define a Node pointer'temp' and
initializewithtop.
Step 4: Display 'temp → data--->' and move it to the next node.
Repeat the same untiltemp reaches to the first node in the stack
(temp → next!= NULL).
Step 4: Finally! Display'temp → data---> NULL'.

Program forStackUsingLinked List


#include<stdio.h>
#include<conio.h>
struct Node
{
int data;
struct Node *next;
}*top = NULL;

void push(int);
void pop();
void display();

void main()
{
int choice, value;
clrscr();
printf("\n:: Stack using Linked List ::\n");
while(1){
printf("\n****** MENU ******\n");
printf("1. Push\n2. Pop\n3. Display\n4. Exit\n");
printf("Enter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
push(value);
break;
case 2: pop(); break;
case 3: display(); break;
case 4: exit(0);
default: printf("\nWrong selection!!! Please try again!!!\n");
}
}
}

void push(int value)


{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
if(top == NULL)
newNode->next = NULL;
else
newNode->next = top;
top = newNode;
printf("\nInsertion is Success!!!\n");
}

void pop()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else
{
struct Node *temp = top;
printf("\nDeleted element: %d", temp->data);
top = temp->next;
free(temp);
}
}

void display()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
while(temp->next != NULL){
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL",temp->data);
}
}

Application ofStack
The stack data structure can be use to implement the following concepts:
1. Parsing
2. Recursive Function
3. Calling Function
4. Expression Evaluation
5. Expression Conversion

o Infix to Postfix
o Infix to Prefix
o Postfix to Infix
o Prefix to Infix

6. Towers of hanoi

1 ConvertdecimaltoBinaryUsingStack[DecimaltoBinaryConversion ]

Decimal number can be converted into equivalent binary number using


stack , The procedure to convert the given number into binary is described in the
following program .

#include<stdio.h>
#include<conio.h>
#include<process.h>
#define MAX 10
typedefstructstack
{
intdata[MAX];
inttop;
}stack;

intempty(stack *s)
{
if(s->top==-1)
return(1);
return(0);
}
//----------------------
intfull(stack *s)
{
if(s->top==MAX-1)
return(1);
return(0);
}
//-----------------------
void push(stack *s,intx)
{
s->top=s->top+1;
s->data[s->top]=x;
}
//-----------------------
intpop(stack *s)
{
intx;
x=s->data[s->top];
s->top=s->top-1;
return(x);
}
//------------------------
voidmain()
{
stack s;
intnum;

s.top=-1;
printf("nEnter decimal number:");
scanf("%d",&num);

while((num!=0))
{
if(!full(&s))
{
push(&s,num%2);
num=num/2;
}
else
{
printf("nStack overflow");
exit(0);
}
}

printf("n");
while(!empty(&s))
{
num=pop(&s);
printf("%d",num);
}
}

Explanation:
Step 1:

s.top=-1;
 Initially Top = -1 .
 It is used to create empty Stack.

Step 2 :

printf("nEnter decimal number:");scanf("%d",&num);


 Accept Decimal Number using Scanf

Step 3:
Now we are dividing the original number by 2 and remainder is pushed
onto stack , In the Second Iteration again we are dividing num by 2 and
remainder is pushed onto stack . This action is carried out till number becomes
0.

Step 4 :

if(!full(&s))
{
push(&s,num%2);
num=num/2;
}

 Push Remainder Onto Stack ifStackis notFulland number!= 0

When number becomes 0 i.e Do not push elements onto stack. Now as shown in
video after pushing pop all remainders one by one display it .

2 ReverseStringUsingStack: DataStructure

Accept Characters continuously and push characters onto stack.Accept


characters till occurrence newline character is encountered.

Program :

#include< stdio.h>
#include< conio.h>
#include< process.h>
# define MAX 50

typedef struct stack


{
char data [MAX];
int top;
}stack;
//----------------------------
int empty (stack *p)
{
if (p-> top == -1)
return (1);
else
return (0);
}
//----------------------------
int full (stack *p)
{
if (p->top == MAX-1)
return (1);
else
return (0);
}
//----------------------------
void push (stack *p, char x)
{
if(!full (p))
{
p -> top = p -> top + 1;
p -> data [p -> top] = x;
}
else
printf ("n stack is full");
}
//-----------------------------------
char pop (stack *p)
{
char x;
if (!empty(p))
{
x=p->data[p->top];
p->top = p->top - 1;
return(x);
}
else
{
printf ("n stack is empty:");
return ('');
}
}
//--------------------------------------
void main ()
{ stack s;
char ch;
s.top = -1;
printf ("nEnter a string:");

while ((ch=getchar())!='n')
{
if(!full(&s))
{
push(&s, ch);
}
else
{
printf ("n stack is full…");
exit (0);
}
}
printf ("nReversed String : ");
while (!empty(&s))
{
ch = pop(&s);
printf("%c",ch);
}
}

3 Evaluation ofpostfixexpression : Usingstack


Expression Representation Techniques :

 Infix Expression
 Prefix Expression
 Postfix Expression

Expression Example Note

Infix a+b Operator Between Operands

Prefix +ab Operator before Operands

Postfix ab+ Operator after Operands

Generally postfix expressions are free from Operator Precedence thats


why they are preferred in Computer system.Computer System Uses Postfix
form to represent expression. Following is the example that shows evaluation of
the Postfix expression using stack as data structure.

QUEUE DATA STRUCTURE

Queue is an abstract data structure, somewhat similar to Stacks. Unlike


stacks, a queue is open at both its ends. One end is always used to insert data
(enqueue) and the other is used to remove data (dequeue). Queue follows First-
In-First-Out methodology, i.e., the data item stored first will be accessed first.
A real-world example of queue can be a single-lane one-way road, where
the vehicle enters first, exits first. More real-world examples can be seen as
queues at the ticket windows and bus-stops.

QueueRepresentation

As we now understand that in queue, we access both ends for different


reasons. The following diagram given below tries to explain queue
representation as data structure −

As in stacks, a queue can also be implemented using Arrays, Linked-lists,


Pointers and Structures. For the sake of simplicity, we shall implement queues
using one-dimensional array.

BasicOperations

Queue operations may involve initializing or defining the queue, utilizing


it, and then completely erasing it from the memory. Here we shall try to
understand the basic operations associated with queues −

 enqueue() − add (store) an item to the queue.


 dequeue() − remove (access) an item from the queue.
Few more functions are required to make the above-mentioned queue
operation efficient. These are −

 peek() − Gets the element at the front of the queue without


removing it.
 isfull() − Checks if the queue is full.
 isempty() − Checks if the queue is empty.

In queue, we always dequeue (or access) data, pointed by front pointer


and while enqueing (or storing) data in the queue we take help of rear pointer.
Let's first learn about supportive functions of a queue −

peek()

this function helps to see the data at the front of the queue. The algorithm
of peek() function is as follows:

begin procedure peek

return queue[front]

end procedure

Implementation of peek() function in C programming language −

int peek()
{
return queue[front];

isfull()
As we are using single dimension array to implement queue, we just
check for the rear pointer to reach at MAXSIZE to determine that the queue is
full. In case we maintain the queue in a circular linked-list, the algorithm will
differ. Algorithm of isfull() function −
begin procedure isfull
if rear equals to MAXSIZE
return true
else
return false
endif
end procedure

Implementation of isfull() function in C programming language −


Example

bool isfull()
{
if(rear == MAXSIZE - 1)
return true;
else
return false;
}

isempty()
Algorithm of isempty() function −

begin procedure isempty

if front is less than MIN OR front is greater than rear


return true
else
return false
endif
end procedure

If the value of frontis less than MIN or 0, it tells that the queue is not yet
initialized, hence empty.
Here's the C programming code −

bool isempty()
{
if(front < 0 || front > rear)
return true;
else
return false;
}

EnqueueOperation
Queues maintain two data pointers, frontand rear. Therefore, its
operations are comparatively difficult to implement than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue −
 Step 1 − Check if the queue is full.
 Step 2 − If the queue is full, produce overflow error and exit.
 Step 3 − If the queue is not full, increment rearpointer to point the next
empty space.
 Step 4 − Add data element to the queue location, where the rear is
pointing.
 Step 5 – return success

Sometimes, we also check to see if a queue is initialized or not, to handle


any unforeseen situations.
Algorithm forenqueueoperation

procedure enqueue(data)
if queue is full
return overflow
endif
rear ← rear + 1
queue[rear] ← data
return true
end procedure

Implementation of enqueue() in C programming language −


Example
int enqueue(int data)

if(isfull())

return 0;

rear = rear + 1;

queue[rear] = data;

return 1;

end procedure

DequeueOperation
Accessing data from the queue is a process of two tasks − access the data
where frontis pointing and remove the data after access. The following steps
are taken to perform dequeueoperation −
 Step 1 − Check if the queue is empty.
 Step 2 – If the queue is empty, produce underflow error and exit.
 Step 3 - If the queue is not empty, access the data where front is
pointing.
 Step 4 − Increment frontpointer to point to the next available data
element.
 Step 5 − Return success.

Algorithm fordequeueoperation

procedure dequeue
if queue is empty
return underflow
end if

data = queue[front]
front ← front + 1
return true

end procedure

Implementation of dequeue() in C programming language −

int dequeue()
{
if(isempty())
return 0;
int data = queue[front];
front = front + 1;
return data;
}

Implementationof QUEUEusingArrayinC language

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX 6
int intArray[MAX];
int front = 0;
int rear = -1;
int itemCount = 0;

int peek()
{
return intArray[front];
}

bool isEmpty()
{ return itemCount == 0;
}

bool isFull()
{ return itemCount == MAX;
}

int size()
{ return itemCount;
}

void insert(int data)


{
if(!isFull())
{
if(rear == MAX-1)
{
rear = -1;
}
intArray[++rear] = data;
itemCount++;
}
}

int removeData()
{
int data = intArray[front++];
if(front == MAX)
front = 0;
itemCount--;
return data;
}
int main() {
/* insert 5 items */
insert(3);
insert(5);
insert(9);
insert(1);
insert(12);
// front : 0
// rear : 4
// ------------------
// index : 0 1 2 3 4
// ------------------
// queue : 3 5 9 1 12

insert(15);

// front : 0

// rear : 5
// ---------------------
// index : 0 1 2 3 4 5
// ---------------------
// queue : 3 5 9 1 12 15

if(isFull()){

printf("Queue is full!\n");

// remove one item

int num = removeData();


printf("Element removed: %d\n",num);

// front : 1
// rear : 5
// -------------------
// index : 1 2 3 4 5
// -------------------
// queue : 5 9 1 12 15
// insert more items
insert(16);
// front : 1
// rear : -1
// ----------------------
// index : 0 1 2 3 4 5
// ----------------------
// queue : 16 5 9 1 12 15

// As queue is full, elements will not be inserted.


insert(17);
insert(18);
// ----------------------
// index : 0 1 2 3 4 5
// ----------------------
// queue : 16 5 9 1 12 15
printf("Element at front: %d\n",peek());
printf("----------------------\n");
printf("index : 5 4 3 2 1 0\n");
printf("----------------------\n");
printf("Queue: ");
while(!isEmpty()) {
int n = removeData();
printf("%d ",n);
}
}

If we compile and run the above program, it will produce the following result −

Output

Queue is full!
Element removed: 3
Element at front: 5
----------------------
index : 5 4 3 2 1 0
----------------------
Queue: 5 9 1 12 15 16

QUEUE USING LINKED LIST


The major problem with the queue implemented using array is, It will
work for only fixed number of data. That means, the amount of data must be
specified in the beginning itself. Queue using array is not suitable when we
don't know the size of data which we are going to use. A queue data structure
can be implemented using linked list data structure. The queue which is
implemented using linked list can work for unlimited number of values. That
means, queue using linked list can work for variable size of data (No need to fix
the size at beginning of the implementation). The Queue implemented using
linked list can organize as many data values as we want.
In linked list implementation of a queue, the last inserted node is always
pointed by 'rear' and the first node is always pointed by 'front'.
Example

In above example, the last inserted node is 50 and it is pointed by 'rear'


and the first inserted node is 10 and it is pointed by 'front'. The order of
elements inserted is 10, 15, 22 and 50.

Operations
To implement queue using linked list, we need to set the following things
before implementing actual operations.
Step 1: Include all the header fileswhich are used in the program. And
declare all the userdefined functions.
Step 2: Define a 'Node' structure with two members dataand next.
Step 3: Define two Nodepointers 'front' and 'rear' and set both
to NULL.
Step 4: Implement the mainmethod by displaying Menu of list of
operations and make suitable function calls in the mainmethod to
perform user selected operation.

enQueue(value) - Insertingan elementintotheQueue


Step 1: Create a newNodewith given value and set 'newNode → next'
to NULL.
Step 2: Check whether queue is Empty(rear== NULL)
Step 3: If it is Emptythen, set front= newNodeand rear= newNode.
Step 4: If it is Not Emptythen, set rear →
next= newNodeand rear= newNode.

deQueue() - Deletingan Elementfrom Queue


Step 1: Check whether queueis Empty(front== NULL).
Step 2: If it is Empty, then display "Queue is Empty!!! Deletion is not
possible!!!" and terminate from the function
Step 3: If it is NotEmptythen, define a Node pointer 'temp' and set it to
'front'.
Step 4: Then set 'front= front→ next' and delete 'temp' (free(temp)).

display() - Displayingtheelements ofQueue


Step 1: Check whether queue is Empty(front== NULL).
Step 2: If it is Emptythen, display 'Queue is Empty!!!' and terminate
the function.
Step 3: If it is Not Emptythen, define a Node pointer 'temp' and
initialize with front.
Step 4: Display 'temp → data--->' and move it to the next node. Repeat
the same until 'temp' reaches to 'rear' (temp → next!= NULL).
Step 4: Finally! Display 'temp → data---> NULL'.

ImplementationofQUEUE usingLinkListinC language


#include<stdio.h>
#include<conio.h>
struct Node
{
int data;
struct Node *next;
}*front = NULL,*rear = NULL;

void insert(int);
void delete();
void display();

void main()
{
int choice, value;
clrscr();
printf("\n:: Queue Implementation using Linked List ::\n");
while(1){
printf("\n****** MENU ******\n");
printf("1. Insert\n2. Delete\n3. Display\n4. Exit\n");
printf("Enter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
insert(value);
break;
case 2: delete(); break;
case 3: display(); break;
case 4: exit(0);
default: printf("\nWrong selection!!! Please try again!!!\n");
}
}
}
void insert(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode -> next = NULL;
if(front == NULL)
front = rear = newNode;
else{
rear -> next = newNode;
rear = newNode;
}
printf("\nInsertion is Success!!!\n");
}
void delete()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
front = front -> next;
printf("\nDeleted element: %d\n", temp->data);
free(temp);
}
}
void display()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
while(temp->next != NULL)
{
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL\n",temp->data);
}
}

CIRCULAR QUEUE
In a normal Queue Data Structure, we can insert elements until queue
becomes full. But once if queue becomes full, we cannot insert the next element
until all the elements are deleted from the queue. For example consider the
queue below...
Ater inserting all the elements into the queue.

Now consider the following situation after deleting three elements from the
queue...

This situation also says that Queue is Full and we can not 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 structure. To
overcome this problem we use circular queue data structure.

What is Circular Queue?


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 backtothefirstposition tomakeacircle.
Graphical representation of a circular queue is as follows...

Implementation ofCircularQueue
To implement a circular queue data structure using array, we first
perform the following steps before we implement actual operations.
Step 1: Include all the header fileswhich are used in the program and
define a constant 'SIZE' with specific value.
Step 2: Declare all user defined functionsused in circular queue
implementation.
Step 3: Create a one dimensional array with above defined SIZE (int
cQueue[SIZE])
Step 4: Define two integer variables 'front' and 'rear' and initialize both
with '-1'. (intfront= -1, rear= -1)
Step 5: Implement main method by displaying menu of operations list
and make suitable function calls to perform operation selected by
the user on circular queue.
enQueue(value) - InsertingvalueintotheCircularQueue
In a circular queue, enQueue() is a function which is used to insert an
element into the circular queue. In a circular queue, the new element is always
inserted at rearposition. The enQueue() function takes one integer value as
parameter and inserts that value into the circular queue.
Pseudocodeforenqueueoperation
void enqueue(int item)
{
if(front = = -1 && rear = = -1)
{
front = 0;
rear = 0;
CQ[rear]=item
}

else if ( ((rear +1)%MAX) = = front)


printf("Queue Overflow");

else
{
rear = (rear +1)%MAX
CQ[rear]=item
}
}

deQueue() - Deletingavaluefrom theCircularQueue


In a circular queue, deQueue() is a function used to delete an element
from the circular queue. In a circular queue, the element is always deleted
from frontposition. The deQueue() function doesn't take any value as
parameter.
Pseudocodefordequeueoperation
void dequeue()
{
if(front = = -1 && rear = = -1)
{
printf("Queue Underflow");
}

else if (rear = = front)


front = rear = -1

else
{
printf("Dequeueing %d", CQ[front]);
front = (front +1)%MAX
}
}

display() - Displays theelements ofaCircularQueue


Step 1: Check whether queueis EMPTY. (front== -1)
Step 2: If it is EMPTY, then display "Queue is EMPTY!!!" and
terminate the function.
Step 3: If it is NOT EMPTY, then define an integer variable 'i' and set
'i = front'.
Step 4: Check whether 'front <= rear', if it is TRUE, then display
'queue[i]' value and increment 'i' value by one (i++). Repeat the
same until 'i <= rear' becomes FALSE.
Step 5: If 'front <= rear' is FALSE, then display 'queue[i]' value and
increment 'i' value by one (i++). Repeat the same until'i <= SIZE -
1' becomes FALSE.
Step 6: Set i to 0.
Step 7: Again display 'cQueue[i]' value and increment i value by one
(i++). Repeat the same until 'i <= rear' becomes FALSE.

DOUBLE ENDED QUEUE (DEQUEUE)


Double Ended Queue is also a Queue data structure in which the insertion
and deletion operations are performed at both the ends (frontand rear). That
means, we can insert at both front and rear positions and can delete from both
front and rear positions.
Double Ended Queue can be represented in TWO ways, those are as follows...
1. Input Restricted Double Ended Queue
2. Output Restricted Double Ended Queue
InputRestricted DoubleEnded Queue
In input restricted double ended queue, the insertion operation is
performed at only one end and deletion operation is performed at both the ends.

OutputRestricted DoubleEnded Queue


In output restricted double ended queue, the deletion operation is
performed at only one end and insertion operation is performed at both the ends.

Function to perform insertion from the front


void enqueue_front(intx)
{
if((f==0 && r==size-1) || (f==r+1))
{
printf("deque is full");
}
elseif((f==-1) && (r==-1))
{
f=r=0;
deque[f]=x;
}
elseif(f==0)
{
f=size-1;
deque[f]=x;
}
else
{
f=f-1;
deque[f]=x;
}
}

Function to perform insertion from the rear


void enqueue_rear(intx)
{
if((f==0 && r==size-1) || (f==r+1))
{
printf("deque is full");
}
elseif((f==-1) && (r==-1))
{
r=0;
deque[r]=x;
}
elseif(r==size-1)
{
r=0;
deque[r]=x;
}
else
{
r++;
deque[r]=x;
}
}

Function to perform Deletion from the front


void dequeue_front()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
elseif(f==r)
{
printf("\nThe deleted element is %d", deque[f]);
f=-1;
r=-1;
}
elseif(f==(size-1))
{
printf("\nThe deleted element is %d", deque[f]);
f=0;
}
else
{
printf("\nThe deleted element is %d", deque[f]);
f=f+1;
}
}

Function to perform Deletion from the rear


void dequeue_rear()
{
if((f==-1) && (r==-1))
{
printf("Deque is empty");
}
elseif(f==r)
{
printf("\nThe deleted element is %d", deque[r]);
f=-1;
r=-1;
}
elseif(r==0)
{
printf("\nThe deleted element is %d", deque[r]);
r=size-1;
}
else
{
printf("\nThe deleted element is %d", deque[r]);
r=r-1;
}
}

EXPRESSION PARSING

The way to write arithmetic expression is known as a notation. An


arithmetic expression can be written in three different but equivalent notations,
i.e., without changing the essence or output of an expression. These notations
are −
 Infix Notation
 Prefix (Polish) Notation
 Postfix (Reverse-Polish) Notation
These notations are named as how they use operator in expression.

InfixNotation
Operators are written in-between their operands. We write expression
in infixnotation, e.g. a - b + c, where operators are used in-between operands.
It is easy for us humans to read, write, and speak in infix notation but the same
does not go well with computing devices. An algorithm to process infix
notation could be difficult and costly in terms of time and space consumption.

PrefixNotation (PolishNotation)
Operators are written before their operands. In this notation, operator
is prefixed to operands, i.e. operator is written ahead of operands. For
example, +ab. This is equivalent to its infix notation a + b. Prefix notation is
also known as PolishNotation.

PostfixNotation (Reversed PolishNotation)


Operators are written after their operands. This notation style is known
as Reversed PolishNotation. In this notation style, the operator is postfixed to
the operands i.e., the operator is written after the operands. For example, ab+.
This is equivalent to its infix notation a+ b.

The following table briefly tries to show the difference in all three notations −

Sr.No. InfixNotation PrefixNotation PostfixNotation

1 a+b +ab ab+

2 (a + b) ∗ c ∗+abc ab+c∗

3 a ∗ (b + c) ∗a+bc abc+∗

4 a/b+c/d +/ab/cd ab/cd/+

5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗
6 ((a + b) ∗ c) - d -∗+abcd ab+c∗d-

ParsingExpressions
As we have discussed, it is not a very efficient way to design an
algorithm or program to parse infix notations. Instead, these infix notations are
first converted into either postfix or prefix notations and then computed.
To parse any arithmetic expression, we need to take care of operator
precedence and associativity also.

Precedence
When an operand is in between two different operators, which operator
will take the operand first, is decided by the precedence of an operator over
others. For example −

As multiplication operation has precedence over addition, b * c will be


evaluated first. A table of operator precedence is asfollows:

Operators Symbols

Parenthesis { }, ( ), [ ]

Exponential notation ^

Multiplication and Division *, /

Addition and Subtraction +, -

Associativity
Associativity describes the rule where operators with the same
precedence appear in an expression. For example, in expression a + b − c, both
+ and – have the same precedence, then which part of the expression will be
evaluated first, is determined by associativity of those operators. Here, both +
and − are left associative, so the expression will be evaluated as (a+ b) − c.
Precedence and associativity determines the order of evaluation of an
expression. Following is an operator precedence and associativity table
(highest to lowest) −

Sr.No. Operator Precedence Associativity

1 Exponentiation ^ Highest Right to Left

2 Multiplication ( ∗ ) & Division ( / ) Second Highest Left to Right

3 Addition ( + ) & Subtraction ( − ) Lowest Left to Right

The above table shows the default behavior of operators. At any point of
time in expression evaluation, the order can be altered by using parenthesis.

For example −
In a + b*c, the expression part b*cwill be evaluated first, with
multiplication as precedence over addition. We here use parenthesis for a +
b to be evaluated first, like (a+ b)*c.
PostfixEvaluation Algorithm
Step 1 − scan the expression from left to right
Step 2 − if it is an operand push it to stack
Step 3 − if it is an operator pull operand from stack and perform operation
Step 4 − store the output of step 3, back to stack
Step 5 − scan the expression until all operands are consumed
Step 6 − pop the stack and perform operation

POLISH NOTATION

Polish notation (PN), also known as normal Polish notation (NPN),


Łukasiewicz notation, Warsaw notation, Polish prefix notation or simply prefix
notation, is a form of notation for logic, arithmetic, and algebra. Its
distinguishing feature is that it places operators to the left of their operands. If
the arity of the operators is fixed, the result is a syntax lacking parentheses or
other brackets that can still be parsed without ambiguity.
The Polish logician Jan Łukasiewicz invented this notation in 1924 in order to
simplify sentential logic.
The term Polish notation is sometimes taken (as the opposite of infix
notation) to also include Polish postfix notation, or reverse Polish
notation (RPN), in which the operator is placed after the operands.
When Polish notation is used as a syntax for mathematical expressions
by programming language interpreters, it is readily parsed into abstract syntax
trees and can, in fact, define a one-to-one representation for the same. Because
of this, Lisp (see below) and related programming languages define their entire
syntax in terms of prefix notation (and others use postfix notation).

Arithmetic
The expression for adding the numbers 1 and 2 is, in prefix notation,
written "+ 1 2" rather than "1 + 2". In more complex expressions, the operators
still precede their operands, but the operands may themselves be nontrivial
expressions including operators of their own. For instance, the expression that
would be written in conventional infix notation as
(5 − 6) × 7
can be written in prefix as
× (− 5 6) 7
Since the simple arithmetic operators are all binary (at least, in arithmetic
contexts), any prefix representation thereof is unambiguous, and bracketing the
prefix expression is unnecessary. As such, the previous expression can be
further simplified to
×−567
The processing of the product is deferred until its two operands are
available (i.e., 5 minus 6, and 7). As with any notation, the innermost
expressions are evaluated first, but in prefix notation this "innermost-ness" can
be conveyed by order rather than bracketing.
In the classical notation, the parentheses in the infix version were required,
since moving them
5 − (6 × 7)
or simply removing them
5−6×7
would change the meaning and result of the overall expression, due to
the precedence rule.

Similarly
5 − (6 × 7)
can be written in Polish notation as
−5×67

ComputerProgramming
Prefix notation has seen wide application in Lisp s-expressions, where the
brackets are required since the operators in the language are themselves data
(first-class functions). Lisp functions may also have variable arity.
The Tcl programming language, much like Lisp also uses Polish notation
through the mathop library. The Ambi programming language uses Polish
Notation for arithmetic operations and program construction.
The postfix reverse Polish notation is used in many stack-based programming
languages like PostScript and Forth. CoffeeScript syntax also allows functions to be

called using prefix notation, while still supporting the unary postfix syntax
common in other languages.
The number of return values of an expression equals the difference
between the number of operands in an expression and the total arity of the
operators minus the total number of return values of the operators.
Here is an implementation (in pseudocode) of prefix evaluation using a
stack. Note that under this implementation the input string is scanned from right
to left. This differs from the algorithm described above in which the string is
processed from left to right. Both algorithms compute the same value for all
valid strings.

Scan the given prefix expression from right to left


for each symbol
{
if operand then
push onto stack
if operator then
{
operand1=pop stack
operand2=pop stack
compute operand1 operator operand2
push result onto stack
}
}
return top of stack as result

Example
− × ÷ 15 − 7 + 1 1 3 + 2 + 1 1
To-
Action Stack Notes
ken

1 Operand 1 Push onto stack.

1 Operand 1 1 Push onto stack.

Opera- Pop the two operands (1, 1), calculate (1 + 1 = 2)


+ 2
tor and push onto stack.

2 Operand 2 2 Push onto stack.


Opera- Pop the two operands (2, 2), calculate (2 + 2 = 4)
+ 4
tor and push onto stack.

3 Operand 3 4 Push onto stack.

1 Operand 1 3 4 Push onto stack.

1 Operand 1 1 3 4 Push onto stack.

Opera- Pop the two operands (1, 1), calculate (1 + 1 = 2)


+ 234
tor and push onto stack.

7 Operand 7 2 3 4 Push onto stack.

Opera- Pop the two operands (7, 2), calculate (7 − 2 = 5)


− 534
tor and push onto stack.

15 5 3
15 Operand Push onto stack.
4

Opera- Pop the two operands (15, 5), calculate (15 ÷ 5 = 3)


÷ 334
tor and push onto stack.

Opera- Pop the two operands (3, 3), calculate (3 × 3 = 9)


× 94
tor and push onto stack.

Opera- Pop the two operands (9, 4), calculate (9 − 4 = 5)


− 5
tor and push onto stack.
The result is at the top of the stack.

You might also like