Unit 1 DS PDF

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

Unit 1 - Syllabus: Introduction: Basic Terminology, Pointers and dynamic

memory allocation, Elementary. Data Organization, Data Structure Operation,


Algorithm Complexity and Time-Space trade-off, Arrays: Array Definition,
Representation and Analysis, single and Multidimensional Arrays, address
calculation, application of arrays, Array as parameters, Ordered list, sparse
Matrices, and Vector.

Definition & Concept of Data Structure

The intimate relationship between data and programs can be traced to the
beginning of computing. In any area of application, the input data, (internally
stored data) and output data may each have a unique structure. Data structure is
representation of the logical relationship existing between individual elements of
data. In other words, a data structure is a way of organizing all data items that
considers not only the elements stored but also their relationship to each other.

Data structure mainly specifies the following four things :

(i) Organization of data


(ii) Accessing methods
(iii) Degree of Associativity
(iv) Processing alternatives for information

Data structures are the building blocks of a program, and it affects the design of
both structural and functional aspects of a program. And hence the selection of a
particular data structure stresses on the following two things.

1. The data structures must be rich enough in structure to reflect the relationship
existing between the data.
2. The structure should be simple so that we can process data effectively whenever
required.

Classification of Data Structure

Data structures are normally divided into two broad categories.

(i) Primitive data structures


(ii) Non-primitive data structures

Figure 1. Data Structure classification

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 1


Primitive Data Structure

These are basic structures and are directly operated upon by the machine
instructions. In general, they have different representations on different
computers. Integer, floating point numbers, character constants, string
constants, pointers etc.

Non-Primitive Data Structures

These are more sophisticated data structures. These are derived from the
primitive data structures. The non-primitive data structures emphasize on
structuring of a group of homogeneous (same type) or heterogeneous (different type)
data items. Arrays, lists and files are examples.

1. Arrays - An array most commonly used non-primitive data structures. It is defined


as a set of finite number of homogeneous elements or data items. It means an array
can contain one type of data only, either all integers, all floating-point numbers, or
all characters. Declaration of arrays is as follows :

int a[10] ;

Where int specifies the data type or type of elements array stores. ”a” is the name
of array, and the number specified inside the square brackets is the number of
elements an array can store this is also called size or length of array.

2. Lists - A list (Linear Linked list) can be defined as a collection of variable number
of data items. Lists are the most commonly used non-primitive data structures. An
element of list must contain least two fields, one for storing data or information
and other for storing address of next element. For storing address we have a
special data structures called pointers, hence the second field of the list must be
pointer type. Technically each such element is referred to as a node.

Figure 2. Linear Linked List

List is categories into two types:

i. Linear List or Linear data structures


ii. Non Linear list or Non Linear data structures

2.1 Linear data structures – Those data structure where the data elements are
organised in some sequence is called Linear data structures. Here operation on data
structure are possible in a sequence. Stack, queue, array are example of linear data
structure.

2.1.1 Stack - A stack is commonly used non-primitive linear data structures. A


stack is also an ordered collection of elements like arrays, but it has a special
Prepared By: Prof. Dheresh Soni SRHU - HSET Page 2
feature that deletion and insertion of elements can be done only from one end,
called the top of stack (TOP). Due to this property it is also called as last in first
out type of data structure (LIFO). It is a non-primitive data structure.

When an element is inserted into a stack or removed from


the stack, its base remains fixed whereas the top of stack
changes. Insertion of element into stack is called Push
and Deletion of element from stack is called Pop. The
figure 3 shows how the operations take place on a stack.

The stacks can be implemented in two ways:

(i) Using arrays (static implementation).


(ii) Using pointers (dynamic implementation)
Figure 3. Operations on stack.

2.1.2 Queues - Queues are first in first out type of Data Structures (i.e., FIFO).
In a queue new elements are added to the queue from one end called REAR
end, and the elements are always removed from other end called the FRONT
end.

The Queues can also be


implemented in two ways:

(i) Using arrays (static implementation). Figure 4. Operations on Queue.


(ii) Using pointers (dynamic implementation)

2.2 Non Linear data structures – Those data structure where the data
elements are not organised in some sequence, organised in some arbitrary function
without any sequence is called Non linear data structures. Graph, Tree are example
of linear data structure.

2.2.1 Trees - A Tree can be defined as finite set of data items (nodes). Tree is
non primitive non-linear type of data structures in which data items are
arranged or stored in a sorted sequence. Trees represent the hierarchical
relationship between various elements. In trees:

1. There is a special data item at the top


of hierarchy called the Root of the tree.
2. The remaining data items are
partitioned into number of mutually
exclusive subsets, each of which is
itself, a tree, which is called the
subtree.
3. The tree always grows in length towards
bottom in data structures, unlike
natural trees which grow upwards. Figure 5 Binary. Tree

The tree structure organizes the data into branches, which relate the information.

2.2.2 Graph - Graph is a mathematical non primitive non-linear data structure


capable of representing many kinds of physical structures. A graph G (V, E) is a

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 3


set of vertices V and a set of edges E. An edge connects a pair of vertices and
many have weight such as length, cost or another measuring instrument for
recording the graph. Vertices on the graph are shown as point or circles and edges
are drawn as arcs or line segment. Thus an edge can be representing as E (V, W)
where V and W are pair of vertices. The vertices V and W lie on E Vertices may be
considered as cities and edges, arcs, line segment as roads.

(a)Undirected graph (b) Directed and Weighted graph.

