Você está na página 1de 69

DATA STRUCTURES

Introduction & Definition


Data may be organized in many different ways; the logical or mathematical model of a particular
organization of data is called Data Structure. Data is organized in some way so we can do the operations on
these data in effective way. A Data Structure is a study of different methods of organizing the data in
memory and possible operations on these structures and devise algorithms for these operations.
Data Structure = { Data items + Storage methods + Algorithm }
The study of Data Structure includes following three steps:
1. Logical or mathematical description of a structure.
2. Implementation of structure on computer
3. Quantitative analysis of structure, which includes determining amount of memory needed to store the
structure and time required to process the structure.
Examples or Data Structures are:
Arrays, Linked List, Stack, Queue, Tree, Graph, etc.

Classification of Data Structures


Data structures are normally divided into two broad categories:
1. Primitive Data Structures:
These are basic structures and are directly operated upon by machine instructions. These have
different representations on different computers.
Example: int, float, char
2. 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 item i.e. arrays, lists, trees, graphs and files.

Data Structures

Primitive Non-primitive

Linear Non-linear
in float cha
t r
arrays lists trees graph
s

Data Structure Operations:


The data appearing in our data structures are processed by means of certain operations. In fact, the particular
data structure that one chooses for a given situation depends largely on the frequency with which specific
operations are performed.
The most commonly used operations on data structure are:
1. Traversing: Accessing each record or element exactly once so that certain items in the record may
be processed. (This accessing and processing is sometimes called visiting the record.)
2. Inserting: Adding a new record to the data structure.
3. Deleting: Removing a record from the data structure.
4. Searching: Finding the location of the record with a given key value, or finding the locations of all
records (data items) which satisfy one or more conditions.
Sometimes two or more of the operations may be used in a given situation. E.g. we may want to delete the
record with a given key, which may mean we first need to search for the location of the record.
1
The following two operations, which are used in special situations, will also be considered.
1. Sorting: Arranging the records in some logical order (alphabetically or numerically)
2. Merging: Combining the records in two different sorted files into a single sorted file.

Abstract Data Type (ADT):


We do only some specific operations with data structures. Data structures with these specific operations are
called ADT. This is basically user defined data type. Here user defines the data structure for organizing his
specific data and provides set of operations to handle this data structure. When we come to write larger
programs we want to encapsulate some functions together with a data type in order to control the access to
the elements of the data type and it is the place where ADT come into play.
ADT is a user defined data type in which data and basic set of operations on the data are bundled together
under one name.
In ADT, we do not need to know how the operations are performed and only thing we need to know is how
we feed and extract data from outside. Thus the process of wrapping functionality up in a box and forgetting
about its internals is called abstraction. We can simply take the abstract data type of list.

List ADT:
Component:
Item

Operations:
Insertion
Deletion
Search
Traverse
Here Item is the component of abstract data type. List can contain no.of items. Operations on these items can
be insertion, deletion, search, and traverse.

Thus ADT is a tool for specifying the logical problems of a data type. It is a mathematical concept not
concerned with space and time efficiency. ADT has two parts for definition.
i. value definition
ii. operator definition

Value definition: Defines the collection of values with the


Definition clause
and
Condition clause (optional clause)
Operator definition: Defines the operations to be performed.
Ex:
ADT RATIONAL:

RATIONAL: i.e., represent a rational with two integers second of which is not equal to 0.

/* value definition */
abstract typedef <integer, integer> RATIONAL,
condition RATIONAL[1] !=0

/* Operator definition */
abstract RATIONAL make rational(a,b)
int a,b;
Pre Condition b!=0;
Post Condition makerational[0] = = a;
makerational[1] = = b;

2
Abstract RATIONAL add(a,b)
RATIONAL a,b;
Post Condition add[1] = = a[1].b[1];
add[0] = = a[0] .b[1] + b[0] . a[1];

So post condition specifies what the operator does & precondition specifies the compulsory condition.

ALGORITHMS:
Difinition: An algorithm is a finite sequence of instructions each of which has a clear meaning and can be
performed with a finite amount of effort in a finite length of time.

Features of an algorithm:
1. Finiteness: The algorithm must terminate after a finite no.of steps.
2. Definiteness: Each step of an algorithm must be precisely definied i.e. the actions to be performed
rigorously and unambiguously specified for ease i.e. there should not be any action such as x 10.
3. Input: Zero or more inputs taken from specified set of objects, which is undetermined.
4. Output: Quantities to be specified related to inputs.
5. Effectiveness: All of the operations be performed in the algorithm in a finite length of time by a man
using pencil and paper.

ARRAYS
Linear Arrays (One dimensional arrays):
One dimensional array may be defined abstractly as a finite ordered set of homogeneous elements. By
finite we mean that there is a specific no.of elements in the array. This number may be large or small, but
it must exist. By ordered we mean that the elements of the array are arranged so that there is a zeroth
element, first element, second element, third element, and so forth . By homogenous we mean that all the
elements in the array must be of the same type. For example, an array may contain all integers or all
characters but may not contain both.
The C declaration of an array:
Syntax:
<storage class> <datatype> <array name> [<size>] ;
Example:
static int A[10] ;
auto int B[10] ;
or
int B[10];
The elements of an array A may be denoted by the subscript notation
A[0], A[1], A[2], . . . , A[N-1] where N is the size of the array.
Regardless of the notation, the number I in A[I] is called a subscript or an index and A[I] is called a
subscripted variable. Note that subscripts allow any element of A to be referenced by its relative position in
A.
In general, the length or the no.of data elements of the array, called its range can be obtained from the index
set by the formula
Length or size = UB LB + 1
Where UB is the largest index, called the Upper Bound, and LB is the smallest index, called the Lower
Bound of the array.

Representation of Linear Arrays in Memory:


Let A be a linear array in the memory of the computer. The memory of the computer is simply a sequence of
addressed locations as follows:

100 101 102 103 104 105 106 107

3
Computer Memory

Let us use the notation

LOC (A[I]) = address of the element A[I] of the array A

As we know, the elements of A are stored in successive memory cells. Accordingly,the computer does not
need to keep track of the address of every element of A, but needs to keep track only of the address of the
first element of A, denoted by

Base(A)

and called the base address of A. Using this address, the computer calculates the address of any element of A
by the following formula:
LOC(A[I]) = Base (A) + ESIZE * (I- LB)

Where, ESIZE is the amount of memory space occupied by an individual element (single element) of the
array A.
The time to calculate LOC(A[I]) is essentially the same for any value of I. Further more, given any subscript
I, one can locate and access the content of A[I] without scanning any other element of A.
Example:
Memory representation of
int A[5] ={10,20,30,40,50};

100 102 104 106 108


10 20 30 40 50

Here LB = 0, UB = 4 and let the size of each element ESIZE = 2 bytes

Then address of the element present at subscript 3 can be calculated as


LOC (A[I]) = Base (A) + ESIZE * (I-LB)

LOC (A[3]) = 100 + 2 * (3 0)


= 106

Operations on Array:
The two basic operations that access an array are extraction and storing. The extraction operation is a
function that accepts an array A and an index i and returns an element of the array. In C, the result of this
operation is denoted by the expression A[i]. The storing operation accepts an array A , an index i and an
element x. In C this operation is denoted by the assignment statement A[i]= x. The operations are defined
by the rule that after the foregoing assignment statement has been executed, the value of A[i] is x. Before a
value has been assigned to an element of the array, its value is undefined and a reference to it in an
expression is illegal.
Various operations can be performed on Arrays:
i. Traversing
ii. Inserting
iii. Deleting,
iv. Searching
v. Sorting
vi. Merging

4
Two Dimensional Arrays
A two-dimensional m x n array A is a collection of m . n data elements such that each element is specified
by a pair of integers (such as I, J), called subscripts, with the property that
0 < I < m and 0 < J < n
The element of A with first subscript I and second subscript J will be denoted by
A[I][J]
Two-dimensional arrays are called matrices in mathematics and tables in business applications; hence two-
dimensional arrays are sometimes called matrix arrays.
There is a standard way of drawing a tow-dimensional m x n array A where the elements of A form a
rectangular array with m rows and n columns and where the element A[I][J] appears in row I and column J.
(A row is a horizontal list of elements, and a column is a vertical list of elements.)

COLUMNS
0 1 2 3
0 A[ 0 ][ 0 ] A[ 0][1] A[ 0 ][ 2] A[0][3]
ROWS 1 A[1][0] A[1][1] A[1][2] A[1][3]
2 A[2][0] A[2][1] A[2][2] A[ 2][3]

Two-Dimensional 3 X 4 Array A

Suppose A is a two-dimensional m x n array. The first dimension of A contains the index set 0, 1, 2, . . . , m,
with lower bound 0 and upper bound m; and the second dimension of A contains the index set 0, 1, 2, . . ., n,
with lower bound 0 and upper bound n. The length of a dimension is the number of integers in its index set.
The pair of lengths m x n (read m by n) is called the size of the array.
The length of a given dimension can be obtained from the formula
Length = upper bound - lower bound + 1
Representation of Two-Dimensional Arrays in Memory
Let A be a two-dimensional m x n array. Although A is pictured as a rectangular array of elements with m
rows and n columns, the array will be represented in memory by a block of m . n sequential memory
locations. Specifically, the programming language will store the array A either
1. row by row, called row-major order or
2. column by column, called column-major order

Ex:

0,0 0,1 0,2 0,3 1,0 1,1 1,2 1,3 2,0 2,1 2,2 2,3

Row 0 Row 1 Row 2


Row-major order

0,0 1,0 2,0 0,1 1,1 2,1 0,2 1,2 2,2 0,3 1,3 2,3

Column 0 Column 1 Column 2 Column 3


column-major order
The computer keeps track of Base(A) the address of the first element A[0][0] of A and computes the
address LOC (A[I][J]) of A[I][J] using the formula

Row major order LOC(A[I][J]) = Base(A) + ESIZE [ n ( I RLB ) + ( J CLB) ]

