You are on page 1of 49

Data Structure

Unit 1

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 vital 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

Basic Terminology

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 of Data Structures

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 of Data Structures

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 particular 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.
Data Structure Classification

Linear Data Structures: 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:

ge[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.

Value of Reference
element part

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 rear and
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 Linear Data Structures: 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 hierarchy are called leaf node while 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. Trees can be classfied into many categories which will be
discussed later in this tutorial.
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 data structure

1) 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.

2) 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.

3) 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 underflow occurs.

4) 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. We will discuss each one of them later in this tutorial.

5) 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.

6) 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
What is an Algorithm?

An algorithm is a process or a set of rules required to perform calculations or some other


problem-solving operations especially by a computer. The formal definition of an algorithm is
that it contains the finite set of instructions which are being carried in a specific order to perform
the specific task. It is not the complete program or code; it is just a solution (logic) of a problem,
which can be represented either as an informal description using a Flowchart or Pseudocode.

Characteristics of an Algorithm

The following are the characteristics of an algorithm:

o Input: An algorithm has some input values. We can pass 0 or some input value to an
algorithm.
o Output: We will get 1 or more output at the end of an algorithm.
o Unambiguity: An algorithm should be unambiguous which means that the instructions in
an algorithm should be clear and simple.
o Finiteness: An algorithm should have finiteness. Here, finiteness means that the
algorithm should contain a limited number of instructions, i.e., the instructions should be
countable.
o Effectiveness: An algorithm should be effective as each instruction in an algorithm
affects the overall process.
o Language independent: An algorithm must be language-independent so that the
instructions in an algorithm can be implemented in any of the languages with the same
output.

Dataflow of an Algorithm

o Problem: A problem can be a real-world problem or any instance from the real-world
problem for which we need to create a program or the set of instructions. The set of
instructions is known as an algorithm.
o Algorithm: An algorithm will be designed for a problem which is a step by step
procedure.
o Input: After designing an algorithm, the required and the desired inputs are provided to
the algorithm.
o Processing unit: The input will be given to the processing unit, and the processing unit
will produce the desired output.
o Output: The output is the outcome or the result of the program.

Why do we need Algorithms?

We need algorithms because of the following reasons:

o Scalability: It helps us to understand the scalability. When we have a big real-world


problem, we need to scale it down into small-small steps to easily analyze the problem.
o Performance: The real-world is not easily broken down into smaller steps. If the
problem can be easily broken into smaller steps means that the problem is feasible.

Let's understand the algorithm through a real-world example. Suppose we want to make a lemon
juice, so following are the steps required to make a lemon juice:

Step 1: First, we will cut the lemon into half.

Step 2: Squeeze the lemon as much you can and take out its juice in a container.

Step 3: Add two tablespoon sugar in it.

Step 4: Stir the container until the sugar gets dissolved.

Step 5: When sugar gets dissolved, add some water and ice in it.

Step 6: Store the juice in a fridge for 5 to minutes.

Step 7: Now, it's ready to drink.

The above real-world can be directly compared to the definition of the algorithm. We cannot
perform the step 3 before the step 2, we need to follow the specific order to make lemon juice.
An algorithm also says that each and every instruction should be followed in a specific order to
perform a specific task.

Now we will look an example of an algorithm in programming.

We will write an algorithm to add two numbers entered by the user.

The following are the steps required to add two numbers entered by the user:
Step 1: Start

Step 2: Declare three variables a, b, and sum.

Step 3: Enter the values of a and b.

Step 4: Add the values of a and b and store the result in the sum variable, i.e., sum=a+b.

Step 5: Print sum

Step 6: Stop

Factors of an Algorithm

The following are the factors that we need to consider for designing an algorithm:

o Modularity: If any problem is given and we can break that problem into small-small
modules or small-small steps, which is a basic definition of an algorithm, it means that
this feature has been perfectly designed for the algorithm.
o Correctness: The correctness of an algorithm is defined as when the given inputs
produce the desired output, which means that the algorithm has been designed algorithm.
The analysis of an algorithm has been done correctly.
o Maintainability: Here, maintainability means that the algorithm should be designed in a
very simple structured way so that when we redefine the algorithm, no major change will
be done in the algorithm.
o Functionality: It considers various logical steps to solve the real-world problem.
o Robustness: Robustness means that how an algorithm can clearly define our problem.
o User-friendly: If the algorithm is not user-friendly, then the designer will not be able to
explain it to the programmer.
o Simplicity: If the algorithm is simple then it is easy to understand.
o Extensibility: If any other algorithm designer or programmer wants to use your
algorithm then it should be extensible.

Importance of Algorithms

1. Theoretical importance: When any real-world problem is given to us and we break the


problem into small-small modules. To break down the problem, we should know all the
theoretical aspects.
2. Practical importance: As we know that theory cannot be completed without the
practical implementation. So, the importance of algorithm can be considered as both
theoretical and practical.