Types of Graph - The graphs are classified in the following categories: -

1. Simple Graph
2. Directed Graph
3. Non-directed Graph
4. Connected Graph
5. Non-connected Graph
6. Multi-Graph

The most commonly used operations on Primitive and Non-primitive data


structures are broadly categorized into four types.

1. CREATE - This operation results in reserving memory for the program elements.
This can be done by declaration statement. The creation of data structure may take
place either during compile time or during run time.

2. SELECTION- This operation deals with accessing a particular data within a data
structure.

3. DESTROY or DELETE - This operation destroys the memory space allocated for the
specified data structure. Malloc() and free() function of C language are used for
these two operations respectively.

4. UPDATION - As the name implies this operation updates or modifies the data in the
data structure. Probably new data may be entered or previously stored data may be
deleted.

Operation On Data Structure

Other operations performed on data structure includes -:

1. SEARCHING – Searching operation finds the presence of the desired data item in
the list of data item. It may also find the locations of all elements that satisfy certain
conditions.

2. SORTING – Sorting is the process of arranging all data items in a data structure in
a particular order say for example, either in ascending order or in descending order.

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 4


3. MERGING - Merging is a process of combining the data items of two different sorted
lists into a single sorted list.

Algorithm preliminaries

An algorithm named after ninth century is defined as - An algorithm is a set of rules


for carrying out calculations either by hand or on a machine. It is a sequence of
computational steps that transform the input into the output or a sequence of
operations performed on data that have to be organized in data structures. We can
also say that algorithm is an obstruction of a program to be executed on a physical
machine.

Algorithm + Data structure = Program

An algorithm is a step-by-step procedure to solve a particular function. That is, it is


a set of instructions written to carry out certain tasks and the data structure is the
way of organizing the data with their logical relationship retained. To develop a
program of an algorithm, we should select an appropriate data structure for that
algorithm. Therefore algorithm and its associated data structures form a program.

Algorithm is a branch in Computer Science that consists of designing and analyzing


computer algorithms:

1. The “design” pertains to :

(i) The description of algorithm at an abstract level by means of a pseudo language


(ii) Proof of correctness that is, the algorithm solves the given problem in all cases.

2. The “analysis” deals with performance evaluation (complexity analysis).

Algorithm is of 2 different types:

1. Incremental Algorithm 2. Divide and Conquer Algorithm

Time and Space analysis of Algorithms

Program Design

There are various ways by which we can specify an program design. Those are -

1. Use of Pseudocode
2. Representation of Flowchart

Pseudocode Definition: Pseudo code is nothing but an informal way of writing a


Program. It is a combination of algorithm written in English and some programming
language. There is no restriction of following syntax of programming language.
Pseudo code cannot be compiled; it is just a previous step of developing a code in
algorithm.

Flowchart - Flowcharts are the graphical representation of the algorithms. The


algorithms and flowcharts are the final steps in organizing the solutions. Using the
algorithms and flowcharts the programmers can find out the bugs in the

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 5


programming logic and then can go for coding. Flowcharts can show errors in the
logic and set of data can be easily tested using flowcharts.
Symbols used in Flowchart

Figure 11 symbol of flowchart

Figure 12 Flowchart of factorial


Performance Analysis

The efficiency of algorithm can be decided by measuring the performance of


algorithm. We can measure the performance of an algorithm by counting two
factors –

1. Amount of time required by the algorithm to execute. This is known as time


complexity.

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 6


2. Amount of space required by the algorithm to execute. This is known as space
complexity.

Suppose we want to find out the time taken by following program statement

x = x+1

Determining the amount of time required by the above statement in terms of clock
time is not possible because following is always dynamic

1. The machine that is used to execute the programming statement


2. Machine language instruction set
3. Time required by each machine instruction.
4. The translation of compiler will make for this statement to machine language.
5. The kind of operating system (multi—programming or time sharing)

The above information varies from machine to machine. Hence it is not possible to
find out the exact figure. Hence the performance of the machine is measured in
terms of frequency count.

Frequency Count and its Importance - Definition: The frequency count is a


count that denotes how many times particular statement is executed. For Example
Consider following code for counting the frequency count

void fun()
{
int a=10;
a++;
printf (“%d, a”);
} The frequency count of above program is 2.

Complexities

Space Complexity - The space complexity can be defined as amount of memory


required by an algorithm to run. To compute the space complexity we use two
factors: constant and instance characteristics. The space requirement S(p) can be
given as

S(P)=C+Sp

where C is a constant i.e. fixed part and it denotes the space of inputs and outputs.
This space is an amount of space taken by instruction, variables and identifiers.
And Sp is a space dependent upon instance characteristics. This is a variable part
whose space requirement depends on particular problem instance.

There are two types of components that contribute to the space complexity

1. Fixed part and


2. variable part.

The fixed part includes space for:-

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 7


1. Instructions 2. Variables 3. Array 4. Space for constants.

The variable part includes space for:-

1. The variables whose size is dependent upon the particular problem instance
being solved. The control statements (such as for, do, While, choice) are used to
solve such instance
2. Recursion stack for handling recursive call.

Time Complexity - The amount of time required by an algorithm to execute is


called the time complexity of that algorithm. For determining the time complexity of
particular algorithm following steps are carried out.

1. Identify the basic operation of the algorithm


2. Obtain the frequency count for this basic operation.
3. Consider the order of magnitude of the frequency count and express it in terms of
big oh notation.

Asymptotic Notations