Or the formula
5
Column major order LOC(A[I][J]) = Base(A) + ESIZE [ m ( J CLB ) + ( I RLB)]

Where,
ESIZE- denotes the size of each element i.e number of words per memory location for the array A.
RLB - denotes the Row Lower Bound
CLB - denotes the Column Lower Bound
m - is the no.of rows and
n - is the no.of columns of the array A.

Matrix Operations
1. Addition
2. Transpose
3. Multiplication, etc

Sparse Matrices:
Matrices with a relatively high proportion of zero entries are called sparse matrices. Two general types of n-
square sparse matrices, which occur in various applications, are given below. The first matrix, where all
entries above the main diagonal are zero or, equivalently, where nonzero entries can only occur on or below
the main diagonal, is called a (lower) triangular matrix. The second matrix, where nonzero entries can only
occur on the diagonal or on elements immediately above or below the diagonal, is called a tridiagonal
matrix.

2 7 5
3 5 3 2 1
1 -7 6 8 -2 4
-1 0 -5 7 6 -1 1
4 -2 9 0 8 9 0 -1
-5 3 6
4 -3

Triangular matrix Tridiagonal matrix

The natural method of representing matrices in memory as two-dimensional arrays may not be suitable for
sparse matrices. That is, one may save space by storing only those entries which may be nonzero.

Sparse Matrix Representation:


The 3-tuple method is used to represent sparse matrices.

3-tuple Method:
As the name implies, every nonzero entry of sparse matrix represented by three tuples. First tuple represents
row, second for column and third for value. Here all the elements of matrix will come sequentially. It can be
row major or column major 3-tuple presentation of sparse matrix. First row will represent the number of
rows, number of columns and number of nonzero elements of matrix. After that each row will represent non
zero entries of matrix.

Example: Let us take a sparse matrix of 6 X 5

0 -7 0 0 5
9 0 0 0 0
0 0 0 0 0
0 0 8 6 0

6
0 0 0 0 0
-1 0 0 0 2

Now we have to represent this matrix in 3-tuple representation of sparse matrix. Here total no.of rows is 6,
number of columns are 5 and nonzero entries are 7. So sparse matrix will be-

Row Column Value


6 5 7
0 1 -7
0 4 5
1 0 9
3 2 8
3 3 6
5 0 -1
5 4 2

Here its a row major 3-tuple presentation. So elements are coming sequentially on the basis of row. Suppose
we want to represent column major 3-tuple sparse matrix then it will be as-

Column Row Value


5 6 7
0 1 9
0 5 -1
1 0 -7
2 3 8
3 3 6
4 0 5
4 5 2

This 3-tuple method can be implemented in programming by using array or linked list.

Sparse Matrix Operations:


1. Create
2. Transpose
3. Addition
4. Multiplication

7
STACK
A Stack is a linear list in which an element may be inserted or deleted only at one end, called the top of the
stack. This means, elements are removed from a stack in the reverse order, that in which they were inserted
into the stack. Thus a stack is referred to as a Last In First Out (LIFO) data structure. It is a linear data
structure.
Examples of Stack are:
1) Stack of trays
2) Stack of books kept one above the other.
Here is a stack of trays:

Characteristics:
1) Deletion of an element can be done only from top of the stack.
2) Insertion of an element can be done only on top of the stack.
Memory Representation:
There are two ways to represent the stack in memory. They are
1. Using array(static memory)
2. Using linked list(dynamic memory)
Stack declaration using an array:
Suppose elements of the stack are of type int and the stack can store a maximum of 10 elements.
#define MAX 10
typedef struct
{
int top;
int elements[MAX];
}stack;
stack s;

Memory representation of empty stack s

0 1 2 3 4 5 6 7 8 9

Operations on Stack
initstack(s) to initialize s as an empty stack
push(s,i) to push element i onto stack s.
pop(s) to access and remove the top element of the stack s
peek(s) to access the top element of the stack without removing it from the stack s.
isfull(s) to check whether the stack s is full
isempty to check whether the stack s is empty

Stack initialization
Before we can use a stack, it needs to be initialized.
As the index of array elements can take any value in the range 0 to MAX-1, the purpose of
initializing the stack is served by assigning value -1 to the top of variable.
This simple task can be accomplished by the following function.
void initstack( stack *ps)
{
ps->top = -1;}
Testing if stack is empty
8
int isempty(stack *ps)
{
if(ps->top==-1)
return 1;
else
return 0;
}
or
int isempty(stack *ps)
{
return ((ps->top==-1)?1:0);
}
or even
int isempty (stack *ps)
{
return ps->top==-1;
}

Testing if the stack is full


int isfull(stack *ps)
{
if(ps->top==MAX-1)
return 1;
else
return 0;
}
or
int isfull(stack *ps)
{
return ((ps->top==MAX-1)?1:0);
}

Push operation
Adding a new element on to the top of the stack is known as push operation. While inserting a new element
into the stack, we must test whether there is a room in the stack or not. If room is available then the element
can be pushed i.e. the TOP identifies the next available room in the stack and the element is stored in the
room.
Overflow: This situation occurs when TOP identifies the last available room of the stack (i.e. stack is full),
and one performs the PUSH operation.
Condition for Overflow:
When TOP = MAX -1(where MAX is the maximum number of elements that can be hold by the array that
represents the stack in memory), where PUSH operation is performed.

void push(stack *ps, int value)