Issues of Algorithms

The following are the issues that come while designing an algorithm:

o How to design algorithms: As we know that an algorithm is a step-by-step procedure so


we must follow some steps to design an algorithm.
o How to analyze algorithm efficiency

ALGORITHM SPECIFICATION

An algorithm is a finite set of instructions that, if followed, accomplishes a particular task.


All algorithms must satisfy the following criteria:
Input: Zero or more quantities (externally produced)
Output: One or more quantities
Definiteness: Clarity, precision of each instruction
Finiteness: The algorithm has to stop after a finite (may be very large) number of steps
Effectiveness: Each instruction has to be basic enough and feasible

PSEUDOCODECONVENTIONS

1. Comments begin with // and continue until the end of the line./* */

2. Blocks are indicated with matching braces { and }.

3.An identifier (= data type) begins with a letter. Simple types are assumed: Integer, float, char,
boolean, and so on. Compound data types can be formed with records:
node = record
{ datatype_1 data_1;

datatype_ndata_n;
node *link;
}
In the example, link is a pointer to the record type node.

4. Assignment of values to variables is done using the assignment statement


<variable> := <expression>; a:=b+c;

5. There are two Boolean values true and false. In order to produce these values, the logical
operators and, or, and not and the relational operators <, ≤ , ≠ , =, ≥, > are provided.

6. Elements of multidimensional arrays are accessed using [ and ]. For example, if A is a two-
dimensional array, the (i,j)th element of the array is denoted A[i,j].

7. The following looping statements are involved: for, while, repeat, until.
1 The while loop takes the following form:
while<condition> do
{
<statement 1>

<statement n>
}
As long as <condition> is true, the statements get executed. When <condition> becomes false,
the loop is exited.

The general form for a for loop is:


for variable:= value1 to value2 step stepval do
{
<statement 1>

<statement n>
}
Here, value1, value2 and step are arithmetic expressions.

The for loop can be implemented as a while loop.


A repeat-until statement is constructed as follows:
repeat
{
<statement 1>

<statement n>
}
Until<condition>
The statements are executed for as long as condition is false.

The instruction break; can be used within any looping instruction to force exit. In case of
nested loops, break; results in the exit of the innermost loop that is part of it. A return
statement within any of the above loops also will result in exiting the loops. A return statement
results in the exit of the function itself.

8. A conditional statement has the following forms:


if<condition> then<statement>
if<condition> then<statement 1> else<statement 2>
Here, <condition> is a Boolean statement, and <statement>, <statement 1> and <statement 2>
are arbitrary statements.
case
{
:<condition 1>:<statement1>

:<condition n>: <statement n>
: else: <statement n+1>
}
9. Input and output are done using the instructions read and write.

10. There is only one type of procedure: Algorithm.


An algorithm consists of a heading and a body. The heading takes the form:

AlgorithmName (<parameter list>)


Where Name is the name of the procedure and (<parameter list>) is a listing of the
procedure parameters. The body has one or more statements enclosed within { and }.

EXAMPLE: SELECTIONSORT

Goal (specification of the outputs):

We must devise an algorithm that sorts a collection of n≥1 elements of arbitrary type.
Solution: From those elements that are currently unsorted, find the smallest and place it next in
the sorted list.

Although the statement adequately describes the sorting problem, it is not an algorithm
because it leaves several questions unanswered. For example, it does not tell us where and how
the elements are initially stored or where we should place the result. We assume that the
elements are stored in an array a, such that i th integer is stored in the i th position a[i], 1 ≤ i≤ n.
This is our first attempt:
1.for i:= 1 to ndo
2.{
3.Examine a[i] to a[n] and suppose
4.the smallest element is a[j];
5.Interchange a[i] with a[j];
6.}

To turn the algorithm into a pseudocode program, two subtasks remain: Finding the
smallest element.
This task is solved by assuming that a[i] is the minimum, and then comparing a[i] with
a[i+1], a[i+2],..., and whenever a smaller element is found, regarding it as a new minimum.
Eventually a[n] is compared with current minimum and we are done.
Defining “interchange”:
Define t:= a[i]; a[i] = a[j]; a[j]:= t.
1.Algorithm SelectionSort(a,n)
2.// Sort the array a[1:n] into non decreasing order
3.{
4.for i:= 1 to n do
5.{
6. j := i;
7.for k := i+1 to n do
8.if k:= (a[k] < a[j]) then j := k;
9. t:=a[i]; a[i]:=a[j]; a[j]:= t;
10.}
11.}

EXAMPLE: SELECTION SORT IN C++


1.Void SelectionSort(Type a[], intn)
2.// Sortthe array a[1:n] into non decreasing order
3.{
4.for (inti=1; i<=n; i++){
5.intj= i;
6.for (intk=i+1; k<=n; k++)
7.if (a[k]<a[j]) j=k;
8.Type t = a[i]; a[i] = a[j]; a[j] = t;
9.}
10.}