To choose the best algorithm, we need to check efficiency of each algorithm. The
efficiency can be measured by computing time complexity of each algorithm.
Asymptotic notation is a shorthand way to represent the time complexity. Using
asymptotic notations we can give time complexity as ”fastest possible”, "slowest
possible” or ”average time”. Various notations such as Ω-Omega, θ-theta and O-Big
O used are called asymptotic notations.

1. Big oh Notation

The Big oh notation is denoted by “O”. It is a method of representing the upper


bound of algorithm's running time. Using big oh notation we can give longest
amount of time taken by the algorithm to complete.

Definition - Let F(n) and g(n) be two non-negative functions. Let n0 and constant c
are two integers such that n0 denotes some value of input and n > n0. Similarly c is
some constant such that c > 0. We can write F(n)<= c*g(n)

Figure 12 Big OH notation

Then F(n) is big oh of g(n). It is also


denoted as F(n) є O (g(n)). In other
words F(n) is less than g(n) if g(n) is
multiple of some constant c.

Example : Consider function F(n) =


2n + 2 and g(n) = n2 . Then we have
to find some constant c, so that F(n)<= c * g(n). As F(n) = 2n + 2 and g(n) = n2 then
we find c

for n = 1 then
F(n) = 2n+ 2
= 2(1) + 2

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 8


F(n) = 4
and g(n) = n2
= (1)2
g(n) = 1
i.e. F(n) > g(n) ‘

If n = 2 then
F(n) = 2(2) + 2
=6
g(n) = (2)2
g(n) = 4
i.e. F(n) > g(n)

If n = 3 then
F(n) = 2(3) + 2
=8
g(n) = (3)2
g(n) = 9

i.e. F(n) < g(n) is true.

Hence we can conclude that for n > 2, we obtain .


F(n) < gm)

Thus always upper bound of existing time is obtained by big oh notation.

2. Omega Notation

Omega notation is denoted by “Ω”. This notation is used to represent the lower
bound of algorithm's running time. Using omega notation we can denote shortest
amount of time taken by algorithm.

Definition - A function F(n) is said to be in Q (g(n)) if F(n) is bounded below by


some positive constant multiple of g(n) such that

F(n) => C * g(n) For all n >n0

It is denoted as F(n) є Ω (g(n)).


Following graph illustrates the curve
for Ω notation.

Figure 13 Omega notation F(n) є Ω (g(n))

Example: Consider F(n) = 2n2 + 5


and g(n) = 7n

Then if n = 0
F(n) = 2(0)2 + 5
g(n) = 7 (0)
=0
i.e. F(n) > g(n)

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 9


But if n = 1
F(n) = 2(1)2 + 5
. =7
g(n) = 7(1)
=7
i.e. F(n) = g(n)

If n = 3 then,
F(n) = 2(3)2 + 5
= 18 + 5
= 23
g(n) = 7(3)
= 21
i.e. F(n) > g(n)

Thus for n > 3 we get F(n) > c * g(n). It can be represented as 2n2 + 5 є Ω (n)

3. Θ Notation

The theta notation is denoted by Θ. By this method the running time is between
upper bound and lower bound.

Definition - Let F(n) and g(n) be two non negative functions. There are two positive
constants namely c1 and c2 such that: c1 g(n) <= F(n) <= c2 g(n)
Then we can say that : F(n) є θ (g(n))

Figure 14 Theta notation F(n) є θ (g(n))

Example: If F(n) = 2n + 8 and


g(n) = 7n.
where n => 2
Similarly F(n) = 2n + 8
g(n) = 7n
i.e. 5n < 2n + 8 < 7n
For n=>2 Here c1 = 5 and
c2 = 7 with n0 = 2.

The theta notation is more precise with both big oh and omega notation.

Order of Growth

If we have two algorithms that


perform same task and the first one
has a computing time of O(n) and
the second of O(n2), then we will
usually Prefer the first one. The
reason for this is that as n increases
the time required for the execution
of second algorithm far more than
the time required for the execution
of first.
Figure 15 order growth table

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 10


Time and space trade-off

Time space trade-off is basically a situation where either a space efficiency (memory
utilization) can be achieved at the cost of time or a time efficiency (performance
efficiency) can be achieved at the cost of memory.

Example 1 : Consider the programs like compilers in which symbol table is used to
handle the variables and constants. Now if entire symbol table is stored in the
program then the time required for searching or storing the variable in the symbol
table will be reduced but memory requirement will be more. On the other hand, if
we do not store the symbol table in the program and simply compute the table
entries then memory will be reduced but the processing time will be more.

Example 2 : Suppose, in a file, if we store the uncompressed data then reading the
data will be an efficient job but if the compressed data is stored then to read such
data more time will be required.

Example 3 : This is an example of reversing the order of the elements. That is, the
elements are stored in an ascending order and we want them in the descending
order. This can be done in two ways -

i. We will use another array b[ ] in which the elements in descending order can be
arranged by reading the array an in reverse direction. This approach will actually
increase the memory but time will be reduced.

ii. We will apply some extra logic for the same array all to arrange the elements in
descending order. This approach will actually reduce the memory but time of
execution will get increased.

Thus time space trade-off is a situation of compensating one performance measure


at the cost of another.

Pointer

Pointer is the variable which holds the address of another variable. It is Defined by
Symbol *. Pointer operator available in C is ‘*’, called ‘value at address’ operator. It
gives the value stored at a particular address. The ‘value at address’ operator is also
called ‘indirection’ operator. Consider the declaration, int i = 3 ;