{
if(isfull(ps))
{
printf(stack overflow.);
return;
}
ps->top++;
ps->elements[ps->top]=value;
}
Pop operation
Removing the top element from the stack is known as pop operation. While performing the POP operation,
deletion of an element can be done from the room that is identified by the TOP and then TOP will identify
the second topmost room of the Stack.
Underflow: This situation occurs when no element is present in the stack (i.e. when TOP identifies NULL)
and POP operation is performed on it.
Condition of Underflow:
When TOP = -1 (i.e. when stack is empty), if POP operation is performed.
9
int pop(stack *ps)
{
int temp;
if(isempty(ps))
{
printf(Stack underflow);
return 0;
}
temp=ps->elements[ps->top];
ps->top--;
return temp;
}
Accessing the top element
There may be instances when we want to access the top element of the stack without removing it from the
stack.
int peek( stack *ps)
{
if(isempty(ps)
{
printf(Stack underflow);
return 0;
}
return(ps->elements[ps->top]);
}

The following figures illustrate operations on a stack, which can accommodate maximum of 10 elements.

Stack after pushing elements 8,10,12,-5,6:

top
0 1 2 3 4 5 6 7 8 9
8 10 12 -5 6

Stack after popping top two elements:


top
0 1 2 3 4 5 6 7 8 9
8 10 12

Stack after pushing elements -5,6,9,55

top
0 1 2 3 4 5 6 7 8 9
8 10 12 -5 6 9 55

10
Applications of Stack:
1. Keeping track of function calls.
2. Used to implement recursive procedures i.e. in recursion.
3. To reverse a list of elements or a string
4. Parsing data
5. Conversion and evaluation of Arithmetic expressions

Postfix
The sum of X and Y is written as X+Y where + is the operator while X and Y are the operands. We have
always learnt to write the sum of two numbers as X + Y; this notation is know as infix. Here, well be talking
about the postfix notation of representing arithmetic operations.

XY+ // postfix

The relative position of the operator with respect to the operands tells whether the expression is written in
postfix or infix notation. As the above expression shows, when the position of the operator is after the two
operands then the expression is said to be in postfix notation and if the position of the operator is between
the two operands the expression is in infix notation.

Rules for Converting Infix to Postfix

Here are some rules for converting infix to postfix:

1. If the expression contains any parentheses, then they should be converted first.
2. Conversion should be done according to the DMAS rule with precedence given first to division (/),
then multiplication (*), then addition (+) and finally subtraction (-). That is, in an expression
containing /, *, + and - first the division (/) sign should be evaluated followed by multiplication (*),
addition (+) and subtraction (-). Also, the exponentiation operator has a precedence higher than
these four common operators.
3. If there are two operators of the same precedence, then conversion should be done left to right.

These rules will become clearer with some examples.

Examples

Let us now consider some additional examples. The expression given below has a combination of
parentheses and division, multiplication and addition operators. In the first step according to rule number 1
the expression inside the parentheses is evaluated and converted into postfix notation, after this according to
rule number 2 first division is evaluated followed by multiplication and then addition.

A + (B * C) * D / E expression containing parentheses


A + (BC *) * D / E convert the multiplication
A + (BC *) * D E / convert the division
A + (BC*) D E / * convert the multiplication
A (BC*) D E / * + convert the addition
ABC*D*E/+ postfix form

The main thing that should be remembered during the conversion process is that the operator with highest
precedence is converted first and that the portion of expression already converted is treated as one single
operand.

11
Heres another example.

Note: In this example, the parentheses have deliberately been added to reverse the precedence.

(A + B) * C infix form
(AB +) * C convert the addition
(AB +) C * convert the multiplication
AB + C* postfix form

In the above example, addition is converted before the multiplication, this is because the parentheses around
A+B gives + more precedence than *. After the conversion of A+ B, AB+ is treated as one single operand
and not a combination of operands and operator. The rules for converting infix to postfix are simple,
providing that the order of precedence is known.

According to rule number 3, when unparenthesized operators of the same precedence are scanned, the order
of conversion is left to right except in the case of exponentiation, where the order is assumed to be from
right to left. Thus A + B + C means (A + B) + C, whereas A $ B $ C means A $ (B $ C). By using parentheses
we can override the default precedence.

The following are some more examples of infix expressions and their postfix equivalents.

Infix Postfix
A+B AB+
A+B-C+D AB+CD+-
(A + B) * (C - D) AB+ CD -*
A$B*C-D+E/F/(G + H) AB$C*DEF/GH+/+-
A - B/(C*D$E) ABCDE$*/-

Tips & Tricks:


1. Whenever a function calls is made, the called function must know how to return to its caller, so
the return address is pushed onto a stack. If a series of function calls occurs, the successive return
values are pushed onto the stack in last in , first out order so that each function can return to its
caller.
2. Stacks are used by the compilers in the process of evaluating expressions and generating machine
language code.

12
QUEUES
A queue is a line of persons waiting for their turn at some service counter. The service counter can be a
ticketing window of a cinema hall, a railway station, etc. Depending on the type of service provided by the
service counter and number of persons interested in service, there could be queues of varying lengths. The
service at the service counter is on the first come first serve (FCFS) basis, i.e., in order of their arrival in the
queue.

Queues in Programming
As far as programming is concerned, a queue is a linear list in which insertion can take place at one end of
the list, called the rear of the list, and deletion can take place at the other end called the front of the list. This
is how a simple FCFS queue works.
In programming jargon, insert and delete operations are known as enqueue and dequeue operations.
Memory Representation:
There are two ways to represent the stack in memory. They are
1. Using array(static memory)
2. Using linked list(dynamic memory)
Array Representation
We use the following declarations in order to represent the queue:
/*Define a queue of MAX 10*/
#define MAX 10
typedef struct
{
int front;
int rear;
int elements[MAX];
} queue;
queue q;
Using this declaration, we can perform the various queue operations.
Types of Queue:
1. Linear Queue
2. Circular Queue
3. Double Ended Queue or DEQUE
4. Priority Queue
Operations on Queues
The following are typical operations performed on queues:
1. initqueue(q) to initialize a queue as an empty queue.
2. enqueue(q,i) to insert element i in a q.
3. dequeue(q) to access and remove an element of queue q.
4. peek(q) to access the first element of the queue q without removing it.
5. isfull(q) to check whether the queue q is full.
6. isempty(q) to check whether the queue q is empty.
Linear Queue:
Queue Initialization
Before we can use a queue, it must be created. The purpose of initializing the queue is served by assigning
-1 (as a sentinel value) to the front and rear variables. Note that the valid range of an index for the array is 0
to MAX1.
void createqueue(queue *q)
{
q->front=q->rear=-1;
}

13
Testing if the Queue is empty
bool isempty(queue q)
{
if(q.front==-1)
return true;
else
return false;
}
Testing if the Queue is Full
bool isfull(queue q)
{
if ((q.front==0) && (q.rear==MAX-1))
return true;
else
return false;
}
Note: bool can be defined as
typedef enum {false, true} bool;

Performing the enqueue Operation on a Linear Queue


While inserting a new element into the queue, we must test whether there is a room in the queue or not. If
room is available then the element can be inserted i.e. the rear identifies the next available room in the
queue and the element is stored in the room.
Overflow: This situation occurs when there is no available room in the queue (i.e. queue is full), and one
performs the insert operation.
Condition for Overflow:
When rear = MAX -1(where MAX is the maximum number of elements that can be hold by the array that
represents the stack in memory), where insert operation is performed.
There are two scenarios that we should consider, assuming that the queue is not full.
1. If the linear queue is empty, then the value of the front and the rear variable will be -1 (the sentinel
value), then both front and rear are set to 0.
2. If the linear queue is not empty, then the rear variable is incremented.
C Code:
void enqueue(queue *q, int value)
{
if(isfull(*q))
{
printf(Queue overflow);
return;
}
if(isempty(*q))
q->front=q->rear=0;
else
{
q->rear++;
}
q->elements[q->rear]=value;
}
Performing the dequeue Operation on a Linear Queue
While performing the delete operation, deletion of an element can be done from the room that is identified
by the front and then front will identify the next room of the queue.
Underflow: This situation occurs when no element is present in the queue (i.e. when front identifies NULL)
and delete operation is performed on it.
Condition of Underflow:
When front = -1 (i.e. when queue is empty), if delete operation is performed.

There are two possibilities:


1. If there is only one element in the linear queue then after dequeueing it will become empty. This state
of the linear queue is reflected by setting the front and rear variables to -1, the sentinel value.
2. Otherwise, the value of the front variable is incremented.
14
C Code
int dequeue(queue *q)
{
int temp;
if(q->front==-1)
{
printf(Queue underflow);
return -1;
}
temp=q->elements[q->front];
if(q->front==q->rear)
q->front=q->rear=-1;
else
q->front++;
return temp;
}

The following figures illustrate operations on a linear queue, which can accommodate maximum of 10
elements.

Circular Queue
The difficulty of managing front and rear in an array-based non-circular queue can be overcome if we treat
the queue position with index 0 as if it comes after the last position (in our case, index 9), i.e., we treat the
queue as circular. Note that we use the same array declaration of the queue.
Implementation of operations on a circular queue:
Testing a circular queue for full
There are two conditions:
(front=0) and (rear=MAX-1)
front=rear+1
If any of these two conditions is satisfied, it means that circular queue is full.
bool isfull(queue *q)
{
if (((q->front==0)&&( q->rear==MAX-1))||( q->front==q->rear+1))
return true;
else
return false;}
15
Note: bool can be defined as typedef enum {false, true} bool;

The enqueue Operation on a Circular Queue


There are three scenarios which need to be considered
1. Testing a circular queue for overflow.
2. If the queue is empty, then the value of the front and the rear variable will be -1 (i.e., the sentinel
value), then both front and rear are set to 0.
3. If the queue is not full:
a. If the value of the rear variable is equal to MAX -1 then rear is set to 0.
b. Otherwise the rear variable is incremented.
void enqueue(queue *q, int value)
{
if(isfull(q))
{
printf(Queue overflow);
return;
}
/*adjust rear variable*/
if (q->front==-1)
q->front=q->rear=0;
else if (q->rear==MAX-1)
q->rear=0;
else
q->rear++;
/*store element at new rear */
q->elements[q->rear]=value;
}
The dequeue Operation on a Circular Queue
Again, there are three possibilities:
1. Testing a circular queue for underflow.
2. If there was only one element in the circular queue, then after the dequeue operation the queue will
become empty. This state of the circular queue is reflected by setting the front and rear variables to -1.
3. If the value of the front variable is equal to MAX-1, then set front variable to 0. Otherwise the front
variable is incremented.
int dequeue(queue *q)
{
if(q->front==-1)
{
printf(Queue underflow);
return -1;

int temp=q->elements[q->front]; /*store the front element in a temporary variable,


so that we return this value later */

/*adjust front variable */


if (q->front == q->rear)
q->front = q->rear=-1;
else if (q->front == MAX-1)
q->front = 0;
else
q->front++;

return temp; /*return the dequeued element*/


}

The following figures illustrate operations on a circular queue, which can accommodate maximum of 10
elements.

16
Empty queue:

Non-empty queues:

DEQUE
A deque (pronounced ether deck or dequeue) is a linear list in which elements can be added or removed
at either end but not in the middle. The term deque is a contraction of the name double ended queue.

deletion deletion
insertion insertion

There are two variations of a deque namely,

17
1. Input restricted deque: This is a deque which allows insertions at only one end of the list but
allows deletions at both ends of the list.
2. Output restricted deque: This is a deque which allows deletions at only one end of the list but
allows insertions at both ends of the list.

PRIORITY QUEUES
A priority queue is a collection of elements such that each element has been assigned a priority and such that
the order in which elements are deleted and processed comes from the following rules:
1. An element of higher priority is processed before any element of lower priority.
2. Two elements with the same priority are processed according to the order in which they were added
to the queue.
There are two types of priority queues
1. Ascending priority queue ( or Minimum priority queue) : This is a priority queue where lower
number has higher priority.
2. Descending priority queue ( or Maximum priority queue): This is a priority queue where higher
number has higher priority.
Applications of Queue:
1. Many computers have only a single processor, so only one user at a time may be serviced. Entries for
the other users are placed in a queue. Each entry gradually advanced to the front of the queue as users
receive service. The entry at the front of the queue is the next to receive service.
2. Queues are also used to support print spooling.
3. Information packets also wait in queues in computer network.

18
Dynamic Storage Management
1. Static Memory allocation: Static memory allocation is the one in which the required memory is
allocated at compile time.
Example: arrays are the best examples of static memory allocation.

Drawbacks :
a. Size is fixed before execution.
b. Insertion and deletion is a time taking process as the elements need to be shifted towards left or
right.
c. There may be a possibility that memory may get wasted or memory is deficit.
2. Dynamic memory allocation: Dynamic memory allocation is the one in which the required memory
is allocated at the run time.
Example: linked lists are the best examples of dynamic memory allocation.

Advantages:
a. The allocated size can be increased or decreased as per the requirement at run time.
b. Memory can be utilized efficiently as the programmer can choose exact amount of memory
needed.
c. The operations like insertion and deletion are less time-consuming as compared to arrays.

Disadvantages:
a. It takes more memory space because each node contains the link or address part.
b. Moving to a specific node directly like array is not possible.

LINKED LIST (Single linked list)


A linked list, or one way list, is a linear collection of data elements, called nodes, where the linear order is
given by means of pointers. That is, each node is divided into two parts: the first part contains the
information of the element, and the second part, called the link field or next pointer field contains the
address of the next node in the list.
A start pointer is used to hold the address of the first element in the list. Also, the last element of the linked
list has a NULL value in the next pointer field to mark the end of the list.

START
100

100 200 300 400


5 200 7 300 4 400 8 X

INFO LINK

One way list or single linked list

Declaration of a Linear Linked List

Suppose we want to store a list of ints, then the linear linked list can be declared as:

typedef struct nodetype


{
int info;
struct nodetype *next;
} node;
node *start;

19
The above declaration defines a new data type, struct nodetype, with a typedef of node.

Creating an empty list


In the previous declaration, the variable start is declared as a pointer to a node.
The variable start has not yet been initialized.
This variable is used to point to the first element of the list
Since the list will be empty in the beginning, the variable start is assigned a sentinel value to indicate
that the list is empty.

void createemptylist(node **start)


{
*head=NULL;
}

Operations on Linked Lists

The main operations that are performed on a linked list are the following:

Traversing and searching


Inserting
Deleting

Overflow condition:
When AVAIL = NULL
It indicates that no free memory block exist in the memory, at this point of time if we try to create a new
node overflow situation occurs.
Underflow condition:
When START = NULL
It indicates that linked list has no nodes or linked list does not exist, at this point of time if we try to delete a
node from linked list to underflow situation.

Sample Program

Here is a sample linked list program:

/*
* Linear Linked List of strings
* http://www.tech-faq.com
*/

#include <stdio.h>
#include <stdlib.h>

struct llnode
{
char *data;
struct llnode *next;
};

void addAtPos(struct llnode **, char *, int );


void search(struct llnode *, char *);
void deleteByElement(struct llnode **, char *);
void deleteByPosition(struct llnode **, int );
void delete_list(struct llnode **);
void display(struct llnode *);
void displaynth(struct llnode *, int );

20
void displayrev(struct llnode *, int );

/*
* Driver functions -- main() and menu()
*/
int menu(struct llnode *);
int main()
{
struct llnode *start = NULL;
int pos;
char temp[80];

while (1)
{
switch(menu(start))
{
case 1:
puts("Enter strings to add at end, blank line to stop: ");
while (gets(temp)[0])
addAtPos(&start, temp, INT_MAX);
break;
case 2:
puts("Enter strings to add at beginning, blank line to stop: ");
while (gets(temp)[0])
addAtPos(&start, temp, 1);
break;
case 3:
printf("Enter position: ");
scanf("%d", &pos);
gets(temp); /*Clear buffer*/
printf("Enter the string to add: ");
addAtPos(&start, gets(temp), pos);
break;
case 4:
printf("Enter string to search: ");
search(start, gets(temp));
break;
case 5:
printf("Enter string to delete: ");
deleteByElement(&start, gets(temp));
break;
case 6:
printf("Enter position: ");
scanf("%d", &pos);
gets(temp);
deleteByPosition(&start, pos);
break;
case 7:
delete_list(&start);
puts("List deleted successfully");
break;
case 8:
display(start);
getchar();
break;
case 9:
printf("Enter position: ");
scanf("%d", &pos);
gets(temp);
displaynth(start, pos);
break;
case 10:
displayrev(start, 1);
getchar();
break;

21
case 11:
exit(0);
}
}
}

int menu(struct llnode *start)


{
int choice;
char temp[80];
puts("1-Add at end");
puts("2-Add at beginning");
puts("3-Add at position");
if (start)
{
puts("4-Search");
puts("5-Delete by element");
puts("6-Delete by position");
puts("7-Delete list");
puts("8-Display");
puts("9-Display nth node");
puts("10-Display in reverse");
}
puts("11-Exit");
scanf("%d", &choice);
gets(temp); /*clear buffer*/
return choice;
}
void addAtPos(struct llnode **start, char *toadd, int pos)
{
struct llnode *prev, *curr;
struct llnode *newnode;
int cnt;
if (pos<1)
{
puts("Invalid position");
return;
}
prev = NULL;
curr = *start;
for (cnt=1; curr && cnt<pos; cnt++)
{
prev = curr;
curr = curr->next;
}
newnode = malloc(sizeof (struct llnode));
newnode->data = malloc(strlen(toadd)+1);
strcpy(newnode->data, toadd);
newnode->next = curr;
if (prev!=NULL)
prev->next = newnode;
else
*start = newnode;
if (curr==NULL && prev!=NULL && pos!=INT_MAX)
puts("Data inserted at end");
}

void search(struct llnode *p, char *tosearch)


{
int pos;

for (pos=1; p && strcmp(p->data, tosearch)!=0; pos++)


p = p->next;

if (p!=NULL)

22
printf("%s found at position %d\n", tosearch, pos);
else
printf("%s not found in list\n", tosearch);
}
void deleteByElement(struct llnode **start, char *todelete)
{
struct llnode *prev, *curr, *temp;
prev = NULL;
curr = *start;

while (curr && strcmp(curr->data, todelete)!=0)


{
prev = curr;
curr = curr->next;
}
if (curr == NULL)
puts("ELEMENT NOT FOUND");
else
{
temp = curr;
if (prev != NULL)
prev->next = curr->next;
else
*start = (*start)->next; /*this deletes the first element*/
free(temp->data);
free(temp);
printf("%s deleted successfully", todelete);
}
getchar();
}
void deleteByPosition(struct llnode **start, int pos)
{
struct llnode *prev, *curr, *temp;
int cnt;
char deleted[80];
prev = NULL;
curr = *start;
for (cnt=1; curr && cnt<pos; cnt++)
{
prev = curr;
curr = curr->next;
}
if (curr == NULL && cnt!=1)
puts("POSITION NOT FOUND");
else
{
temp = curr;
if (prev != NULL)
prev->next = curr->next;
else
*start = (*start)->next; /*this deletes the first element*/
strcpy(deleted, temp->data);
free(temp->data);
free(temp);
printf("%s deleted successfully", deleted);
}
getchar();
}
void delete_list(struct llnode **start)
{
struct llnode *temp;
while(*start)
{
temp = *start;
*start = (*start)->next;
free(temp);

23
}
}
void display(struct llnode *p)
{
int cnt;
for (cnt=1; p; cnt++)
{
printf("%d: %s\n",cnt,p->data);
p = p->next;
}
}

void displaynth(struct llnode *p, int pos)


{
int cnt;
for (cnt=1; p && cnt<pos; cnt++)
p=p->next;

if (p && cnt==pos)
printf("The %dth node is %s\n", pos, p->data);
else
printf("Invalid position\n");
getchar();
}

void displayrev(struct llnode *p, int cnt)


{
if (p)
{
displayrev(p->next, cnt+1);
printf("%d: %s\n", cnt, p->data);
}
}
Circular Singly Linked List
It is a single linked list where the link part of last node contains the address of first node. In other
words when we traverse a circular single linked list the first node can be visited after the last node.
As the circular single linked list is of circular in nature as a result of which there will be no
node whose address part i.e. the link part is NULL.

Operations on Circular single linked list:


1. Insertion ( at the beginning, at the end)
2. Deletion ( at the beginning, at the end)

Fresh node is a new node that is to be inserted in the list.


Advantages:
1. Nodes can be accessed easily.
2. Deletion of nodes is easier.
3. Concatenation and splitting of circular linked list can be doen efficiently.

Disadvantages:
1. It may enter into an infinite loop.
2. Head node is required to indicate the START or END of the circular linked list.
3. Backward traversing is not possible.

Doubly Linked List


In a doubly linked list, also called a two-way list, each node is divided into three parts:

24
1. The first part, called the previous pointer field, contains the address of the preceding element in the
list.
2. The second part contains the information of the list.
3. The third part, called the next pointer field, contains the address of the succeeding element in the list.

In addition, two pointer variables, named head and tail, are used that contain the address of first element and
the address of last element of the list.

Implementation of a Doubly Linked List in C

Suppose we want to store list of integers. Then, we define the following self-referential structure:

typedef struct nodetype


{
struct nodetype *prev;
int info;
struct nodetype *next;
}node;
node *head,*tail;

The above declaration defines a new data type called struct nodetype with a typedef of node. Two node
pointers are also declared: head and tail.

Creating an Empty List


In the previous declaration, the variables head and tail are each declared as a pointer to a node.
These variables are not yet initialized.
head is used to point to the first element of the list and tail is used to point to the last element of
the list.
Since the list will be empty in the beginning, the variables head and tail should be assigned a sentinel
value to indicate the list is empty.

void createemptylist(node **head, node **tail)


{
*head=*tail=NULL;
}

A call to the above function will need to pass the addresses of head and tail (as double pointers) as follows:
createemptylist(&head, &tail);

25
Operations on Doubly Linked Lists

The following operations can be performed on doubly linked lists:

Inserting an element
Deleting an element
Traversing and Searching

Doubly Linked List - Sample Program


/*
* Doubly Linked List
* http://www.tech-faq.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

typedef struct node


{
struct node *prev;
int data;
struct node *next;
} Node;

/*MakeNode: Make a newnode inserting data into it .


Return the newnode's address*/
Node *MakeNode(int data)
{
Node *p=malloc(sizeof (Node));
p->data = data;
return p;
}

/*Insert: Insert new data in the list at position specified by pos, beginning from 1
pos is invalid if < 1. If pos>length of list, insert at end*/
void Insert(int pos, int data, Node **start)
{

if (pos<1)
{
puts("Invalid position");
return;
}
if (*start == NULL)
{
*start = MakeNode(data);
(*start)->prev = NULL;
(*start)->next = NULL;
}
else
{
Node *curr=*start;
for (;curr->next && pos>1; pos--)
curr = curr->next;
if (pos > 1 ) /*insert at end*/
{
Node *newnode = MakeNode(data);
newnode->prev = curr;
newnode->next = curr->next;
curr->next = newnode;
}
else if (curr==*start) /*insert at beginning*/

26
{
Node *newnode = MakeNode(data);
newnode->prev = curr->prev;
newnode->next = curr;
curr->prev = newnode;
*start = newnode;
}
else
{
Node *newnode = MakeNode(data);
newnode->prev = curr->prev;
newnode->next = curr;
curr->prev->next = newnode;
curr->prev = newnode;
}
}
}

void DeleteByPosition(int pos, Node **start)


{
if (pos<1 || *start==NULL)
puts("Invalid position");
else if (pos==1) /*delete first node*/
{
Node *temp = *start;
*start = (*start)->next;
free(temp);
}
else /*find the position*/
{
Node *temp = *start;
for (;temp && pos>1; pos--)
temp = temp->next;
if (temp==NULL)
puts("Invalid position");
else
{
temp->prev->next = temp->next;
if (temp->next)
temp->next->prev = temp->prev;
free(temp);
}
}
}

void DeleteByValue(int value, Node **start)


{
if ((*start)->data == value) /*delete first node*/
{
Node *temp = *start;
*start = (*start)->next;
free(temp);
}
else /*find the node*/
{
Node *temp = (*start)->next;
for (; temp && temp->data!=value; temp=temp->next)
;
if (temp==NULL)
puts("Element not found");
else
{
temp->prev->next = temp->next;
if (temp->next)
temp->next->prev = temp->prev;
free(temp);

27
}
}
}

void Traverse(Node *x)


{
int cnt=1;
while (x)
{
printf("%d: %d\n", cnt++, x->data);
x = x->next;
}
}

void TraverseInReverse(Node *x)


{
int cnt=1;
if (x)
{
while (x->next) /*Go to the end*/
{
x = x->next;
cnt++;
}
while (x)
{
printf("%d: %d\n", cnt--, x->data);
x = x->prev;
}
}
}

int menu(void)
{
int choice;
puts("1-Insert data at end");
puts("2-Insert at beginning");
puts("3-Insert at position");
puts("4-Delete by position");
puts("5-Delete by element");
puts("6-Display");
puts("7-Display in reverse");
puts("8-Exit");
printf("Enter choice: ");
scanf("%d", &choice);
while (getchar()!='\n')
;
return choice;
}
/* Test driver. Note: gets() is unsafe and
* should not be used in production environments.
* Its use here is only for illustration.
*/
int main(void)
{

Node *start = NULL;


char input[10];
int pos;
while(1)
{
switch(menu())
{
case 1:
puts("Enter numbers, blank line to stop");
while (gets(input)[0])

28
Insert(INT_MAX, atoi(input), &start);
break;
case 2:
printf("Enter number: ");
Insert(1, atoi(gets(input)), &start);
break;
case 3:
printf("Enter position: ");
pos = atoi(gets(input));
printf("Enter number: ");
Insert(pos, atoi(gets(input)), &start);
break;
case 4:
printf("Enter position: ");
DeleteByPosition(atoi(gets(input)), &start);
break;
case 5:
printf("Enter value: ");
DeleteByValue(atoi(gets(input)), &start);
break;
case 6:
Traverse(start);
break;
case 7:
TraverseInReverse(start);
break;
case 8:
exit(0);
}
}
}

Circular Doubly Linked List


The doubly linked list of circular in nature is referred as circular doubly linked list i.e. in a circular
doubly linked list, the NEXT part of last node contains the address of first node and the PREV part of first
node contains the address of last node.
The regular operations like insertion, deletion, traversing, searching, merging, splitting,
disposing etc can be implemented very easily.

Header Linked List


A header-list is a linked list, which always contain a special node, called the header node, at
the beginning of the list. A list, which contains this type of node is called the header-linked list.
This type of linked list is useful when information other than that found in each node is needed.
1. A grounded header list is header list where the last node contains the NULL pointer.
2. A circular header list is a header list where the last node points back to the header node.

Applications of Linked List


1. Linked Stack along with its operations.
2. Linked Queue along with its operations.
3. To maintain dictionary of names (Linked Dictionary).
This type of problem occurs in compiler construction to store the list of identifiers appearing in a
program to maintain symbol table.
4. Polynomial manipulation (addition/subtraction).
Inserting the nodes sequentially can do the addition and multiplication can be done by repeated
additions.
5. Long integer operations.
The hardware of most computers allows integer of only a maximum specific length. But using linked
lists we can perform arithmetic operations on long integers.
29
6. Representation of sparse matrix.

Tips & Tricks


1. Linked list are dynamic, so the length of a list can increase or decrease as necessary.
2. Linked lists can be maintained in sorted order by inserting each new element at the proper point
in the list.
3. Linked list nodes are normally not stored contiguously in memory. Logically, however the nodes
of a linked list appear to be contiguous.

How is Data Stored in My Hard Disk?


A key concept in data storage is that it is linear, logical and systematic in nature. When you 'save' data to
the hard disk, the disk will follow a logical system.

A hard disk can be compared to a large, square piece of graphing paper composed of squares a thousand
long and a thousand wide. Each individual square can accommodate one kilobyte of data and thus a 'strip'
having a thousand squares can store one megabyte of data. In such a case, the disk head will 'fill' in the
upper leftmost square first then continue down the line one square at a time until all the appropriate
squares are filled.

A second key concept to remember is that data storage follows a simple rule data is stored (or 'written') on
the first available space, wherever this may be.

Data Storage Example

Imagine that you are saving a 1-Mb Word document on the hard disk. Saving a 1 MB file means the disk
will systematically 'fill in' a thousand squares.

After saving the Word document, let us assume that you saved to your hard disk a 3 MB photo. Following
the key concepts of data storage, the disk writer head will go through the disk and then look for the first
available space which in our example, happens to be the square following the last kilobyte of the Word
document. Thus, the 3 MB photo will be saved at the space next to the Word document.

Afterwards, you go back to your Word document and trim it down, ending up with a file 700 KB in size,
which you then save to your hard disk. Given the smaller file size, the 1 MB space requirement was reduced
to 700 KB, leaving 300 KB free or (using the analogy above) 300 blank squares immediately after the Word
document. There's now available space between the Word Document and the 3 MB photo.

When you save yet another file, say a 2 MB Excel file, the hard disk will follow its rule of saving data on the
first available space. Thus, the Excel file will be split into two portions: 300 KB would be written on the 300
KB free space and the rest will be placed in the next available space after the image file.

The Problem

Data storage, while designed to be logical and systematic, leads to file fragmentation. Imagine adding,
deleting files and editing files for days, weeks and months. After a few months, the hard disk will become so
fragmented that it would take the disk a lot of time to find fragments of a single file.

It is easy to see from this that data retrieval would be easy if the file components are in a single, contiguous
location on the hard disk. De-fragmentation is the process wherein the different file fragments in a data
storage system are located, 'gathered' and consolidated into specific areas of the storage device, thus making

30
it easier to retrieve data allowing for speedier processing as well as reduced wear and tear on data retrieval
mechanisms.

Trees
Arrays, linked lists, stacks and queues are used to represent linear and tabular data. These structures are not
suitable for representing hierarchical data.

In hierarchical data we have

ancestors, descendants
superiors, subordinates, etc

Family Structure

Business Corporate Structure

31
Federal Government Structure

Introduction to Trees
Fundamental data storage structures used in programming
Combine advantages of ordered arrays and linked lists
Searching can be made as fast as in ordered arrays
Insertion and deletion as fast as in linked lists

Tree characteristics
Consists of nodes connected by edges
Nodes often represent entities (complex objects) such as people, car parts etc.
Edges between the nodes represent the way the nodes are related.
Its easy for a program to get from one node to another if there is a line connecting them.
The only way to get from node to node is to follow a path along the edges.

Tree Terminology
Root: node without parent (A)
Internal node: node with at least one child (A, B, C, F)
External node: (a.k.a. leaf) node without children (E, I, J, K, G, H, D)
Ancestors of a node: parent, grandparent, grand-grandparent, etc
Depth of a node: number of ancestors
Height of a tree: maximum depth of any node (3)
Descendant of a node: child, grandchild, grand-grandchild, etc
Degree of an element: no. of children it has
Subtree: tree consisting of a node and its descendants

32
Path: traversal from node to node along the edges that results in a sequence
Root: node at the top of the tree
Parent: any node, except root has exactly one edge running upward to another node. The node above
it is called parent.
Child: any node may have one or more lines running downward to other nodes. Nodes below are
children.
Leaf: a node that has no children
Subtree: any node can be considered to be the root of a subtree, which consists of its children and its
childrens children and so on.
Visiting: a node is visited when program control arrives at the node, usually for processing.
Traversing: to traverse a tree means to visit all the nodes in some specified order.
Levels: the level of a particular node refers to how many generations the node is from the root. Root
is assumed to be level 0.
Keys: key value is used to search for the item or perform other operations on it.

Binary Trees
Every node in a binary tree can have at most two children.
The two children of each node are called the left child and right child corresponding to their
positions.
A node can have only a left child or only a right child or it can have no children at all.

A binary tree is a tree with the following properties:


o Each internal node has at most two children (exactly two for proper binary trees)
o The children of a node are an ordered pair
We call the children of an internal node left child and right child
Alternative recursive definition: a binary tree is either
o a tree consisting of a single node, or
o a tree whose root has an ordered pair of children, each of which is a binary tree
Applications:
o arithmetic expressions
o decision processes
o searching

33
Arithmetic Expression Tree
Binary tree associated with an arithmetic expression
o internal nodes: operators
o external nodes: operands
Example: arithmetic expression tree for the expression (2 * (a - 1) + (3 * b))

Compiling arithmetic expressions

We can represent an arithmetic expression such as

(A + B) * (C + D) * 2 X / Y
as a binary tree, in which the leaves are constants or variables and the nodes are operations:

Properties of Proper Binary Trees

Notation

n number of nodes
e number of external nodes
34
i number of internal nodes
h height

Properties

e = i +1
n =2e -1
h <= i
h <= (n -1)/2
e <= 2h

Searching
Searching is the process of finding the location of a given element in a set of elements. The search is said to
be successful if the given element is found i.e., the element does exist in the collection (such as an array);
otherwise it is unsuccessful.

Two simple approaches to searching are:

Linear search: This method traverses a list sequentially to locate the search key.
Binary search: This method works on sorted lists by progressively making better guesses to find the
location of a search key.

Linear Search
This method traverses a list sequentially to locate the search key.

The algorithm that one chooses generally depends on the organization of the array elements. If the elements
are in random order, then one should choose the linear search technique.

Algorithm
linearsearch (a,n,item,loc)

Here a is an array of the size n. This algorithm finds the location of the element item in the array a. If
search item is found, it sets loc to the index of the element; otherwise, it sets loc to -1

Begin
for i=0 to (n-1) by 1 do
if (a[i] = item) then
set loc=I
exit
endif
endfor
set loc -1
end

C/C++ Implementation of the Algorithm


int linearsearch (int a[], int n, int key)
{
int i;
for(i=0;i<n;i++)
35
{
if(a[i]==key)
return i;
}
return -1;
}

Analysis of Linear Search

In the best possible case, the item may be found at the first position. In that case, the search operation
terminates in success with just one comparison.

The worst case occurs when the item is present at the last position or it is missing from the array. In the
former case, the search terminates in success with n comparisons. In the latter case, the search terminates in
failure with n comparisons. Thus, we find that in the worst case, linear search carries out n operations.

Complete Program
/**
* Program that illustrates the working of Linear Search
* http://www.tech-faq.com
*/

#include <stdio.h>

/**
* Parameters:
* a: array to search in
* n: number of elements in the array
* key: search key
*
* Return Value: index of the first occurence
* of key in a[], or -1 if not found
*/
int linearsearch (int a[], int n, int key)
{
int i;
for(i=0;i<n;i++)
{
if(a[i]==key)
return i;
}
return -1;
}

int main()
{
int array[50], num, index, element, key;

/*Fill the array*/


puts("How many elements do you want to create the array with? (Max 50)");
scanf("%d", &num);

printf("Enter the elements: ");


fflush(stdout);

for (index=0; index<num; index++)


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

printf("Enter the search key: ");


fflush(stdout);
scanf("%d", &key);

36
if ( (index=linearsearch(array, num, key)) != -1)
printf("The element was found at index: %d\n", index);
else
printf("Could not find %d\n", key);

return 0;
}

Binary Search
This method works on sorted lists by progressively making better guesses to find the location of a search
key.

Illustration

Consider the following array:

3 10 15 20 35 40 60

Suppose we want to search the element "15"

1. We take beg = 0, end = 6 and compute the location of the middle element as

2. We then compare the search key with mid i.e. a[mid]=a[3] is not equal to 15. Since beg<end, we start
the next iteration.
3. As a[mid]=20>15, therefore, we take end = mid-1 = 3-1 = 2 whereas beg remains the same.. Thus

4. Since a[mid] i.e. a[1]=10<15, therefore, we take beg=mid+1=1+1=2, while end remains the same.
5. Now beg=end. Compute the mid element:

Since a[mid] i.e. a[2]=15, the search terminates on success.

Algorithm

Binarysearch(a,n,item,loc)

Begin
set beg=0
set end=n-1
set mid=(beg+end)/2
while((beg<=end) and(a[mid]!=item) do
if(item<a[mid]) then
set end=mid-1
else
set beg=mid+1
endif
set mid=(beg+end)/2
endwhile
if(beg>end) then
set loc=-1
37
else
set loc=mid
endif
end

C/C++ Implementation
int binarysearch(int a[], int n, int key)
{
int beg,end,mid;
beg=0; end=n-1;
mid=(beg+end)/2;
while((beg<=end)&&(a[mid]!=key))
{
if(key<a[mid])
end=mid-1;
else
beg=mid+1;
mid=(beg+end)/2;
}
if(beg>end)
return -1;
else
return mid;
}

Analysis of Binary Search

In each iteration, the search is reduced to one-half of the array. For n elements in the array, there will be a
maximum of log2 n iterations.

Thus, the complexity of binary search is O(log2 n).

Complete Program Listing


/**
* Program that illustrates the working of Binary Search
* http://www.tech-faq.com
*/

#include <stdio.h>

int binarysearch(int a[], int n, int key)


{
int beg,end,mid;
beg=0; end=n-1;
mid=(beg+end)/2;
while((beg<=end)&&(a[mid]!=key))
{
if(key<a[mid])
end=mid-1;
else
beg=mid+1;
mid=(beg+end)/2;
}
if(beg>end)
return -1;
else
return mid;
}

int main()
{
int arr[50], n, key, index;
38
printf("How many elements do you want to create the array with?(max 50): ");
fflush(stdout);

scanf("%d", &n);

puts("Enter the array elements in ascending order");


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

printf("Enter the search key: ");


fflush(stdout);

scanf("%d", &key);

index = binarysearch(arr, n, key);

if (index == -1)
puts("Sorry, the given key was not found");
else
printf("The given key was found at index: %d\n", index);

return 0;
}

Sorting
Sorting is the process of arranging elements in some logical order.

Sorting methods are classified into the following categories:

External sorting: This deals with sorting of data stored in external files. This method is used when
the volume of data is very large and cannot be held in a computers RAM.
Internal sorting: This deals with sorting of data held in the RAM of a computer.

Sorting Methods

The following are links to tutorials on some of the most popular sorting methods:

Bubble sort
Bucket sort
Insertion sort
Merge sort
Quick sort
Selection sort
Shell sort

Bubble Sort
It requires n-1 passes to sort an array.
In each pass every element a[i] is compared with a[i+1], for i=0 to (n-k-1), where k is the pass
number and if they are out of order i.e. if a[i]>a[i+1], they are swapped.
This will cause the largest element to move up or bubble up.

39
Thus after the end of the first pass the largest element in the array will be placed in the last or nth
position and on the next pass, the next largest element will be placed at position (n-1). This continues
for each successive pass until the last or (n-1)th pass when the second smallest element will be
placed at the second position.

Pass1.
Step 1. if a[0]>a[1] then swap a[0] and a[1].
Step 2. if a[1]>a[2] then swap a[1] and a[2].

Step n-1. if a[n-2]>a[n-1] then swap a[n-2] and a[n-1].


Pass2.
Step 1. if a[0]>a[1] then swap a[0] and a[1].
Step 2. if a[1]>a[2] then swap a[1] and a[2].

Step n-2. if a[n-3]>a[n-2] then swap a[n-3] and a[n-2].

Pass k.
Step 1. if a[0]>a[1] then swap a[0] and a[1].
Step 2. if a[1]>a[2] then swap a[1] and a[2].

Step n-k. if a[n-(k+1)]>a[n-k] then swap a[n-(k+1)] and a[n-k].

Pass n-1
Step 1. if a[0]>a[1] then swap a[0] and a[1].

40
Analysis of bubble sort
First pass requires n-1 comparison
Second pass requires n-2 comparison
kth pass requires n-k comparisons
Last pass requires only one comparison

Therefore the total comparisons are:

41
Algorithm

Bubblesort(a,n)

Begin
for k=1 to (n-1) by 1 do
for j=0 to (n-k-1) by 1 do
if(a[j]>a[j+1]) then
set temp=[j]
set a[j]=a[j+1]
set a[j]=temp
endif
endfor
endfor
end

Complete Program
/**
* Bubble sort
* http://www.tech-faq.com
*/

#include <stdio.h>

void bubblesort(int a[], int size)


{
int temp, i, j;
for (i=0; i<size-1; i++)
for (j=0; j<size-1-i; j++)
if (a[j]>a[j+1])
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}

int main()
{
int a[50], n, i;
printf("How many elements in the array(max 50)?: ");
fflush(stdout);
scanf("%d", &n);
puts("Enter array elements");
for (i=0; i<n; i++)
scanf("%d", &a[i]);
bubblesort(a, n);

puts("-----The sorted array is--------------");


for (i=0; i<n; i++)
printf("%d ", a[i]);

printf("\n");
return 0;
}

Bucket/Radix Sort
Most people use the bucket sort method when sorting a list of names in alphabetical order. The procedure is:

42
First, the names are grouped according to the first letter, thus the names are arranged in 26 classes,
one for each letter of the alphabet. The first class consists of those names that begin with letter A, the
second class consists of those names that begin with letter B, and so on.
Next, the names are grouped according to the second letter. After this step, the list of names will be
sorted on the first two letters.
This process is continued for a number of times equal to the length of the name with maximum
letters.

Since there are 26 letters in the alphabet, we make use of 26 buckets, one for each letter of the alphabet.
After grouping these names according to their specific letter, we collect them according to the order of the
buckets. This new list becomes the input for the next pass when we consider the next letter.

To sort decimal numbers where the base (or radix) is 10, we need 10 buckets, numbered from 0-9. Unlike
sorting names, decimal numbers are sorted from right to left i.e. first on unit digits, then on ten digits and so
on.

Example

Here is an illustration of how bucket sorting works. Consider the following unsorted array:

321 150 235 65 573 789 928 542

43
44
After pass three, when the numbers are collected, they are in the following order:

65 150 235 321 542 573 789 928

Thus, the numbers are sorted.

Algorithm
Bucketsort(a,n)

Here a is a linear array of integers with n elements, the variable digitcount is used to store the number of
digits in the largest number in order to control the number of passes to be performed.

Begin
find the largest number of the array
set digitcount=no. of digits of the largest no.
for pass=1 to digitcount by 1 do
initialize buckets
for i=1 to n-1 by 1 do
set digit=obtain digit no. pass of a[i]
put a[i] in bucket no. digit
increment bucket count for bucket no. digit
endfor
collect all the numbers from buckets in order
endfor
end

45
Analysis of Bucket Sort

Suppose that the number of digits in the largest number of the given array is s and the number of passes to
be performed is n. Then, the number of comparisons, f(n), needed to sort the given array are:
f(n)=n*s*10, where 10 is the decimal number base.

Though s is independent of n, but if s=n, then in worst case, f(n)=O(n2).

But on the other hand, if s=log10 n, then f(n)=O(n log10 n).

From the above discussion, we conclude that bucket sort performs well only when the number of digits in
the elements are very small.

Insertion Sort
This algorithm is very popular with bridge players when they sort their cards. In this procedure, we pick up a
particular value and then insert it at the appropriate place in the sorted sub list.

This algorithm requires n-1 passes.

Pass1: a[1] is inserted either before or after a[0] so that a[0] and a[1] are sorted.
Pass2: a[2] is inserted either before a[0] or between a[0] and a[1] or after a[1] so that the elements a[0], a[1],
a[2] are sorted.
Pass3: a[3] is inserted either before a[0] or between a[0] and a[1] or between a[1] and a[2] or after a[2] so
that the elements a[0], a[1], a[2], a[3] are sorted.
Pass k: a[k] is inserted in its proper place in the sorted sub array a[0], a[1], a[2],a[k-1] so that the
elements a[0], a[1], a[2],a[k-1],a[k] are sorted.
Pass n-1:a[n-1] is inserted in its proper place in the sorted sub array a[0], a[1], a[2],a[n-2] so that the
elements a[0], a[1], a[2],a[n-1] are sorted.

46
47
Analysis of Insertion Sort

The worst-case performance occurs when the elements of the input array are in descending order.

In the worst-case, the first pass will require one comparison, the second pass will require 2 comparisons, and
so on until the last pass which will require (n-1) comparisons. In general, the kth pass will require k-1
comparisons.

Therefore the total number of comparisons is:


F(n)=1+2+3+..+(n-k)+.+(n-3)+(n-2)+(n-1)
=n(n-1)/2
=O(n2)

Algorithm

insertionsort(a,n)
Here a is a linear array with n elements. This algorithm sorts the elements in ascending order. It uses a
temporary variable temp to facilitate the exchange of two values and variables j and k are used as loop
control variables.

begin
for k=1 to (n-1) by 1 do
set temp=a[k]
set a[j]=k-1
while((temp<a[j]) and j>=0) do
set a[j+1]=a[j]
set j=j-1
endwhile

48
set a[j+1]=temp
endfor
end

Complete Program
/**
* Insertion sort
* http://www.tech-faq.com
*/

#include <stdio.h>

void insertionsort(int a[], int n)


{
int i, k, y;
for (k=1; k<n; k++)
{
y = a[k];
for (i=k-1; i>=0 && y<a[i]; i--)
a[i+1] = a[i];
a[i+1] = y;
}
}

int main()
{
int a[50], n, i;
printf("How many elements do you want to create the array with?"
"max(50): ");
scanf("%d", &n);
fflush(stdout);
puts("Enter the array elements");
for (i=0; i<n; i++)
scanf("%d", &a[i]);
insertionsort(a, n);
puts("--------Sorted Array--------");
for (i=0; i<n; i++)
printf("%d ", a[i]);
printf("\n");

return 0;
}

Output

How many elements do you want to create the array with?(max 50): 6
Enter the array elements
3
67
85
89
56
44
--------Sorted Array--------
3
44
56
67
85
89

49
Merge Sort
Merging means combining elements of two arrays to form a new array. The simplest way of merging two
arrays is to first copy all the elements of one array into a new array and then append all the elements of the
second array to the new array. If you want the resultant array to be sorted, you can sort it by any of the
sorting techniques.

If the arrays are originally in sorted order, they can be merged in such a way as to ensure that the combined
array is also sorted. This technique is known as merge sort.

The technique of merge sort (i.e., sorting during merging) is much more efficient than sorting after merging
for arrays that are already sorted.

STEP 1: Let us consider two arrays say, A[7] and B[5] to be merged to form a new array. The new array say
C will be having 7+5=12 elements.

STEP 2: Compare A[0] and B[0]; if A[0]<B[0] (say); move A[0] to C[0]. Increment the pointers of array A
and array C (the pointer of that array is incremented whose element is moved in the third array).

STEP 3: Now compare the elements of A and B where the pointers are pointing. That is, compare A[1] and
B[0].
Suppose that we find B[0]<A[1], so we move B[0] to C[1] and increment Bs pointer to point to the next
element in array B.

C++ Implementation
/**
* A and B are the input arrays which contain
* elements in ascending order. Their respective
* sizes are m and n. C is the output array
* that will contain the elements from the
* two arrays combined and in sorted order.
*/
void mergesort(int A[], int B[], int C[], int m, int n)
{
int a=0, b=0, c=0;
for (a=0, b=0, c=0; a<m && b<n;)
{
if (A[a]<B[b])
C[c++] = A[a++];
else
C[c++] = B[b++];
}
if (a<m)
while (a<m)
C[c++] = A[a++];
else
while (b<n)
C[c++] = B[b++];
}

Complete Program
/**
* Merge Sort
* http://www.tech-faq.com
*/

#include <stdio.h>

void mergesort(int [], int [], int [], int, int);


50
int main()
{
int A[50], B[50], C[100], m, n, i;
printf("How many elements do you want to create the first array"
"with? (max 50): ");
fflush(stdout);
scanf("%d", &m);
puts("Enter the array elements in ascending order:");
for (i=0; i<m; i++)
scanf("%d", &A[i]);

printf("\nHow many elements do you want to create the second array"


"with? (max 50): ");
scanf("%d", &n);
puts("Enter the array elements in ascending order:");
for (i=0; i<n; i++)
scanf("%d", &B[i]);
mergesort(A, B, C, m, n);
puts("\n------The sorted array is----");
for (i=0; i<m+n; i++)
printf("%d\n", C[i]);

return 0;
}

/**
* A and B are the input arrays which contain
* elements in ascending order. Their respective
* sizes are m and n. C is the output array
* that will contain the elements from the
* two arrays combined and in sorted order.
*/

void mergesort(int A[], int B[], int C[], int m, int n)


{
int a=0, b=0, c=0;
for (a=0, b=0, c=0; a<m && b<n;)
{
if (A[a]<B[b])
C[c++] = A[a++];
else
C[c++] = B[b++];
}
if (a<m)
while (a<m)
C[c++] = A[a++];
else
while (b<n)
C[c++] = B[b++];
}

Output

How many elements do you want to create the first arraywith? (max 50): 3
Enter the array elements in ascending order:
4
8
10

How many elements do you want to create the second arraywith? (max 50):
4
Enter the array elements in ascending order:

51
3
5
7
9

------The sorted array is----


3
4
5
7
8
9
10

Quick Sort
Quick sort is a divide-and-conquer sorting algorithm. To understand quick-sort, let us look at a high-level
description of the algorithm.

1. Divide: If the sequence S has 2 or more elements, select an element x from S to be your pivot. Any
arbitrary element, like the last, will do. Remove all the elements of S and divide them into 3
sequences:
a. L, which holds Ss elements less than x
b. E, which holds Ss elements equal to x
c. G, which holds Ss elements greater than x
2. Recurse: Recursively sort L and G
3. Conquer: Finally, to put elements back into S in order, first insert the elements of L, then those of E,
and then those of G.

Idea of Quick Sort


1. Select: pick a pivot element x
2. Divide: rearrange elements so that the x goes to its final position E
3. Recurse and Conquer: recursively sort

Illustration of the Divide Step

Let us choose the last element of the array, 50, as the pivot element. Our aim is to place this element in its
final position. In other words, all elements less than or equal to the pivot will be to its left and all elements
greater than or equal to the pivot will be to its right.

We will take the help of two variables: l and r. l scans the sequence from the left, and r from the right.

l is successively incremented until it reaches an element greater than or equal to the pivot.

r is successively decremented until it reaches an element that is less than the pivot.

Now, l and r both point to elements on the wrong sides, so a swap is performed. The swap is performed only
if l < r.

52
These steps are repeated until l >= r. Now, we swap the pivot element with the element at position l. This
concludes the partitioning procedure.

Algorithm

quicksort(a, l, r)

Begin
if(l<r) then
splitarray(a,l,r,loc) //partition the array
quicksort(a,l,loc-1) //recursively sort left sub array
quicksort(a,loc+1,r) //recursively sort right sub array
endif
end

Analysis of Quick Sort

Finding the location of the element that splits the array into two sections is an O(n) operation, because every
element in the array is compared to the pivot element.

After the division, each section is examined separately.

If the array is split approximately in half (an optimistic assumption), then there will be log2n splits.

53
Therefore, the total comparisons will be:
f(n) = n*log2n = O(nlog2n)

Complete Program
/**
* Quick Sort
* http://www.tech-faq.com
*/

#include <stdio.h>

void quicksort(int [], int );

int main()
{
int a[50], n, i;
printf("How many elements do you want to create the array with?"
"(max 50): ");
scanf("%d", &n);
fflush(stdout);
puts("Enter the array elements");
for (i=0; i<n; i++)
scanf("%d", &a[i]);
quicksort(a, n);
puts("--------Sorted Array--------");
for (i=0; i<n; i++)
printf("%d\n", a[i]);
printf("\n");

return 0;
}

void recursivequicksort(int [], int, int);


void quicksort(int arr[], int n)
{
recursivequicksort(arr, 0, n-1);
}

int partition(int [], int, int);


void recursivequicksort(int arr[], int low, int high)
{
int pivotpos; /*position of the pivot after partitioning*/
if (low<high)
{
pivotpos = partition(arr, low, high);
recursivequicksort(arr, low, pivotpos-1);
recursivequicksort(arr, pivotpos+1, high);
}
}

void swap(int [], int, int);


int partition(int arr[], int low, int high)
{
int down = low, up = high;
int pivot = arr[high];
while (down<up)
{
while(arr[down]<pivot && down<up)
down++;
while (arr[up]>=pivot)
up--;
if (down<up)
swap(arr, down, up);
}
54
arr[high] = arr[down];
arr[down] = pivot;
return down;
}

void swap(int arr[], int i, int j)


{
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j]=temp;
}

Output

How many elements do you want to create the array with?(max 50): 6
Enter the array elements
3
67
85
89
56
44
--------Sorted Array--------
3
44
56
67
85
89

Selection Sort
Selection sort requires (n-1) passes to sort an array.
In the first pass, we find the smallest element from a[0], a[1], a[2], a[n-1] and swap it with the first
element, i.e. a[0]. In the second pass, we find the smallest element from a[1], a[2], a[3].a[n-1] and swap it
with a[1] and so on.

Pass1.

1. Find the location loc of the smallest element in the entire array, i.e. a[0],[1],a[2]a[n-1].
2. Interchange a[0] & a[loc]. Then, a[0] is trivially sorted.

Pass2.

1. Find the location loc of the smallest element in the entire array, i.e. a[1],a[2]a[n-1].
2. Interchange a[1] & a[loc]. Then a[0], a[1] are sorted.

Pass k.

1. Find the location loc of the smallest element in the entire array, i.e. a[k],a[k+1],a[k+2]a[n-1].
2. Interchange a[k] & a[loc]. Then a[0],a[1],a[2],a[k] are sorted.

Pass n-1.

1. Find the location loc of the smaller of the elements a[n-2],a[n-1].


55
2. Interchange a[n-2] & a[loc]. Then elements a[0],a[1],a[2].a[n-1] are sorted.

56
57
Analysis of Selection Sort
The first pass requires n-1 comparisons to find the location of the smallest element.
The second pass requires n-2 comparisons.
The kth pass requires n-k comparisons.
The last pass requires only one comparison.

Therefore, the total number of comparisons are:


F(n)=(n-1)+(n-2)++(n-k)+3+2+1
=n(n-1)/2
Thus, the complexity is O(n2)

Sub Algorithm to Find the Smallest Element in the Array

Smallestelement(a,n,k,loc)
Here a is linear array of size n. This sub algorithm finds the location loc of smallest element among a[k-
1],a[k+1],a[k+2]a[n-1]. A temporary variable small is used to hold the current smallest element and j is
used as the loop control variable.

Begin
set small=a[k-1]
set loc=k-1
for j=k to (n-1) by 1 do
if(a[j]<small) then
set small = a[j]
set loc=j
endif
endfor
end

58
Selection Sort Algorithm

Selectionsort(a,n)
Here a is the linear array with n elements. This algorithm sorts elements into ascending order. It uses a
temporary variable temp to facilitate the exchange of two values and variable i is used as a loop control
variable

Begin
for i=1 to (n-1) by 1 do
call Smallestelement(a,n,I,loc)
set temp=a[i-1]
set a[i-1]=a[loc]
set a[loc]=temp
endfor
end

Complete Program
/**
* Selection Sort
* http://www.tech-faq.com
*/

#include <stdio.h>

void selectionsort(int [], int );

int main()
{
int a[50], n, i;
printf("How many elements do you want to create the array with?"
"(max 50): ");
scanf("%d", &n);
fflush(stdout);
puts("Enter the array elements");
for (i=0; i<n; i++)
scanf("%d", &a[i]);
selectionsort(a, n);
puts("--------Sorted Array--------");
for (i=0; i<n; i++)
printf("%d\n", a[i]);
printf("\n");

return 0;
}

void selectionsort(int arr[], int size)


{
int small, pos, tmp, i, j;
for (i=0; i<size; i++)
{
small = arr[i];
pos = i;
for (j=i+1; j<size; j++)
{
if (arr[j] < small)
{
small = arr[j];
pos = j;
}
}
tmp = arr[i];
arr[i] = arr[pos];
59
arr[pos] = tmp;
}
}

Output

How many elements do you want to create the array with?(max 50): 6
Enter the array elements
3
67
85
89
56
44
--------Sorted Array--------
3
44
56
67
85
89

Shell Sort
Invented by Donald Shell in 1959, Shell sort is the most efficient of the O(n2) class of sorting algorithms. It
is also the most complex of the O(n2) algorithms. This algorithm is similar to bubble sort in the sense that it
also moves elements by exchanges.

Shell sort begins by comparing elements that are at distance d. With this, the elements that are quite away
from their place will move more rapidly than the simple bubble sort.

In each pass, the value of d is reduced to half.

In each pass, each element is compared with the element that is located d indexes away from it, and an
exchange is made if required. The next iteration starts with a new value of d.

The algorithm terminates when d=1.

Example:

12 9 -10 22 2 35 40

Let us consider the starting value of d to be half the number of elements.


d = n/2 = 7/2 = 3

60
61
Analysis of Shell Sort
1. It is difficult to predict the complexity of Shell sort, as it is very difficult to show the effect of one
pass on another pass.
2. One thing is very clear that if the new distance d is computed using the above formula, then the
number of passes will approximately be log2d since d=1 will complete the sort.
3. Empirical studies have shown that the worst case complexity of Shell sort is O(n2).

C/C++ Implementation
void shellsort(int a[], int n)
{
int d, temp, i;
d=n/2;
while (d>=1)
{
for (i=0;i<n-d;i++)
{
if (a[i]>a[i+d])
{
temp=a[i];
a[i]=a[i+d];
a[i+d]=temp;
}
}
if(d==1)
return;
d=d/2.0+0.5;
}
}

Complete Program
/**
* Shell Sort
* http://www.tech-faq.com
*/

#include <stdio.h>
62
void shellsort(int [], int );

int main()
{
int a[50], n, i;
printf("How many elements do you want to create the array with?"
"(max 50): ");
scanf("%d", &n);
fflush(stdout);
puts("Enter the array elements");
for (i=0; i<n; i++)
scanf("%d", &a[i]);
shellsort(a, n);
puts("--------Sorted Array--------");
for (i=0; i<n; i++)
printf("%d\n", a[i]);
printf("\n");

return 0;
}

void shellsort(int a[], int n)


{
int d, temp, i;
d=n/2;
while (d>=1)
{
for (i=0;i<n-d;i++)
{
if (a[i]>a[i+d])
{
temp=a[i];
a[i]=a[i+d];
a[i+d]=temp;
}
}
if(d==1)
return;
d=d/2.0+0.5;
}
}

Output

How many elements do you want to create the array with?(max 50): 6
Enter the array elements
3
67
85
89
56
44
--------Sorted Array--------
3
44
56
67
85
89

63
Heaps
A heap is a binary tree that satisfies the following properties:

Shape property
Order property

By the shape property, we mean that a heap must be a complete binary tree. A complete binary tree has all its
levels filled, except possibly for the last, where the leaves must be located as far to the left as possible.

By the order property, we mean that for every node in the heap, the value stored in that node is greater than
or equal to the value in each of its children. A heap that satisfies this property is known as max heap.

Alternatively, every node in the heap can have a value less than or equal to the value in each of its children.
In this case, we have a min heap.

Heap Implementation
A heap is efficiently implemented using a linear array i.e. by sequential representation.
Since a heap is a complete binary tree, a heap of size n is represented in memory using a linear array
of size n.
A node at index i has its left child at 2i, right child at 2i+1 and parent at index i/2.

64
Operations on Heaps
Insertion
Deletion

Insertion of an Element in a Heap


An element is initially inserted as the last child of the heap.
On insertion, the heap remains a complete binary tree, but the order property may be violated.
We have to perform a series of operations to move the element up from the last position until either it
ends up in a position where the order property is satisfied or we hit the root node.
In this tutorial, we refer to this process as the reheapify upward operation.

65
Algorithm
ReheapifyUpward(heap,start)

Here heap is a linear array and start is the index of the element from where the reheapifyupward
operation is to start. It uses parent as the index of the parent node in the heap.

Begin
if heap[start] is not root node then
if (heap[parent]<heap[start]) then
swap heap[parent] and heap[start]
call ReheapifyUpward(heap,parent)
66
endif
endif
end

C/C++ implementation
void reheapifyUpward(int heap[],int start)
{
int temp, parent;
if(start>1)
{
parent=start/2;
if(heap[parent]<heap[start])
{
temp = heap[start];
heap[start] = heap[parent];
heap[parent] = temp;
reheapifyUpward(heap,parent);
}
}
}

Deleting an Element from a Heap


Deleting an Element from the Heap
Deletion always occurs at the root of the heap.
If we delete the root element it creates a hole or vacant space at the root position.
Because the heap must be complete, we fill the hole with the last element of the heap.
Although the heap becomes complete, i.e. it satisfies the shape property, the order property of heaps
is violated.
As the value that comes from the bottom is small, we have to perform another operation to satisfy the
order property.
This operation involves moving the element down from the root position until either it ends up in a
position where the order property is satisfied or it hits a leaf node.
In this tutorial, we refer to this operation as the reheapify downward operation.

67
Algorithm
ReheapifyDownward(heap,start,finish)

Here heap is a linear array, start is the index of the element from where the reheapify downward
operation is to start, and finish is the index of the last element of the heap. The variable index is used
to keep track of the index of the largest child.

Begin
if heap[start] is not leaf node then
set index=index of the child with largest value
if(heap[start]<heap[index]) than
swap heap[start] and heap[index]
call ReheapifyDownward(heap,index,finish)
endif
endif
end

C/C++ Implementation
void reheapifyDownward(int heap[],int start,int finish)
{
int index,lchild,rchild,maximum,temp;
lchild=2*start; /*index of the left child*/
rchild=lchild+1; /*index of the right child*/
if(lchild<=finish)
{
maximum=heap[lchild];
index=lchild;
if(rchild<=finish)
{
if(heap[rchild]>maximum)

68
{
maximum=heap[rchild];
index=rchild;
}
}
if(heap[start<heap[index])
{
temp=heap[start];
heap[start]=heap[index];
heap[index]=temp;
reheapifyDownward(heap,index,finish)
}
}
}

Coding the Function for Deletion

Deletion from the heap is done through the following steps:

Assign the value of the root to a temporary variable, which can be returned from the function for
further processing.
Bring the last element of the heap to the root node position.
Reduce the size of the heap by one.
Apply the reheapify downward operation from the root node.

DeleteElement(heap,n,item)

Here heap is a linear array representing a heap with n elements. This algorithm deletes the element from the
root of the heap and assigns it to item (an output parameter). Note that the code assumes that the array index
begins from 1 and ends at n.

Begin
set item=heap[1]
set heap[1]=heap[n]
set n=n-1;
call reheapifyDownward(heap,1,n)
End

C/C++ Implementation
int deleteElement(int heap[],int *n)
{
int temp;
temp=heap[1];
heap[1]=heap[*n];
--*n;
reheapifydownward(heap,1,n);
return temp;
}

69

Você também pode gostar