Approaches of Algorithm

The following are the approaches used after considering both the theoretical and practical
importance of designing an algorithm:

o Brute force algorithm: The general logic structure is applied to design an algorithm. It


is also known as an exhaustive search algorithm that searches all the possibilities to
provide the required solution. Such algorithms are of two types:
1. Optimizing: Finding all the solutions of a problem and then take out the best
solution or if the value of the best solution is known then it will terminate if the
best solution is known.
2. Sacrificing: As soon as the best solution is found, then it will stop.
o Divide and conquer: It is a very implementation of an algorithm. It allows you to design
an algorithm in a step-by-step variation. It breaks down the algorithm to solve the
problem in different methods. It allows you to break down the problem into different
methods, and valid output is produced for the valid input. This valid output is passed to
some other function.
o Greedy algorithm: It is an algorithm paradigm that makes an optimal choice on each
iteration with the hope of getting the best solution. It is easy to implement and has a faster
execution time. But, there are very rare cases in which it provides the optimal solution.
o Dynamic programming: It makes the algorithm more efficient by storing the
intermediate results. It follows five different steps to find the optimal solution for the
problem:
1. It breaks down the problem into a subproblem to find the optimal solution.
2. After breaking down the problem, it finds the optimal solution out of these
subproblems.
3. Stores the result of the subproblems is known as memorization.
4. Reuse the result so that it cannot be recomputed for the same subproblems.
5. Finally, it computes the result of the complex program.
o Branch and Bound Algorithm: The branch and bound algorithm can be applied to only
integer programming problems. This approach divides all the sets of feasible solutions
into smaller subsets. These subsets are further evaluated to find the best solution.
o Randomized Algorithm: As we have seen in a regular algorithm, we have predefined
input and required output. Those algorithms that have some defined set of inputs and
required output, and follow some described steps are known as deterministic algorithms.
What happens that when the random variable is introduced in the randomized algorithm?.
In a randomized algorithm, some random bits are introduced by the algorithm and added
in the input to produce the output, which is random in nature. Randomized algorithms are
simpler and efficient than the deterministic algorithm.
o Backtracking: Backtracking is an algorithmic technique that solves the problem
recursively and removes the solution if it does not satisfy the constraints of a problem.

The major categories of algorithms are given below:

o Sort: Algorithm developed for sorting the items in a certain order.


o Search: Algorithm developed for searching the items inside a data structure.
o Delete: Algorithm developed for deleting the existing element from the data structure.
o Insert: Algorithm developed for inserting an item inside a data structure.
o Update: Algorithm developed for updating the existing element inside a data structure.

Algorithm Analysis

The algorithm can be analyzed in two levels, i.e., first is before creating the algorithm, and
second is after creating the algorithm. The following are the two analysis of an algorithm:

o Priori Analysis: Here, priori analysis is the theoretical analysis of an algorithm which is
done before implementing the algorithm. Various factors can be considered before
implementing the algorithm like processor speed, which has no effect on the
implementation part.
o Posterior Analysis: Here, posterior analysis is a practical analysis of an algorithm. The
practical analysis is achieved by implementing the algorithm using any programming
language. This analysis basically evaluate that how much running time and space taken
by the algorithm.

Algorithm Complexity

The performance of the algorithm can be measured in two factors:

o Time complexity: The time complexity of an algorithm is the amount of time required to


complete the execution. The time complexity of an algorithm is denoted by the big O
notation. Here, big O notation is the asymptotic notation to represent the time complexity.
The time complexity is mainly calculated by counting the number of steps to finish the
execution. Let's understand the time complexity through an example.

1. sum=0;  
2. // Suppose we have to calculate the sum of n numbers.  
3. for i=1 to n  
4. sum=sum+i;  
5. // when the loop ends then sum holds the sum of the n numbers  
6. return sum;  

In the above code, the time complexity of the loop statement will be atleast n, and if the value of
n increases, then the time complexity also increases. While the complexity of the code, i.e.,
return sum will be constant as its value is not dependent on the value of n and will provide the
result in one step only. We generally consider the worst-time complexity as it is the maximum
time taken for any given input size.

o Space complexity: An algorithm's space complexity is the amount of space required to


solve a problem and produce an output. Similar to the time complexity, space complexity
is also expressed in big O notation.

For an algorithm, the space is required for the following purposes:

1. To store program instructions


2. To store constant values
3. To store variable values
4. To track the function calls, jumping statements, etc.

Auxiliary space: The extra space required by the algorithm, excluding the input size, is known as
an auxiliary space. The space complexity considers both the spaces, i.e., auxiliary space, and
space used by the input.

So,

Space complexity = Auxiliary space + Input size.

Types of Algorithms

The following are the types of algorithm:

o Search Algorithm
o Sort Algorithm