This declaration tells the C compiler to:


(a) Reserve space in memory to hold the
integer value.
(b) Associate the name i with this memory
location.
(c) Store the value 3 at this location.

The other pointer operator available in C is ‘*’, called ‘value at address’ operator. It
gives the value stored at a particular address. The ‘value at address’ operator is also
called ‘indirection’ operator.

int *j;

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 11


The meaning of *. It stands for ‘value at address’. Thus, int *j would mean, the
value at the address contained in j is an int. Here is a program that demonstrates
the relationships we have been discussing.

main( )
{
int i = 3 ;
int *j ;
j = &i ;
printf ( "\nAddress of i = %u", &i ) ;
printf ( "\nAddress of i = %u", j ) ;
printf ( "\nAddress of j = %u", &j ) ;
printf ( "\nValue of j = %u", j ) ;
printf ( "\nValue of i = %d", i ) ;
printf ( "\nValue of i = %d", *( &i ) ) ;
printf ( "\nValue of i = %d", *j ) ;
}

Memory Allocations In C

There are two types of memory allocations possible in C

1. Compile-time or Static allocation.


2. Run-time or Dynamic allocation (using pointers).

In the first type of allocation (Compile-time or Static allocation.), the required


amount of memory is allocated to the program element (identifiers names which
include variables name, function name, program name etc.) at the start of the
program. Here the memory to be allocated to the variable is fixed and is determined
by the compiler at the compile time (if it is a single integer variable it allocates two
bytes to it, if it is an array of five integer values it allocates ten bytes to it and if it is
a single float type of variable compiler allocates four bytes to it.). For example,
consider the following declaration :

int x, y ;
float a[5] ;

when the first statement is encountered, the compiler will allocate two bytes to each
variables x and y. The second statement results into the allocation of 20 bytes to
the array a (5 *4), where there are five elements and each element of float type takes
four bytes).

First problem with this is that the extra elements added as a part of this array ”a”
are not allocated, the consecutive memory location after the five elements, i.e., only
the first five elements are stored in consecutive memory location and the other
elements are stored randomly at any unknown locations the memory. Thus during
accessing these extra elements would not be made available to user, only the first
five values can be accessible.

The second problem with static memory allocation is that if you store less number
of elements than the number of elements for which you have declared memory, then
the rest of the memory will be wasted. This leads to the inefficient use of memory.

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 12


The concept of Dynamic or Run-time memory allocation helps us to overcome this
problem in arrays, as well as allows us to be able to get the required chunk of
memory at run-time. This is best suited type of allocation where we do not know the
memory requirement in advance, which is the case with most of real-life problems.
In other words dynamic memory allocation gives flexibility for programmer. As well
as it makes efficient use of memory, by allocating the required amount of memory
whenever needed.

1. The Malloc ( ) Function

The Malloc ( ) function allocates a block of memory in bytes. The user should
explicitly give the block size it requires for the use. The Malloc ( ) function is like a
request to the RAM of the system to allocate memory, if the request is granted (i.e.,
the Malloc ( ) function stays successful in allocating memory), returns a pointer to
the first block of that memory. The type of the pointer it returns is void, which
means that we can assign it any type of pointer. However if the Malloc ( ) function
fails to allocate the required amount of memory, it returns a NULL. The Malloc ( )
function is available in header file alloc.h or stdlib.h in TURBO C. The syntax of this
function is as follows:

malloc (number of e1ements * size of each e1e_ment) ;


For example,
int *ptr
ptr = ma11oc (10 * sizeof(int))

Where size represents the size of memory required in bytes (i.e., number of
contiguous memory locations to be allocated). But as already told that the function
Malloc ( ) returns a void pointer so a cast operator is required to change the
returned pointer type according to our need, the above declaration would take the
following form :
Ptr_var = (type_cast *) ma11oc (size)

Where ptr_ var is the name of pointer that holds the starting address of allocated
memory block, type_ cast is the data type into which the returned pointer (or type
void) is to be converted, and size specifies the size of allocated memory block in
bytes. Example
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define NULL 0
void main( )
{
int *ptr;
int i, n, sum=0;
float avg ;
print(“Enter the number of elements you want to store in the array”) ;
scan(“%d", &n) ;
ptr = (int *) malloc(sizeof(int)); /* Dynamic memory allocation */
if (ptr = =NULL) /* checking if request granted or not */
{

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 13


printf (“The required amount of memory is not available") ;
getch();
exit(0);
}
else
{
printf( “Enter the elements\n") ;
for (i = 0 : i < n ; i++)
scanf (”%d", prt+i);
for(i=0; i<n; i++)
{
sum = sum + (*(ptr + i )) ; /* Finding Sum of array elements */
}
printf(“$um of %d elements of array is= %d", n, sum) ;
avg = sum/n ; /* Finding Average of array e1ement */
printf (“The average of %d number of the array is %f", n, avg) ;
}
getch();
}

The output of the above program will be


Enter the number of elements you want to store in the array 5
Enter the element
22
34
1
45
6
Sum of elements of array is = 108
The average of 5 number of the array is 21.6

2. The Calloc( ) Function

This function works exactly similar to malloc( ) function except for the fact that it
needs two arguments as against one argument required by malloc( ).

For example,
int *ptr;
ptr = (int *) calloc (10 ,2);

Here 2 specifies the size of data type in byte tor which we want the allocation to be
made, which in this case is 2 for integers. And 10 spec1fy the number of elements
for which is to be made. Argument passed to the function malloc was (n * 10), it is a
single argument (don’t be confused) because multiple arguments are always
separated by commas. The argument (n *10) has no commas in between hence it is
a single argument.

After the execution of the above statement a memory block of 20 bytes is allocated
to the requesting program and the address of first block is assigned to pointer ptr.
Another minor difference is that if the memory allocated by malloc( ) function
contains garbage values, while memory allocated is by calloc( ) function contains all

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 14


zeros. The calloc( ) function is also available in the header file <stdlib.h> or
<alloc.h> in TURBO C.

3. The Free( ) Function

The free( ) function is used to de-allocate the previously allocated memory using
malloc( ) or calloc( ) functions. The syntax of this function is : free (ptr_var) ;

Where ptr__var is the pointer in which the address of the allocated memory block is
assigned. The free function is used to return the allocated memory to the system
RAM.

4. The Realloc( ) Function

This function is used to resize the size of memory block, which is already allocated.
It found use of in two situations: -

(i) If the allocated memory block is insufficient for current application.


(ii) If the allocated memory is much more than what is required by the current
application.

The syntax of this function is as follows :


ptr_var = realloc (ptr_var, new_size) ;

Where ptr_var is the pointer holding the starting address of already allocated
memory block; and new_size is the size in bytes you want the system to allocate
now, it may be smaller than the size of previously allocated memory block or may
be greater than the size of previously allocated memory block depending upon the
requirement. This function is also available in the header file <stdlib.h>. Example of
functions free( ) and malloc( ).

#inc1ude<stdio.h>
#include<conio.h>
#inc1ude<string.h>
void main( )
{
char *msg ;
msg = (char *) malloc(30 * sizeof(char));
strcopy (msg, “Hello”) ;
printf (“The message now is %s \n", msg) ;
msg = (char *) relloc (msg, 50) ;
strcopy(msg, “good morning. . . .") ;
printf ( “\nThe message is now %s", msg) ;
free(msg) ;
getch( ) ;
}

The output of the above program is:

The message is now Hello


The message Is now good morning

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 15


ARRAYS CONCEPTS

An array most commonly used non-primitive data structures. It is defined as a set of


finite number of homogeneous elements or data items. Or we can say that an array is
a collection of similar elements. These similar elements could be all ints, or all
floats, or all chars, etc. Usually, the array of characters is called a ‘string’, whereas
an array of ints or floats is called simply an array It means an array can contain
one type of data only, either all integers, all floating-point numbers, or all characters.
Declaration of arrays is as follows :

int a[10] ;
int num[6] = { 2, 4, 12, 5, 45, 5 } ;
int n[ ] = { 2, 4, 12, 5, 45, 5 } ;

Where int specifies the data type or type of elements array stores. ”a” is the name of
array, and the number specified inside the square brackets is the number of
elements an array can store this is also called size or length of array.

Following are some of the concepts to be remembered about arrays:

1. The individual element of an array can be accessed by specifying name of the array
followed by index or subscript inside square brackets. For. example to access fifth
element of array a, we have to give the following statement : a[4]

2. The first element of the array has index zero [0]. It means the first element and last
element will be specified as: a[0] and a[9] respectively.

3. The elements of array will always be stored in consecutive memory locations.

4. The number of elements that can be stored in an array i.e, the size of an array or its
length is given by the following equation: - (upper bound – lower bound) + 1.

5. Arrays can always be read or written through loop. If we read-a one-dimensional


array, it requires one loop for reading and other for writing (printing) the array, for
example :

(a) For reading an array


for (i =0; i<=9; i++)
{ scanf (“%d", &a[i]) };

(b) For writing an array


for(i=0; i<=9; i++)
{ Printf(“%d", a[i] } ;

Some common operations performed on arrays are :

1. Creation of an array.
2. Traversing an array (accessing array elements)
3. Insertion of new elements.
4. Deletion of required elements.
5. Modification of an element.
6. Merging of arrays.

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 16


A program of array to find average marks.
main( )
{
int avg, sum = 0 ; int i ; int marks[30] ; /* array declaration */
clrscr();
for ( i = 0 ; i <= 29 ; i++ )
{ printf ( "\nEnter marks " ) ;
scanf ( "%d", &marks[i] ) ; /* store data in array */
}
for ( i = 0 ; i <= 29 ; i++ )
sum = sum + marks[i] ; /* read data from an array*/
avg = sum / 30 ; printf ( "\nAverage marks = %d", avg ) ;
getch();
}

Array elements can be passed to a function by calling the function by value, or by


reference. In the call by value we pass values of array elements to the function,
whereas in the call by reference we pass addresses of array elements to the function.
We can also use Pointer in array. Array elements are always stored in contiguous
memory locations. A pointer when incremented always points to an immediately next
location of its type.

Types of Array

1. One dimension Array


2. Two dimension Array
3. Three dimension Array

One dimension Array

Above details are of 1-D array. Declaration of one dimension arrays is as follows :

int a[10] ;
int num[6] = { 2, 4, 12, 5, 45, 5 } ;
int n[ ] = { 2, 4, 12, 5, 45, 5 } ;

Two dimension Array

It is also possible for arrays to have two or more dimensions. The two-dimensional
array is also called a matrix. A sample program that stores roll number and marks
obtained by a student side by side in a matrix.

main( )
{
int stud[4][2] ; int i, j ;
clrscr();
for ( i = 0 ; i <= 3 ; i++ )
{
printf ( "\n Enter roll no. and marks" ) ;
scanf ( "%d %d", &stud[i][0], &stud[i][1] ) ;
}
for ( i = 0 ; i <= 3 ; i++ )

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 17


printf ( "\n%d %d", stud[i][0], stud[i][1] ) ;
getch();
}

There are two parts to the program—in the first part through a for loop we read in
the values of roll no. and marks, whereas, in second part through another for loop
we print out these values.

Look at the scanf( ) statement used in the first for loop:


scanf ( "%d %d", &stud[i][0], &stud[i][1] ) ;

In stud[i][0] and stud[i][1] the first subscript of the variable stud, is row number
which changes for every student. The second subscript tells which of the two
columns are we talking about—the zeroth column which contains the roll no. or the
first column which contains the marks. Remember the counting of rows and
columns begin with zero. The complete array arrangement is shown below.

Thus, 1234 is stored in stud[0][0], 56 is


stored in stud[0][1] and so on. The above
arrangement highlights the fact that a two-
dimensional array is nothing but a
collection of a number of one- dimensional
arrays placed one below the other.

Figure 16. 2-D array

In our sample program the array elements have been stored rowwise and accessed
rowwise. However, you can access the array elements columnwise as well.
Traditionally, the array elements are being stored and accessed rowwise; therefore we
would also stick to the same strategy. Initialising a 2-Dimensional Array is done as

int stud[4][2] = { { 1234, 56 }, { 1212, 33 }, { 1434, 80 }, { 1312, 78 } } ;

or even this would work...

int stud[4][2] = { 1234, 56, 1212, 33, 1434, 80, 1312, 78 } ;

Three dimension Array

A three-dimensional array can be thought of as an array of arrays of arrays. The


outer array has three elements, each of which is a two-dimensional array of four one-
dimensional arrays, each of which
contains two integers. In other
words, a one-dimensional array of
two elements is constructed first.
Then four such one-dimensional
arrays are placed one below the
other to give a two-dimensional
array containing four rows. Then,
three such two-dimensional arrays
are placed one behind the other to
yield a three-dimensional array Figure 17. 3-D array

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 18


containing three 2-dimensional arrays. In the array declaration note how the
commas have been given. Figure 2 would possibly help you in visualising the
situation better. In practice, 3-D array is rarely used. An example of initializing a
three-dimensional array for understanding is shown:

int arr[3][4][2] = { { { 2, 4 }, { 7, 8 }, { 3, 4 }, { 5, 6 } }, { { 7, 6 }, { 3, 4 }, { 5, 3 }, { 2, 3
} }, { { 8, 9 }, { 7, 2 }, { 3, 4 }, { 5, 1 }, } } ;

Passing Arrays as a parameter to Function

Arrays like other simple variables can be passed to function. To pass, Its name is
written inside the argument list in the call statement. Arrays are by default passed to
function by Call by reference method because array name is itself ,a pointer to the
first memory location of the array. However we can pass individual array elements
through call by value method also. The following program illustrates, how to pass
array to the functions.

#include<stdio.h>
#include<conio.h>
int funarray(int[ ], int) ;
main( )
{
int a[10], i, sum = 0;
clrscr( );
printf(“Enter the array") ;
for (i=0; i<=9; i++)
{
scanf("%d". &a[i]);
}
sum = funarray(a, 9) ;
printf (“The sum of array elements is %d", sum) ;
getch( );
}
int funarray (int p[ ], int n)
{
int s == 0, i;
for (i=0; i<=n-1; i++)
{
s = s + p[i];
}
return(s) ;
}

When passing array to any function, the array name is used as an argument for the
function declaration.

Insertion In One-Dimensional Array

Let we have an array


a[6] = {1, 5, 7, 22, 6, 90} ;

Insertion of a new element in an array can be done in two ways :

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 19


(i) Insertion at the end of array.
(ii) Insertion at required position.

Inserting an element at the end of an array can be easily done provided the memory
space allocated for the array is large enough to accommodate the additional element.

Figure 18. Inserting element in an array

For inserting the element at required position, elements must be moved downwards
to new locations. To accommodate the new element and keep the order of the
elements.

Suppose we want to insert 20 in array a, at location with index 4, it means the


elements 22 onwards must be shifted downwards as shown in figure 5. For inserting
an element into a linear array insert (a, len, pos, num) where a is linear array len be
total no of elements with in array pos is the position at which number man will be
inserted.

Algorithm
1. [ Initialization the venue of i ] Set i = len
2. Repeat for i = len down to pos
[shift the e1ements down by 1 position]
Set a [i+1] = a [i]
[End of loop]
3. [Insert the element at required position]
set a[pos] = num
4. [Reset len] Set len = len + 1
5. Display the new list of arrays
6. End
To insert an element in an array.

#inc1ude<stdio.h>
#inc1ude<conio.h>

int i, 1en, pos, num;


main( ) ;

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 20


{
int a[100] ;
void insert ( int a[ ], int, int, int) ;
c1rscr( ) ;
printf("Enter integers to be read") ;
scanf(“%d”, &len) ;
printf (“Enter integers”) ;
for (i = 0; i <= 1en-1 ; i++)
{
Scanf(“%d”, &a[i]) ;
}
Printf (“Enter integer to be inserted") ;
Scanf ("%d", &num) ;
print("Enter position in the array for insertion”) ;
scanf ("%d", &pos)
--pos ;
insert (a, len, pos, num);
}
void insert (int a[ ], int 1en, int pos, int num)
{
for (i =1en; i >= pos ; i--)
{
a[i +1] = a[i] ; /* shifts down by 1 position */
}
a[pos] = num ;
if (pos > len)
{
printf(“insertion outside the array”) ;
}
1en++ ; .
printf (“New array") ;
for(i =0 ; i <1en ; i++)
{
printf (“%d\n”, a[i]) ;
}
}

Deletion Of An Element From One-Dimensional Array

For deleting an element from the array, the logic is straight forward. Deleting an
element at the end of an array presents no difficulties, but deleting element
somewhere in the middle of the array would require to shift all the elements to fill the
space emptied by the deletion of the element a[5] = 90, then the elements following it
were moved upward by one location as shown in figure 19.
Algorithm for deletion of an element from the array

The following algorithm deletes the element stored at position pos from a linear array
a and assign it to a variable item. Delete (a, pos, n) where n be the length of linear
array

Algorithm
1. Set item = a[pos]

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 21


2. Repeat for j = pos to n - 1
[shifting elements 1 position upwards]
Set a [j] = a [j+ 1] [End of loop]
3. Reset n = n-1
4. Display the new list of element of arrays.
5. End

Figure 19. Deleting an alumni from one-dimensional array.

Program to delete an element in an array.

#inc1ude<stdio.h>
#inc1ude<conio.h>
int i, n ;
main( )
{
int a[100], pos;
void del (int a[ ], int, int) :
clrscr ( ) :
printf (“How many e1ements in the array\n") ;
scanf (“%d", &n) ;
printf ("Enter the element of the array\n") ;
for (i=0; i <=n-1; i++)
scanf ("%d", &a[i]) ;
printf ("0n which position e1ement do you want delete\n") :
scanf ("%d", &pos) ;
del (a, pos, n) ;
getch( );
}
void del (int a[ ], int pos, int n)
{
int j, item ;
item = a [pos];
for (j=pos; j<=n-1; j++)
{
a[j] = a [j+1];
}
N=n-1;
Printf ("New array\n") :
Prepared By: Prof. Dheresh Soni SRHU - HSET Page 22
for (i=0 ; i <=n-1; i++)
printf(“%d\n", a[i]) ;
}

Traversing Of An Array

Traversing means to access all the elements of the array, starting from first element
(Upper bond) last element in the array one-by-one.

Algorithm
Let LB be the lower bound and UB be the upper bound of linear array a.
1. [Initialize counter] set 1 at 1ower bound LB
2. Repeat for i = LB to UB
[visit element] Disp1ay a[ i ]
[End of the loop]
3. Exit

Program to traverse an array.

#<inc1ude<stdio.h>
#<inc1ude<conio.h>
void main( )
{
int n, 1, a[10] ;
clrscr( );
printf (“Enter the length of the array") ;
scanf (“%d", &n) ;
printf (“Enter the elements of the array”) ;
for(i=0; i<=n-1; i++)
{
scanf (“%d\n”, &a[ i ] );
}
printf (“Traversing of the array") ;
for (i=0: i<=n-1; i++)
printf (“\n%d", a[ i ]) ;
getch ( );
}

IMPLEMENTATION OF A TWO-DIMENSIONAL ARRAY

A two-dimensional array can be implemented in a programming language in two


ways:

1. Row-major implementation - Row-major implementation is a linearization


technique in which elements of array are reader from the keyboard row-wise i.e, the
complete first row is stored, then
the complete second row is stored
and so on. For example, an array
a[3][3] is stored m the memory as
shown in Fig. 20 below: Figure 20

The storage can be clearly understood by arranging array as matrix as shown below :

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 23


Above figure shows the actual physical storage of
elements of the array, whereas the matrix form is
logical representation of array (and is not the actual
way in which two-dimension array is stored).

Address of elements in Row major implementation

The computer does not keep the track of all elements of the array, rather, it keeps a
base address (i.e. the address of first element in the array), and calculates the
address of required element when needed. It calculates this (in row major
implementation) by the following relation :

Address of element a[i][j] = B + W(n(i - L1) + (j – L2))

Where B is the base address of the array, W is size of each array element, n is the
number of columns (i.e., U2 — L2). L1 the lower bound of row, L2 is lower bound of
column. An example to get a clear idea of row major implementation:

Important: Usually number of rows and columns of a matrix are given (like A[20][30]
or A[40][60] ) but if it is given as A[L1.......U1, L2........U2]. In this case number of
rows and columns are calculated using the following methods:
Number of rows (M) will be calculated as = (U1 – L1) + 1
Number of columns (N) will be calculated as = (U2 – L2) + 1
And rest of the process will remain same as per requirement (Row Major Wise (U1 –
L1) or Column Major Wise (U2 – L2)).

Example: A two-dimensional array defined as a[4.. 7, -1.. 3] requires 2 bytes of


storage space for each element. If the array is stored in row-major form, then
calculate the address of element at location a[6, 2]. Give that the base address
is 100.

SOLUTION. The following information is given :

Base address B = 100


Size of each element in the array W = 2 bytes
Lower bound of row L1 = 4
Lower bound of column L2 = -1
Upper bound of row U1 = 7
Upper bound of column U2 = 3
Row number of the required element i = 6
Column number of required element i = 2

Note: Normally we for n we do U2 – L2, but here we do


Now the number of columns n will be: U2 - L2 + 1 = 3- (-1) + 1 = 5
As the address of a particular, element in a two-dimensional array (in row major
implementation) is given by the relation:

Address of a [i][j] = B + W ( n ( i – L1) + ( j - L2))

Therefore address of element a[6, 2] or a[6][2] will be :

Address of a [6][2] = 100 + 2(5(6 - 4) + (2 - (- 1) ) )

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 24


=100+2(5*2 +3)
=100 + 26
= 126.

2. Column-major implementation - In column major implementation memory


allocation is done column by column i.e., first the elements of the complete first
column is stored, then elements of complete second column is stored column is
stored and so on. For
example an array a[3][3] is
stored' in the memory as
shown in the Fig. 21 below:

Address of element in Column major implementation

In Column major implementation address of an element a[ i ][ j ] is given by the


relation:

Address of a [i][j] = B + W ( m ( j – L2) + ( i – L1))

Where m is the number of rows (i.e. U1—L1).

Important: Usually number of rows and columns of a matrix are given (like A[20][30]
or A[40][60] ) but if it is given as A[L1.......U1, L2........U2]. In this case number of
rows and columns are calculated using the following methods:

Number of rows (M) will be calculated as = (U1 – L1) + 1


Number of columns (N) will be calculated as = (U2 – L2) + 1
And rest of the process will remain same as per requirement (Row Major Wise (U1 –
L1) or Column Major Wise (U2 – L2)).

Example: Each element of an array a[-20......20, 10.........35] requires one byte


of storage If the array is column major implemented, and the beginning of the
array is at location 500 (base address), determine the address of element a[0,
30] or a[0][30].

SOLUTION. The following information is given .


Base address B = 500
Size of each element W = 1 byte
Lower bound of row L1 = -20
Lower bound of Column L2= 10
Upper bound of row U1 = 20
Upper bound of column U2 = 35
Row number of the required element i = 0
Column number of required element j = 30

Note: Normally we for m we do U1 – L1, but here we do


Number of rows m for this configuration will be: U1 – L1 + 1 = 20 - (-20) + 1 = 41

As address of a particular element in two—dimensional array (column major


implemented) is given by the relation :

Address of a[ i ][j ] = B + W(m(j- L2) + (i – L1))

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 25


Therefore address of element a[0][30] will be .

Address of a[0][30] = 500 + 1 (41(30 - 10) + (0 - (-20)


= 500 + 1(41 * 20 + 20)
= 500 + 1 (820 + 20)
= 500 + 840'
= 1340

Sparse Matrix

A matrix is a two-dimensional data object made of m rows and n columns, therefore


having total m x n values. If most of the elements of the matrix have 0 value, then it
is called a sparse matrix. We use Sparse Matrix instead of simple matrix.

1. Storage: There are lesser non-zero elements than zeros and thus lesser memory
can be used to store only those elements.
2. Computing time: Computing time can be saved by logically designing a data
structure traversing only non-zero elements. Example:
00304
00570
00000
02600

Representing a sparse matrix by a 2D array leads to wastage of lots of memory as


zeroes in the matrix are of no use in most of the cases. So, instead of storing zeroes
with non-zero elements, we only store non-zero elements. This means storing non-
zero elements with triples- (Row, Column, value). Sparse Matrix Representations can
be done in many ways following are two common representations:

1. Array representation
2. Linked list representation

Method 1: Using Arrays - 2D array is used to represent a sparse matrix in which


there are three rows named as

 Row: Index of row, where non-zero element is located


 Column: Index of column, where non-zero element is located
 Value: Value of the non zero element located at index – (row,column)

Method 2: Using Linked Lists - In linked list, each node has four fields. These four
fields are defined as:

 Row: Index of row, where non-zero element is located


 Column: Index of column, where non-zero element is located
 Value: Value of the non zero element located at index – (row,column)

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 26


 Next node: Address of the next node

Other representations: - As a Dictionary where row and column numbers are used
as keys and values are matrix entries. This method saves space but sequential
access of items is costly. As a list of list. The idea is to make a list of rows and every
item of list contains values. We can keep list items sorted by column numbers.

Ordered List

An ordered list is a list where the items in the list are held in some kind of sorted
order. The criterion that the items are sorted by is usually called the key. The
ordered list can be accessed at arbitrary locations within the structure, either for
lookup or for deletion. Ordered list is the most basic of the searchable container. A
searchable container supports the following additional operations:

1. Initialise - It is used to give first value to objects into a the container;


2. Add - It is used to add objects into a the container;
3. Delete - It is used to delete objects into a the container;
4. Search - It is used to find objects into a the container;
5. isFull - It is used to test whether a container is full or not.
6. isEmpty - It is used to test whether a given container in empty or not.
7. Insert – It is used to put objects into a the container;
8. Withdraw - It is used to remove objects from the container;
9. Find - It is used to locate objects in the container; and,
10. IsMember - It is used to test whether a given object instance is in the container.

An ordered list is a container which holds a sequence of objects. Each object has a
unique position in the sequence. In addition to the basic selection of operations
supported by all searchable containers, ordered lists provide the following
operations:

1. FindPosition - It is used to find the position of an object in the ordered list;


2. operator [ ] - It is used to access the object at a given position in the ordered list;
3. Withdraw (Position &) - It is used to remove the object at a given position from
the ordered list.
4. InsertAfter - It is used to insert an object into the ordered list after the object at a
given position; and
5. InsertBefore - It is used to insert an object into the ordered list before the object
at a given position.
In many aspects the ordered list when implemented by array is very similar to the
constructs that we have seen already.

Prepared By: Prof. Dheresh Soni SRHU - HSET Page 27

You might also like