Search Algorithm

On each day, we search for something in our day to day life. Similarly, with the case of
computer, huge data is stored in a computer that whenever the user asks for any data then the
computer searches for that data in the memory and provides that data to the user. There are
mainly two techniques available to search the data in an array:

o Linear search
o Binary search

Linear Search
Linear search is a very simple algorithm that starts searching for an element or a value from the
beginning of an array until the required element is not found. It compares the element to be
searched with all the elements in an array, if the match is found, then it returns the index of the
element else it returns -1. This algorithm can be implemented on the unsorted list.

Binary Search

A Binary algorithm is the simplest algorithm that searches the element very quickly. It is used to
search the element from the sorted list. The elements must be stored in sequential order or the
sorted manner to implement the binary algorithm. Binary search cannot be implemented if the
elements are stored in a random manner. It is used to find the middle element of the list.

Sorting Algorithms

Sorting algorithms are used to rearrange the elements in an array or a given data structure either
in an ascending or descending order. The comparison operator decides the new order of the
elements.

Why do we need a sorting algorithm?

o An efficient sorting algorithm is required for optimizing the efficiency of other


algorithms like binary search algorithm as a binary search algorithm requires an array to
be sorted in a particular order, mainly in ascending order.
o It produces information in a sorted order, which is a human-readable format.
o Searching a particular element in a sorted list is faster than the unsorted list.

Array
Array is a container which can hold a fix number of items and these items should be of the same
type. Most of the data structures make use of arrays to implement their algorithms. Following
are the important terms to understand the concept of Array.
 Element − Each item stored in an array is called an element.
 Index − Each location of an element in an array has a numerical index, which is used to
identify the element.

Array Representation

Arrays can be declared in various ways in different languages. For illustration, let's take C array
declaration.
Arrays can be declared in various ways in different languages. For illustration, let's take C array
declaration.

As per the above illustration, following are the important points to be considered.
 Index starts with 0.
 Array length is 10 which means it can store 10 elements.
 Each element can be accessed via its index. For example, we can fetch an element at
index 6 as 9.

Basic Operations of Array

Following are the basic operations supported by an array.


 Traverse − print all the array elements one by one.
 Insertion − Adds an element at the given index.
 Deletion − Deletes an element at the given index.
 Search − Searches an element using the given index or by the value.
 Update − Updates an element at the given index.
In C, when an array is initialized with size, then it assigns defaults values to its elements in
following order.

Data Type Default Value

bool false

char 0

int 0

float 0.0
double 0.0f

void

Traverse Operation

This operation is to traverse through the elements of an array.

Example

Following program traverses and prints the elements of an array:


#include <stdio.h>
main() {
int LA[] = {1,3,5,7,8};
int item = 10, k = 3, n = 5;
int i = 0, j = n;
printf("The original array elements are :\n)";
for(i = 0; i<n; i++) {
printf"LA[%d] = %d \n"<< i, LA[i]);
}
}
When we compile and execute the above program, it produces the following result −

Output

The original array elements are :


LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 7
LA[4] = 8

Insertion Operation

Insert operation is to insert one or more data elements into an array. Based on the requirement, a
new element can be added at the beginning, end, or any given index of array.
Here, we see a practical implementation of insertion operation, where we add data at the end of
the array −

Example

Following is the implementation of the above algorithm −


#include < stdio.h >
main() {
int LA[] = {1,3,5,7,8};
int item = 10, k = 3, n = 5;
int i = 0, j = n;

printf(“The original array elements are :\n");

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


print(“fLA[%d] = %d \n",i, LA[i]);
}

n = n + 1;

while( j >= k) {
LA[j+1] = LA[j];
j = j - 1;
}

LA[k] = item;

cout<<"The array elements after insertion :\n";

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


printf(“LA[%d] = %d \n", i, LA[i]);
}
}
When we compile and execute the above program, it produces the following result −

Output

The original array elements are :


LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 7
LA[4] = 8
The array elements after insertion :
LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 10
LA[4] = 7
LA[5] = 8
For other variations of array insertion operation click here
Deletion Operation

Deletion refers to removing an existing element from the array and re-organizing all elements of
an array.

Algorithm

Consider LA is a linear array with N elements and K is a positive integer such that K<=N.


Following is the algorithm to delete an element available at the Kth position of LA.
1. Start
2. Set J = K
3. Repeat steps 4 and 5 while J < N
4. Set LA[J] = LA[J + 1]
5. Set J = J+1
6. Set N = N-1
7. Stop

Example

Following is the implementation of the above algorithm −


#include < stdio.h >

void main() {
int LA[] = {1,3,5,7,8};
int k = 3, n = 5;
int i, j;

printf("The original array elements are :\n");

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


printf("LA[%d] = %d \n", i, LA[i]);
}

j = k;

while( j < n) {
LA[j-1] = LA[j];
j = j + 1;
}

n = n -1;

printf("The array elements after deletion :\n");

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


printf("LA[%d] = %d \n", i, LA[i]);
}
}
When we compile and execute the above program, it produces the following result −

Output

The original array elements are :


LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 7
LA[4] = 8
The array elements after deletion :
LA[0] = 1
LA[1] = 3
LA[2] = 7
LA[3] = 8

Search Operation

You can perform a search for an array element based on its value or its index.

Algorithm

Consider LA is a linear array with N elements and K is a positive integer such that K<=N.


Following is the algorithm to find an element with a value of ITEM using sequential search.
1. Start
2. Set J = 0
3. Repeat steps 4 and 5 while J < N
4. IF LA[J] is equal ITEM THEN GOTO STEP 6
5. Set J = J +1
6. PRINT J, ITEM
7. Stop

Example

Following is the implementation of the above algorithm −


#include <stdio.h>

void main() {
int LA[] = {1,3,5,7,8};
int item = 5, n = 5;
int i = 0, j = 0;
printf("The original array elements are :\n");

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


printf("LA[%d] = %d \n", i, LA[i]);
}

while( j < n){


if( LA[j] == item ) {
break;
}

j = j + 1;
}

printf("Found element %d at position %d\n", item, j+1);


}
When we compile and execute the above program, it produces the following result −

Output

The original array elements are :


LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 7
LA[4] = 8
Found element 5 at position 3

Update Operation

Update operation refers to updating an existing element from the array at a given index.

Algorithm

Consider LA is a linear array with N elements and K is a positive integer such that K<=N.


Following is the algorithm to update an element available at the Kth position of LA.
1. Start
2. Set LA[K-1] = ITEM
3. Stop

Example

Following is the implementation of the above algorithm −


#include <stdio.h>

void main() {
int LA[] = {1,3,5,7,8};
int k = 3, n = 5, item = 10;
int i, j;

printf("The original array elements are :\n");

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


printf("LA[%d] = %d \n", i, LA[i]);
}

LA[k-1] = item;

printf("The array elements after updation :\n");

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


printf("LA[%d] = %d \n", i, LA[i]);
}
}
When we compile and execute the above program, it produces the following result −

Output

The original array elements are :


LA[0] = 1
LA[1] = 3
LA[2] = 5
LA[3] = 7
LA[4] = 8
The array elements after updation :
LA[0] = 1
LA[1] = 3
LA[2] = 10
LA[3] = 7
LA[4] = 8
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 n is the number of items.

How Bubble Sort Works?

We take an unsorted array for our example. Bubble sort takes Ο(n 2) 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 list is an array of n elements. We further assume that swap function swaps the


values of the given array elements.
Algorithm 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
}

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.
To ease-out the issue, we use one flag variable swapped which will help us see if any swap has
happened or not. If no swap has occurred, i.e. the array requires no more processing to be
sorted, it will come out of the loop.
Pseudocode of BubbleSort algorithm can be written as follows −
Algorithm bubbleSort( list : array of items )
{
loop = list.count;

for i = 0 to loop-1 do:


swapped = false

for j = 0 to loop-1 do:

/* compare the adjacent elements */


if list[j] > list[j+1] then
/* swap them */
swap( list[j], list[j+1] )
swapped = true
end if

end for

/*if no number was swapped that means


array is sorted now, break the loop.*/

if(not swapped) then


break
end if

end for

return list
}

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.

// below we have a simple C++ program for bubble sort


#include <iostream.h>
void bubbleSort(int arr[], int n)
{
int i, j, temp;
for(i = 0; i < n; i++)
{
for(j = 0; j < n-i-1; j++)
{
if( arr[j] > arr[j+1])
{
// swap the elements
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}

// print the sorted array


printf("Sorted Array: ");
for(i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}

int main()
{
int arr[100], i, n, step, temp;
// ask user for number of elements to be sorted
printf("Enter the number of elements to be sorted: ");
scanf("%d", &n);
// input elements if the array
for(i = 0; i < n; i++)
{
printf("Enter element no. %d: ", i+1);
scanf("%d", &arr[i]);
}
// call the function bubbleSort
bubbleSort(arr, n);
return 0;
}
Linear Search

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.

Linear Search Algorithm (Sequential Search Algorithm)


Linear search algorithm finds a given element in a list of elements with O(n) time
complexity where n is total number of elements in the list. This search process starts comparing
search element with the first element in the list. If both are matched then result is element found
otherwise search element is compared with the next element in the list. Repeat the same until
search element is compared with the last element in the list, if that last element also doesn't
match, then the result is "Element not found in the list". That means, the search element is
compared with element by element in the list.

Linear search is implemented using following steps...


Step 1 - Read the search element from the user.
Step 2 - Compare the search element with the first element in the list.
Step 3 - If both are matched, then display "Given element is found!!!" and
terminate the function
Step 4 - If both are not matched, then compare search element with the
next element in the list.
Step 5 - Repeat steps 3 and 4 until search element is compared with last
element in the list.
Step 6 - If last element in the list also doesn't match, then display
"Element is not found!!!" and terminate the function.

Algorithm:
//Let array a[n] stores n elements. Determine whether element ‘x’ is present or not.
linsrch(a[n], x)
{
index = 0;
flag = 0;
while (index < n) do
{
if (x == a[index])
{
flag = 1;
break;
} index ++;
}
if(flag == 1)
write(“Data found at %d position“, index);
else
write(“data not found”);
}

Example
Consider the following list of elements and the element to be searched...
Implementation of Linear Search Algorithm using C++ Programming Language
#include<iostream.h>
#include<conio.h>
void main()
{
int list[20],size, i, sElement;
cout<<”Enter size of the list: ";
cin>>size;
cout<<”Enter any”<<size<<” integer values: ";
for(i = 0; i < size; i++)
cin>>list[i];
cout<<"Enter the element to be Search: ";
cin>>sElement;
// Linear Search Logic
for(i = 0; i < size; i++)
{
if(sElement == list[i])
{
Cout<<"Element is found at”<<i<< “ index” ;
break;
}
}
if(i == size)
cout<<"Given element is not found in the list!!!";
getch();
}

Binary Search
Binary search algorithm falls under the category of interval search algorithms. This algorithm
is much more efficient compared to linear search algorithm. Binary search only works
on sorted data structures. This algorithm repeatedly target the center of the sorted data structure
& divide the search space into half till the match is found.
The time complexity of binary search algorithm is O(Log n).
Binary search is implemented using following steps...
BSEARCH(ARR, LB, UB, ITEM, LOC)
{
/*Here, ARR is a sorted list of elements, with LB and UB are lower and upper bounds for the
array. ITEM needs to be searched in the array and algorithm returns location LOC, index at
which ITEM is present else return -1.*/

Step1: Set BEG = LB, END = UB and MID = INT([BEG+END]/2)


Step 2: Repeat step 3 and 4 while BEG <= END and ARR[MID] != ITEM
Step 3: IF ITEM< ARR[MID] then:
Set END = MID-1
Else:
Set BEG = MID+1
Step 4: Set MID = INT(BEG+END)/2
Step 5: IF ARR[MID] = ITEM then:
Set LOC = MID
Else:
Set LOC = NULL
Step 6. Exit.
}

Algorithm
//Let array a[n] of elements in increasing order, n ≥0, determine whether ‘x’ is present, and if so,
set j, such that x = a[j] else return 0.
binsrch(a[], n, x)
{
low = 1; high = n;
while (low < high) do
{
mid = (low + high)/2
if (x < a[mid])
high = mid – 1;
else if (x > a[mid])
low = mid + 1;
else return mid;
} return 0;

Example:
Let’s say here, ITEM = 62
ARR[1] 2 3 4 5 6 7 8 9
BEG = 1 and END =9 Hence MID = (1+9)/2 = 5

ARR[MID]=ARR[5] = 52

Step 1: ARR[MID] < ITEM : thus END =9 and BEG = MID +1 = 6.


52<62
Thus our new sub-array is,

6 7 8 9
Step 2: Now BEG =6 and END =9 thus MID = INT([6+9]/2)= 6
NOW ARR[6] =ITEM. Thus LOC = MID
ARR[6]=62 = ITEM =62,
Thus LOC = 6
BEG=MID+1=6+1=7
MID=7+9/2=16/2=8
ARR[8]==71
BEG 7, END =MID-1=7
ARR[7]= ITEM
71=71
The complexity of Binary Search
Here are the complexities of the binary search given below.
 Worst Case: O(nlogn)
 Best Case: O(1)
 Average Case: O(nlogn)

//C++ Program to Implement Binary Search –


#include < iostream >
using namespace std;
int binarySearch(int arr[], int left, int right, int x)
{
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] == x)
{
return mid;
} else if (arr[mid] < x)
{
left = mid + 1;
} else
{
right = mid - 1;
}
}
return -1;
}
int main()
{
int myarr[10];
int num;
int output;
cout << "Please enter 10 elements ASCENDING order" << endl;
for (int i = 0; i < 10; i++)
{
cin >> myarr[i];
}
cout << "Please enter an element to search" << endl;
cin >> num;
output = binarySearch(myarr, 0, 9, num);
if (output == -1)
{
cout << "No Match Found" << endl;
} else
{
cout << "Match found at position: " << output+1 << endl;
}
return 0;
}

Example 2
 
Consider-
 We are given the following sorted linear array.
 Element 15 has to be searched in it using Binary Search Algorithm.
 

 
Binary Search Algorithm works in the following steps-
 
Step-01:
 
To begin with, we take beg=0 and end=6.
We compute location of the middle element as-
mid
= (beg + end) / 2
= (0 + 6) / 2
=3
Here, a[mid] = a[3] = 20 ≠ 15 and beg < end.
So, we start next iteration.
 
Step-02:
 
Since a[mid] = 20 > 15, so we take end = mid – 1 = 3 – 1 = 2 whereas beg remains unchanged.
We compute location of the middle element as-
mid
= (beg + end) / 2
= (0 + 2) / 2
=1
Here, a[mid] = a[1] = 10 ≠ 15 and beg < end.
So, we start next iteration.
 
Step-03:
 
Since a[mid] = 10 < 15, so we take beg = mid + 1 = 1 + 1 = 2 whereas end remains
unchanged.
We compute location of the middle element as-
mid
= (beg + end) / 2
= (2 + 2) / 2
=2
Here, a[mid] = a[2] = 15 which matches to the element being searched.
So, our search terminates in success and index 2 is returned.

Binary Search Algorithm Advantages-


 
The advantages of binary search algorithm are-
 It eliminates half of the list from further searching by using the result of each comparison.
 It indicates whether the element being searched is before or after the current position in
the list.
 This information is used to narrow the search.
 For large lists of data, it works significantly better than linear search.
 
Binary Search Algorithm Disadvantages-
 
The disadvantages of binary search algorithm are-
 It employs recursive approach which requires more stack space.
 Programming binary search algorithm is error prone and difficult.
 The interaction of binary search with memory hierarchy i.e. caching is poor.
(because of its random access nature)
Record Structures

A record structure is an aggregate entity containing one or more elements. (Record


elements are also called fields or components.) You can use records when you
need to declare and operate on multi-field data structures in your programs.

Creating a record is a two-step process:

You must define the form of the record with a multistatement structure


declaration.
1. You must use a RECORD statement to declare the record as an entity with a
name. (More than one RECORD statement can refer to a given structure.)

Examples
Intel Fortran record structures, using only intrinsic types, easily convert to Fortran
95/90 derived types. The conversion can be as simple as replacing the
keyword STRUCTURE with TYPE and removing slash ( / ) marks. The following
shows an example conversion:

Record Structure

STRUCTURE /employee_name/
CHARACTER*25 last_name
CHARACTER*15 first_name
END STRUCTURE
STRUCTURE /employee_addr/
CHARACTER*20 street_name
INTEGER(2) street_number
INTEGER(2) apt_number
CHARACTER*20 city
CHARACTER*2 state
INTEGER(4) zip
END STRUCTURE
The record structures can be used as subordinate record variables within another
record, such as the employee_data record. The equivalent Fortran 90 derived
type would use the derived-type objects as components in a similar manner, as
shown below:

Record Structure

STRUCTURE /employee_data/
RECORD /employee_name/ name
RECORD /employee_addr/ addr
INTEGER(4) telephone
INTEGER(2) date_of_birth
INTEGER(2) date_of_hire
INTEGER(2) social_security(3)
LOGICAL(2) married
INTEGER(2) dependents
END STRUCTURE

C/C++ arrays allow you to define variables that combine several data items of the
same kind, but structure is another user defined data type which allows you to
combine data items of different kinds.
Structures are used to represent a record, suppose you want to keep track of your
books in a library. You might want to track the following attributes about each
book −

 Title
 Author
 Subject
 Book ID

Defining a Structure

To define a structure, you must use the struct statement. The struct statement
defines a new data type, with more than one member, for your program. The
format of the struct statement is this −
struct [structure tag]
{
member definition;
member definition;
...
member definition;
} [one or more structure variables];
The structure tag is optional and each member definition is a normal variable
definition, such as int i; or float f; or any other valid variable definition. At the end
of the structure's definition, before the final semicolon, you can specify one or
more structure variables but it is optional. Here is the way you would declare the
Book structure −
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;

Accessing Structure Members


To access any member of a structure, we use the member access operator (.). The
member access operator is coded as a period between the structure variable name
and the structure member that we wish to access. You would use struct keyword to
define variables of structure type. Following is the example to explain usage of
structure −
#include <iostream.h>
#include <string.h>

struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};

int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book

// book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;
// book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;

// Print Book1 info


cout << "Book 1 title : " << Book1.title <<endl;
cout << "Book 1 author : " << Book1.author <<endl;
cout << "Book 1 subject : " << Book1.subject <<endl;
cout << "Book 1 id : " << Book1.book_id <<endl;

// Print Book2 info


cout << "Book 2 title : " << Book2.title <<endl;
cout << "Book 2 author : " << Book2.author <<endl;
cout << "Book 2 subject : " << Book2.subject <<endl;
cout << "Book 2 id : " << Book2.book_id <<endl;

return 0;
}

When the above code is compiled and executed, it produces the following result −
Book 1 title : Learn C++ Programming
Book 1 author : Chand Miyan
Book 1 subject : C++ Programming
Book 1 id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Yakit Singha
Book 2 subject : Telecom
Book 2 id : 6495700

Structures as Function Arguments


You can pass a structure as a function argument in very similar way as you pass any
other variable or pointer. You would access structure variables in the similar way as
you have accessed in the above example −
#include <iostream.h>
#include <string.h>
void printBook( struct Books book );
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book

// book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;

// book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;

// Print Book1 info


printBook( Book1 );

// Print Book2 info


printBook( Book2 );

return 0;
}
void printBook( struct Books book ) {
cout << "Book title : " << book.title <<endl;
cout << "Book author : " << book.author <<endl;
cout << "Book subject : " << book.subject <<endl;
cout << "Book id : " << book.book_id <<endl;
}

When the above code is compiled and executed, it produces the following result −
Book title : Learn C++ Programming
Book author : Chand Miyan
Book subject : C++ Programming
Book id : 6495407
Book title : Telecom Billing
Book author : Yakit Singha
Book subject : Telecom
Book id : 6495700
Pointers to Structures
You can define pointers to structures in very similar way as you define pointer to any
other variable as follows −
struct Books *struct_pointer;
Now, you can store the address of a structure variable in the above defined pointer
variable. To find the address of a structure variable, place the & operator before the
structure's name as follows −
struct_pointer = &Book1;
To access the members of a structure using a pointer to that structure, you must use
the -> operator as follows −
struct_pointer->title;
Let us re-write above example using structure pointer, hope this will be easy for you to
understand the concept −
#include <iostream.h>
#include <string.h>

using namespace std;


void printBook( struct Books *book );

struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book

// Book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;

// Book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;

// Print Book1 info, passing address of structure


printBook( &Book1 );

// Print Book1 info, passing address of structure


printBook( &Book2 );

return 0;
}
// This function accept pointer to structure as parameter.
void printBook( struct Books *book ) {
cout << "Book title : " << book->title <<endl;
cout << "Book author : " << book->author <<endl;
cout << "Book subject : " << book->subject <<endl;
cout << "Book id : " << book->book_id <<endl;
}

When the above code is compiled and executed, it produces the following result −
Book title : Learn C++ Programming
Book author : Chand Miyan
Book subject : C++ Programming
Book id : 6495407
Book title : Telecom Billing
Book author : Yakit Singha
Book subject : Telecom
Book id : 6495700

Record - a linear, direct-access data structure with heterogeneous components.


See taxonomy.

Field or member - component of the record; each field has its own type

FieldName - the identifier used to refer to the field. The identifier only need to be
unique within the struct.  It will not be in conflict with names  used elsewhere or in
other structs.

Records have been implemented as classes in C++.

Example:
Student (idnumber, name, status, credits, gpa)
enum StatusType {FULLTIME, PARTTIME, NONDEGREE, GRAD}
struct StudentType {
   int idNumber;
   char name[30];
   StatusType status;
   float credits;
   float gpa;
};

StudentType student1;
StudentType student2;

Dot operator
The dot (.) operator is used to select members from a record (struct).

So that student1.idNumber accesses the member idNumber from the object student1.

student2.name refers to the name of object student2.


if(student1.status == FULLTIME) 

Variations on the struct statement:


1. Combined struct and instance declarations

struct StudentType {
....
}student1,student2; //not recommended
 

2. Unnamed struct types

struct {
.....
} student1, student2; //no way to later make a new instance

Features about structs


Structs can be assigned as as aggregate

This is an important feature. Compare to the fact that you cannot assign whole arrays
or strings by themselves. You must copy an array element by element, a string using
the strcpy( ) function. 

student1 = student2; //OK

Arrays cannot be assigned as an aggregate 

student1.name = student2.name; //NOT OK  

Structs can be passed as parameters either by value or by reference

Fieldname Issues
The fieldnames are local to the struct.
The same member name can be used in other structs.

Every member name reference is always associated with the record or instance name.

struct type1{ struct type2{


    int a;     int a;
    int b;     float b;
}; };
type1 x,y; type2 b,a;

Note the various references to identifier "a" and "b" x.a y.a a.a a.b int a  

Membership
Any structure can be a member of a struct

struct NameType {
   char first[15];
   char middleInit;
   char last[15];
};
struct StudentType {
   NameType name;
   int      idNum;
   float    credits;
   float    gpa;
};
StudentType student1,student2;
student1.name.last;
student2.name.first[0];

Type compatibility
Two struct instances are compatible only if their types are identical or are renamings
of the same type.

struct TypeA{  struct TypeA{  typedef int Counter; 


   int a,b;     int a,b;  Counter i,j,k; 
};  };  int n; 
struct TypeB { 
   int a,b;  typedef TypeA TypeB;  i = n; //OK
}; 
TypeA r;  r = s; //OK
TypeB s; 
r = s; //NOT OK

You might also like