Sunteți pe pagina 1din 198

Data ABI 301

Structures
Through
‘C’
Language
ACeL
It is important for every Computer Science student to
understand the concept of Information and how it is
AMITY
organized or how it can be utilized. UNIVERSITY
Preface

This Course gives the in-depth knowledge to the use, design, and analysis of data
structures in computer programs. The very commonly used data structures like arrays,
stacks, queues, lists, trees, hashing and graphs will be discussed in detail. Sorting and
hashing are important topics in the study of algorithms. They are also closely related to
the design of data structures. Several algorithms to implement these techniques are
included in the Syllabus
Syllabus

Course Contents:
Module I: Basic concepts of data representation

Abstract data types: Fundamental and derived data types, Representation, Primitive Data
Structures.

Module II: Arrays

Representation of arrays single and multi dimensional arrays. Address calculation using
column and rows major ordering. Various operations on arrays, Vector, Application of
arrays: matrix multi multiplication, sparse polynomial and addition.

Module III: Stacks and Queues

Representation of stacks and queues using arrays and linked list. Circular queues, priority
queue and D-queue. Application of stacks: conversion from infix to postfix expression,
Evaluation of postfix expression using stacks.

Module IV: Linked List

Singly linked list; operations on list. Linked stacks and queue. Polynomial
representations and manipulation using linked lists, doubly linked list, addition of two
polynomials.
Module V: Trees

Binary trees traversal method: preorder, in-order, post-ordered traversal. Recursive and
non-recursive algorithm for above mentioned Traversal methods. Representation of trees
and its application: Binary tree representation of a tree, Binary search tree: height
balanced (AVL) tree

Module VI: Searching, sorting and complexity

Searching: Sequential and binary search, indexed search, Sorting: insertion, selection,
bubble, quick, merge, heap sort.

Module VII: Graphs

Graph representation: adjacency list, adjacency multicasts, adjacency lists. Traversal


scheme: Depth first search, Breadth first search. Spanning tree: definition, minimal
spanning tree algorithms.
Table of Contents
Preface ............................................................................................................................................ 2
Syllabus .......................................................................................................................................... 3
Module I : Basic Concepts of Data Representation ................................................................... 8
1.1 Introduction to Data & Data Structures ................................................................................. 8
1.2 What is Data Structure? ....................................................................................................... 10
1.3 Abstract Data Types ............................................................................................................ 13
MODULE II : Arrays ............................................................................................................... 19
2.1 Representation of Arrays ..................................................................................................... 19
MODULE L III : Stack and Queues ....................................................................................... 33
Lists ........................................................................................................................................... 33
3.1 Introduction to stack ............................................................................................................ 33
3.3 Stack Declaration Using an Array ....................................................................................... 35
3.4 Representation of Stack in memory .................................................................................... 36
3.5 Creating an Empty Stack ..................................................................................................... 36
3.6 Push Operation .................................................................................................................... 39
3.7 Pop Operation ...................................................................................................................... 39
3.8 /* C: Program implements array as a stack. */ .................................................................... 40
3.9 Stack as a Linked List.......................................................................................................... 43
3.10 Applications of Stack ........................................................................................................ 46
3.11 Introduction to Queues ...................................................................................................... 62
3.12 Queues in Programming .................................................................................................... 63
3.13 Operations on Queues........................................................................................................ 65
3.14 Array Representation ......................................................................................................... 65
3.15 Types of Queues ................................................................................................................ 66
3.16 Circular Queue ................................................................................................................. 69
3.17 Priority Queue ................................................................................................................... 88
MODULE IV : Linked list ....................................................................................................... 95
4.1 Linked List .......................................................................................................................... 95
4.2 Operations on linked list...................................................................................................... 95
4.3 Types of Link List ............................................................................................................. 103
4.3.1 Singly Linked List ...................................................................................................... 103
4.3.2 Circular Linked List ................................................................................................... 107
4.3.3 Doubly Linked List..................................................................................................... 114
4.4 Freeing up the Entire List .................................................................................................. 123
MODULE V : Trees ................................................................................................................ 125
5.1 Tree.................................................................................................................................... 125
5.2 Tree characteristics ............................................................................................................ 126
5.3 Binary Tree ........................................................................................................................ 126
5.3 (a) Binary Tree Terminology......................................................................................... 128
5.3 (b) Operations on Binary Tree ....................................................................................... 129
5.3(c )Searching a node ....................................................................................................... 130
5.4 Binary search- inserting a node ......................................................................................... 133
5.5 Binary search- deleting a node .......................................................................................... 135
5.6 Traversal of a Binary Tree................................................................................................. 140
5.7 Threaded Binary Tree ........................................................................................................ 143
5.8 AVL Tree .......................................................................................................................... 145
5.8.1 Insertion of a node in an AVL Tree............................................................................ 147
5.8.2 Deletion of a Node in AVL Tree ................................................................................ 148
MODULE VI : Searching , Sorting and Complexity ............................................................ 150
6.1 Searching ........................................................................................................................... 150
6.2 Linear Search ..................................................................................................................... 150
6.2 (a) Algorithm ................................................................................................................. 150
6.2 (b)C Implementation of the Algorithm.......................................................................... 151
6.2 (c) Analysis of Linear Search ........................................................................................ 151
6.3 Binary search ..................................................................................................................... 152
6.4 Sorting ............................................................................................................................... 156
(A) Algorithm .......................................................................................................................... 158
MODULE VII : Graph ............................................................................................................. 177
7.1 Introduction to Graph ........................................................................................................ 177
7.2 Graph Theory Terminologies ............................................................................................ 178
7.3 Sequential representation of Graph ................................................................................... 180
7.3.1 Adjacency Matrices .................................................................................................... 180
7.3.2 Adjacency List ............................................................................................................ 181
7.3.3 Graph Traversal .......................................................................................................... 183
7.4 Spanning Tree.................................................................................................................... 186
7.5 Constructing a Spanning Tree ........................................................................................... 186
Problem for Practice ................................................................................................................ 191
Reference Books...................................................................................................................... 198
Module I : Basic Concepts of Data Representation

1.1 Introduction to Data & Data Structures

It is important for every Computer Science student to understand the concept of


Information and how it is organized or how it can be utilized.

What is information?

If we arrange some data in appropriate sequence, then it forms a Structure and gives us a
meaning. This meaning is called information. The basic unit of information in computer
Science is a bit, binary digit.

So we found two things in information: One is Data and the other is Structure

Data

Data is derived from a Latin word “Datum” which means collection.

So data can be defined as collection of facts and figures.

Data is classified into two types.

1. Group data Item


2. Elementary data Item

Group Data Item

Data item that can be subdivided into different segments is called group data item.
For example, name is a group data item because it can be subdivided into different
segments i.e. First name, middle name, last name

Elementary Data Item

The data item that cannot be subdivided into different segments is called elementary data
item

For example, Account No, ID Number etc.

Field
Field is the collection of related character.

For example, Name, Roll No, Class etc.

A single field cannot provide full information about any entity.

Record
Record is the collection of related fields.

For example, the record of student includes its Roll No, Name, Class, and

Registration No. etc


Table/ Relation/ File

Collection of related records is called a file or table.

For example

Rno Name Class

1 Muhammad 3rd Year

2 Imran 3rd Year

3 Hussain 3rd Year

1.2 What is Data Structure?

A data structure is a systematic way of organizing and accessing data.

A data structure tries to structure data! Usually more than one piece of data. Should
define legal operations on the data The data might be grouped together (e.g. in an linked
list) .

When we define a data structure we are in fact creating a new data type of our own. i.e.
using predefined types or previously user defined types. Such new types are then used to
reference variables type within a program

Why Data Structures?

1. Data structures study how data are stored in a computer so that operations can be
implemented efficiently
2. Data structures are especially important when you have a large amount of
information
3. Conceptual and concrete ways to organize data for efficient storage and
manipulation.

Types of Data Structure

There are two types of data structure

1) Linear Data Structure


2) Non Linear Data Structure

1) Linear Data Structure


When the data is to be stored in the memory in linear form is called linear data
structure.

For example, Array, Stack, Queue, Link List etc.

2) Non Linear Data Structure

When the data is to be stored in the computer memory in dispersed order is called
non-linear data structure.

For example, trees, files, graphs etc.

Classification of Linear Data Structure

It can be classified into two types.

1) Physical Data Structure


2) Logical Data Structure
1) Physical Data Structure
When the data is to be stored in the computer memory in proper order is called

physical data structure.

Example: -

When we insert the data in one-dimensional array, then the data is stored
in the memory in that way, as we insert the data in sequence.

2) Logical Data Structure


The two-dimensional array is called logical data structure because we are not
sure, that in which format data in stored in the memory, then we represent the data
in the memory at row major order or column major order, so due to this reason it
is called logical data structure.

Operation Perform on Data Structure

When data is proceeding then different type of operation perform. Following are most
important operation, which play major role in data structure.
1) Traversing
2) Searching
3) Sorting
4) Insertion
5) Deletion
6) Merging
7) Copying

1) Traversing
Accessing of records is called traversing. It is also known as
visiting of records.
2) Searching: -
Finding the location of record with given value is called Searching.

3) Sorting
The arrangement of data in ascending or descending order is called
sorting.

4) Insertion
Adding new record to structure is called inserting.

5) Deletions
Removing a record or set of records form data structure

6) Merging
When two or more than two records are combined, this process is called
merging.

7) Copying
The creation of duplicate data item is called copying process.

1.3 Abstract Data Types

Data can be of many types e.g., integer, fraction or real, character, string etc.
• Numbers that does not have fraction part represents integer data.
• Numbers with decimals or fraction represents real data.
• Any data which is enclosed within single quotes represents character data.
Any data which is enclosed within double quotes represents a string

Since the data is of many types, therefore C language provides many ways and options to
handle all kind of data.

C Programming Language provides ways to handle different types of data by providing


data types.

What is Data Type

“Data types” are defined as ways or means to identify the type of data and associated
operations of handling it.

Data types can be divided into two types:

Fundamental or Built-in data types (Primary Data Types)

Derived data types ( Secondary Data Types)

User Defined data types

Built-in Data Types:

The basic built-in or fundamental data types that are available in C are:

1. Char
2. Int
3. Float
4. Double
5. Void
1. Integer int

2. Character char

3. Floating Point float

4. Double precision floating point double

5. Void void

Integer Type :

Integers are whole numbers with a machine dependent range of values. A good
programming language as to support the programmer by giving a control on a range of
numbers and storage space. C has 3 classes of integer storage namely short int, int and
long int. All of these data types have signed and unsigned forms. A short int requires half
the space than normal integer values. Unsigned numbers are always positive and
consume all the bits for the magnitude of the number. The long and unsigned integers are
used to declare a longer range of values.

Floating Point Types :

Floating point number represents a real number with 6 digits precision. Floating point
numbers are denoted by the keyword float. When the accuracy of the floating point
number is insufficient, we can use the double to define the number. The double is same as
float but with longer precision. To extend the precision further we can use long double
which consumes 80 bits of memory space.

Void Type :

Using void data type, we can specify the type of a function. It is a good practice to avoid
functions that does not return any values to the calling function.
Character Type :

A single character can be defined as a defined as a character type of data. Characters are
usually stored in 8 bits of internal storage. The qualifier signed or unsigned can be
explicitly applied to char. While unsigned characters have values between 0 and 255,
signed characters have values from –128 to 127.

Size and Range of Data Types on 16 bit machine.

TYPE SIZE Range


(Bits)

Char or Signed Char 8 -128 to 127

Unsigned Char 8 0 to 255

Int or Signed int 16 -32768 to 32767

Unsigned int 16 0 to 65535

Short int or Signed short 8 -128 to 127


int

Unsigned short int 8 0 to 255


Long int or signed long int 32 -2147483648 to 2147483647

Unsigned long int 32 0 to 4294967295

Float 32 3.4 e-38 to 3.4 e+38

Double 64 1.7e-308 to 1.7e+308

Long Double 80 3.4 e-4932 to 3.4 e+4932

Derived Data Types:

Derived data types are those datatypes which are derived from the fundamental data
types using some declaration operators.The basic derived types that are available in C
are:

1. Array
2. Functions and pointers
3. Structures
4. Classes etc..
User defined Data Types:

With the help of a user defined data type, a programmer can create an
identifier that denotes an already existing data type. The programmer
defined datatype identifier is then used in a program to declare variables.
MODULE II : Arrays

2.1 Representation of Arrays

The C Language provides a capability that ensures the user to design a set of similar Data
types, called Array.

In computer programming, a group of homogeneous elements of a specific data type is


known as an array, one of the simplest data structures. Arrays hold a series of data
elements, usually of the same size and data type. Individual elements are accessed by
their position in the array. The position is given by an index, which is also called a
subscript. The index usually uses a consecutive range of integers, (as opposed to an
associative array) but the index can have any ordinal set of values

Some arrays are multi-dimensional, meaning they are indexed by a fixed number of
integers, for example by a tuple of four integers. Generally, one- and two-dimensional
arrays are the most common. Most programming languages have a built-in array data
type.
Array

Array is the collection of finite homogeneous type of data, which are stored in successive
memory locations.The memory locations are identifies by a name which is called the
array name.

The data items of the array are called the elements of array and each data item is stored in
separate memory location.Each memory location within the array is identifies by its
position value, which is called the index of the array.

110 112 114 116 118 120 Memory


Location

22 11 13 52 21 05

x[1] x[2] x[3] x[4] x[5] x[6] Element

index

Types of array

There are two types of array.


1) One dimensional array
2) Two Dimensional array

1) One Dimensional Array (Linear Array)


One-dimensional array represent the data in the form of vector, list or linear form. It is
used to store similar type of data consisting of only one row or column.
110 112 114 116 118 120 Memory
Location

22 11 13 52 21 05

x[1] x[2] x[3] x[4] x[5] x[6] Element

index

2) Two Dimensional Array (Matrix)


Two-dimensional array represent the data in the form of table or matrix.In two-
dimensional array the storage of elements can be take place by two techniques.
i) Row Major Order
ii) Column Major Order

Row Major Order


To understand the concept of row major order, consider the following matrix of 2 X 3.
12 9 16

5 11 22

Now the storage of above matrix in row major order is

110 112 114 116 118 120


12 9 16 5 11 22
X(1,1) x(1,2) x(1,3) x(2,1) x(2,2) x(2,3)

Column Major Order


To understand the concept of column major order, consider the following matrix of 2 X 3.

12 9 16

5 11 22
Now the storage of above matrix in column major order is

110 112 114 116 118 120

12 5 9 11 16 22

X(1,1) x(2,1) x(1,2) x(2,2) x(1,3) x(2,3)

Accessing/ Retrieving the elements of array from computer memory.

Vector Method

This method is used to access the elements of array by using the proper formula, which
access the memory address of the required element.

Accessing of One-Dimensional Array Element

One-Dimensional array can be accessed by using the following formula.

MA(i) = BA + w (i–1)
Where
MA = Required Memory Address
BA = Starting Memory Address
i = index
w = word size / Length
Example:

Consider the following array

110 112 114 116 118 120

22 11 13 52 21 05

x[1] x[2] x[3] x[4] x[5] x[6]

Now, suppose we have to find the memory address of x[4], then

MA(i) = BA + w (i-1)
MA(4) = 110 + 2 (4 –1)
= 110 + 6
= 116
So the required memory address of x[4] is 116 and the required element is 52.

Accessing of Two-Dimensional Array Element


The element of two-dimensional array can be accessed in two ways.

1)Accessing 2-D array in Row Major Order

To understand the concept of row major order, consider the following matrix of 2 X 3.

12 9 16
5 11 22

Now the storage of above matrix in row major order is

110 112 114 116 118 120


12 9 16 5 11 22
x(1,1) x(1,2) x(1,3) x(2,1) x(2,2) x(2,3)
Suppose we have to access x(2,1) element memory location, then we use the following
formula.

MA(i,j) = BA + w[ n (i-1) + (j-1) ]


Where
“i” represent row and “j” represent column and “n” are the total numbers of
columns.
Now
MA(2,1) = 110 + 2[ 3 (2-1) + (1-1) ]
= 110 + 6
= 116
Which is required memory address.

2) Accessing 2-D array in Column Major Order


To understand the concept of column major order, consider the following matrix of 2 X
3.
12 9 16
5 11 22

Now the storage of above matrix in column major order is

110 112 114 116 118 120


12 5 9 11 16 22
x(1,1) x(2,1) x(1,2) x(2,2) x(1,3) x(2,3)

Suppose we have to access x(1,3) element memory location, then we use the formula.
MA (i,j) = BA + w[ m(j-1) + (i-1) ]
Where “m” is the total number of rows.
Now
MA (1,3) = 110 + 2[ 2(3-1) + (1-1)]
= 110 + 8
= 118
Which is the required memory location.

TRAVERSING OF ARRAY ELEMENT


Algorithm Traversing (X, LB, UB, N)

[This algorithm is used to traverse the element of array “X” where

N = Total Number where N=UB

LB = Lower Bound

UB = Upper Bound]

I = Loop Counter

Step-1 [initialize the loop counter]

Repeat step-2 for I=LB to UB

Step-2 [Process the Element of Array]

Print  X [I]

Step-3 [Finished]

Exit

ALGORITHM FOR THE INSERTION OF ELEMENT AT THE END


OF ARRAY.

Algorithm InsertionEnd (X, I, N, K, Item)


[This algorithm is used to insert the element at the end of array where

X = Array Name
I = Loop Variable

K = Position where element is to be stored

N = Total Element

Item = Element which is to be inserted. ]

Step-1 [Read an array using for loop]

Repeat Step-2 For I = 1 To N

Step-1 [Read Element]

Read X [I]

End of Loop

Step-3 [Input location where element is to be inserted]

Read (K)

Step-4 [Perform check for Position]

If (K<= N) then

Goto step-7

Step-5 [Insert the new element]

X[K]  Item

Step-6 [Reset the value of N]

N=N+1
Step-7 [Finished]

Exit

ALGORITHM FOR THE DELETION OF ELEMENT AT THE END


OF ARRAY.

Algorithm DeletionEnd (X, I, N, K, Item)

[This algorithm is used to delete the element at the end of array where

X = Array Name

I = Loop Variable

K = Position where element is to be deleted

N = Total Element

Item = Element which is to be deleted ]

Step-1 [Read an array using for loop]

Repeat Step-2 For I = 1 To N

Step-1 [Read Element]

Read X [I]

End of Loop
Step-3 [Input location where element is to be Deleted]

Read (K)

Step-4 [Perform check for Position]

If (K= = N) then

Item  X[K]

Else

Goto step-6

Step-5 [Reset the value of N]

NN-1

Step-6 [Finished]

Exit

/*Program to implement an array. */

#include <stdio.h>

#include <conio.h>

#define MAX 5

void insert ( int *, int pos, int num ) ;


void del ( int *, int pos ) ;

void reverse ( int * ) ;

void display ( int * ) ;

void search ( int *, int num ) ;

void main( )

int arr[5] ;

clrscr( ) ;

insert ( arr, 1, 11 ) ;

insert ( arr, 2, 12 ) ;

insert ( arr, 3, 13 ) ;

insert ( arr, 4, 14 ) ;

insert ( arr, 5, 15 ) ;

printf ( "\nElements of Array: " ) ;

display ( arr ) ;

del ( arr, 5 ) ;

del ( arr, 2 ) ;

printf ( "\n\nAfter deletion: " ) ;

display ( arr ) ;

insert ( arr, 2, 222 ) ;


insert ( arr, 5, 555 ) ;

printf ( "\n\nAfter insertion: " ) ;

display ( arr ) ;

reverse ( arr ) ;

printf ( "\n\nAfter reversing: " ) ;

display ( arr ) ;

search ( arr, 222 ) ;

search ( arr, 666 ) ;

getch( ) ;

/* inserts an element num at given position pos */

void insert ( int *arr, int pos, int num )

/* shift elements to right */

int i ;

for ( i = MAX - 1 ; i >= pos ; i-- )

arr[i] = arr[i - 1] ;

arr[i] = num ;

/* deletes an element from the given position pos */

void del ( int *arr, int pos )


{

/* skip to the desired position */

int i ;

for ( i = pos ; i < MAX ; i++ )

arr[i - 1] = arr[i] ;

arr[i - 1] = 0 ;

/* reverses the entire array */

void reverse ( int *arr )

int i ;

for ( i = 0 ; i < MAX / 2 ; i++ )

int temp = arr[i] ;

arr[i] = arr[MAX - 1 - i] ;

arr[MAX - 1 - i] = temp ;

}}

/* searches array for a given element num */

void search ( int *arr, int num )

/* Traverse the array */


int i ;

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

if ( arr[i] == num )

printf ( "\n\nThe element %d is present at %dth position.", num,

i+1);

return ;

}}

if ( i == MAX )

printf ( "\n\nThe element %d is not present in the array.", num ) ;

/* displays the contents of a array */

void display ( int *arr )

/* traverse the entire array */

int i ;

printf ( "\n" ) ;

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

printf ( "%d\t", arr[i] ) ;

}
MODULE L III : Stack and Queues

Lists

The arrangement of elements in some sequence is called list. There are two types
of list.

i) Ordered List
ii) Unordered List

i) Ordered List
The proper sequence of elements is called ordered list. For example

The days of week, the month of year, seasons of year.

ii) Unordered List


In this type of list the storage of element is not take place in proper order.

For example Stack, Queue, Dequeue, Circular Queue, Link List

3.1 Introduction to stack

A stack is a linear Structure in which item may be added or removed only at one end.
There are certain frequent situations in computer science when one wants to restrict
insertions and deletions so that they can take place only at the beginning or the end of the
end of the list, not in the middle.

Two of the Data Structures that are useful in such situations are Stacks and queues.

A stack is a list of elements in which an elements may be inserted or deleted only at one
end, called the Top. This means, in particular, the elements are removed from a stack in
the reverse order of that which they are inserted in to the stack. The stack also called
"last-in first -out (LIFO) " list.
Special terminology is used for two basic operation associated with stack :

1. "Push" is the term used to insert an element into a stack.


2. "Pop" is the term used to delete an element from a stack

3.2 Operations on Stacks

• createstack(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
Two Major Operation of the Stack

1) Over Flow
When the stack is full and new element is inserted/ Push then overflow condition
occur.

2) Under Flow
When the stack is empty, and a deletion process is performed then under flow
condition occur.

Example

i) Pile of Trays in Cafeteria


ii) Stack of iron shorts
iii) Pile of bricks
iv) Pile of books in Library

3.3 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;

• Here we have defined our own data type named stack.


• The first element “top” will be used to index the topmost element
• Array “elements” holds the elements of the stack
• The last line declares a variable “s” of type stack
3.4 Representation of Stack in memory

3.5 Creating an Empty Stack

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.

ALGORITHM FOR THE INSERTION OF ELEMENT IN THE


STACK

Algorithm push (stack, N, item, Top)

[This algorithm is used to insert the element in the stack where


Stack = Array
N = Total number of elements
Item = which is to be inserted
Top = Top pointer/ Counter ]

Step-1 [Check for overflow]


If (Top >= 4) then
Write (“ Over Flow”)
Goto step-4
End if

Step-2 [Increment the Top Pointer]


Top = Top + 1

Step-3 [Insert the element into stack]


Stack [Top] = item
Goto setp-1

Step-4 [Finished]
Exit
ALGORITHM FOR THE DELETION OF ELEMENT FROM THE
STACK

Algorithm pop (stack, item, Top)

This algorithm is used to delete a element from the stack where

Stack = Array

Item = which is to be inserted

Top = Top pointer/ Counter ]

Step-1 [Check for under flow]

If (Top = 0) then

Write (“ Under Flow”)

Goto step-4

End if

Step-2 [Delete the item from stack]

Item  stack [Top]

Step-3 [Decrement the Top Pointer]

Top = Top – 1

Goto step-1
3.6 Push Operation

Before the push operation, if the stack is empty, then the value of the top will be - 1 and if
the stack is not empty then the value of the top will be the index of the element currently
on the top. Therefore, when we place the value onto the stack, the value of top is
incremented so that it points to the new top of stack, where incoming element is placed.

void push(stack *ps, int value)

ps->top++;

ps->elements[ps->top]=value;

3.7 Pop Operation

The element on the top of the stack is assigned to a local variable, which later on will be
returned via the return statement. After assigning the top element to a local variable, the
variable top is decremented so that it points to a new top

int pop(stack *ps)

int temp;

temp=ps->elements[ps->top];

ps->top--;

return temp;

}
3.8 /* C: Program implements array as a stack. */

#include <stdio.h>

#include <conio.h>

#define MAX 10

struct stack

int arr[MAX] ;

int top ;

};

void initstack ( struct stack * ) ;

void push ( struct stack *, int item ) ;

int pop ( struct stack * ) ;

void main( )

struct stack s ;

int i ;

clrscr( ) ;

initstack ( &s ) ;

push ( &s, 11 ) ;

push ( &s, 23 ) ;

push ( &s, -8 ) ;
push ( &s, 16 ) ;

push ( &s, 27 ) ;

push ( &s, 14 ) ;

push ( &s, 20 ) ;

push ( &s, 39 ) ;

push ( &s, 2 ) ;

push ( &s, 15 ) ;

push ( &s, 7 ) ;

i = pop ( &s ) ;

printf ( "\n\nItem popped: %d", i ) ;

i = pop ( &s ) ;

printf ( "\nItem popped: %d", i ) ;

i = pop ( &s ) ;

printf ( "\nItem popped: %d", i ) ;

i = pop ( &s ) ;

printf ( "\nItem popped: %d", i ) ;


i = pop ( &s ) ;

printf ( "\nItem popped: %d", i ) ;

getch( ) ;

/* intializes the stack */

void initstack ( struct stack *s )

s -> top = -1 ;

/* adds an element to the stack */

void push ( struct stack *s, int item )

if ( s -> top == MAX - 1 )

printf ( "\nStack is full." ) ;

return ;

s -> top++ ;

s -> arr[s ->top] = item ;

}
/* removes an element from the stack */

int pop ( struct stack *s )

int data ;

if ( s -> top == -1 )

printf ( "\nStack is empty." ) ;

return NULL ;

data = s -> arr[s -> top] ;

s -> top-- ;

return data ;

3.9 Stack as a Linked List

When implemented as an array, stack suffers from the basic limitation of an array- that its
size cannot be increased or decreased once it is declared. As a result, one ends up
reserving either too much space or too less space for an array and in turn for a stack. This
problem can be overcome if we implement a stack using a linked list. In case of a linked
stack we shall push and pop nodes from one end of a linked list.
The stack as linked list is represented as a singly connected list. Each node in the linked
list contains the data and a pointer that gives location of the next node in the list.
3.9 (a)/* C: Program implements linked list as a stack. */

#include <stdio.h>
#include <conio.h>
#include <alloc.h>

/* structure containing data part and linkpart */


struct node
{
int data ;
struct node *link ;
};
void push ( struct node **, int ) ;
int pop ( struct node ** ) ;
void delstack ( struct node ** ) ;
void main( )
{
struct node *s = NULL ;
int i ;
clrscr( ) ;
push ( &s, 14 ) ;
push ( &s, -3 ) ;
push ( &s, 18 ) ;
push ( &s, 29 ) ;
push ( &s, 31 ) ;
push ( &s, 16 ) ;

i = pop ( &s ) ;
printf ( "\nItem popped: %d", i ) ;

i = pop ( &s ) ;
printf ( "\nItem popped: %d", i ) ;

i = pop ( &s ) ;
printf ( "\nItem popped: %d", i ) ;
delstack ( &s ) ;
getch( ) ;
}

/* adds a new node to the stack as linked list */


void push ( struct node **top, int item )
{
struct node *temp ;
temp = ( struct node * ) malloc ( sizeof ( struct node ) ) ;
if ( temp == NULL )
printf ( "\nStack is full." ) ;

temp -> data = item ;


temp -> link = *top ;
*top = temp ;
}

/* pops an element from the stack */


int pop ( struct node **top )
{
struct node *temp ;
int item ;
if ( *top == NULL )
{
printf ( "\nStack is empty." ) ;
return NULL ;
}
temp = *top ;
item = temp -> data ;
*top = ( *top ) -> link ;
free ( temp ) ;
return item ;
}
/* deallocates memory */
void delstack ( struct node **top )
{
struct node *temp ;
if ( *top == NULL )
return ;
while ( *top != NULL )
{
temp = *top ;
*top = ( *top ) -> link ;
free ( temp ) ;
}
}

3.10 Applications of Stack

The place where stacks are frequently used is in evaluation of arithmetic expression. An
arithmetic expression consists os operands and operators. The operands can be numeric
values or numeric variables. The operators used in an arithmetic expression represent the
operations like addition, subtraction, multiplication, division

When higher level programming languages came into existence one of the major hurdles
faced by the computer scientists was to generate machine language instructions that
would properly evaluate any arithmetic expression. To convert complex assignment
statement such as

X= A/B+C*D-F*G/Q

3.10 (a) 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, we’ll 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 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

Here’s 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$*/-

/* C: Program to convert an Infix form to Postfix form */

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#define MAX 50
struct infix
{
char target[MAX] ;
char stack[MAX] ;
char *s, *t ;
int top ;
};
void initinfix ( struct infix * ) ;
void setexpr ( struct infix *, char * ) ;
void push ( struct infix *, char ) ;
char pop ( struct infix * ) ;
void convert ( struct infix * ) ;
int priority ( char ) ;
void show ( struct infix ) ;
void main( )
{
struct infix p ;
char expr[MAX] ;
initinfix ( &p ) ;
clrscr( ) ;
printf ( "\nEnter an expression in infix form: " ) ;
gets ( expr ) ;

setexpr ( &p, expr ) ;


convert ( &p ) ;
printf ( "\nThe postfix expression is: " ) ;
show ( p ) ;
getch( ) ;
}

/* initializes structure elements */


void initinfix ( struct infix *p )
{
p -> top = -1 ;
strcpy ( p -> target, "" ) ;
strcpy ( p -> stack, "" ) ;
p -> t = p -> target ;
p -> s = "" ;
}
/* sets s to point to given expr. */
void setexpr ( struct infix *p, char *str )
{
p -> s = str ;
}
/* adds an operator to the stack */
void push ( struct infix *p, char c )
{
if ( p -> top == MAX )
printf ( "\nStack is full.\n" ) ;
else
{
p -> top++ ;
p -> stack[p -> top] = c ;
}}

/* pops an operator from the stack */


char pop ( struct infix *p )
{
if ( p -> top == -1 )
{
printf ( "\nStack is empty.\n" ) ;
return -1 ;
}
else
{
char item = p -> stack[p -> top] ;
p -> top-- ;
return item ;
}
}
/* converts the given expr. from infix to postfix form */
void convert ( struct infix *p )
{
char opr ;

while ( *( p -> s ) )
{
if ( *( p -> s ) == ' ' || *( p -> s ) == '\t' )
{
p -> s++ ;
continue ;
}
if ( isdigit ( *( p -> s ) ) || isalpha ( *( p -> s ) ) )
{
while ( isdigit ( *( p -> s ) ) || isalpha ( *( p -> s ) ) )
{
*( p -> t ) = *( p -> s ) ;
p -> s++ ;
p -> t++ ;
}}
if ( *( p -> s ) == '(' )
{
push ( p, *( p -> s ) ) ;
p -> s++ ;
}
if ( *( p -> s ) == '*' || *( p -> s ) == '+' || *( p -> s ) == '/' || *( p -> s ) == '%' || *( p -> s )
== '-' || *( p -> s ) == '$' )
{
if ( p -> top != -1 )
{
opr = pop ( p ) ;
while ( priority ( opr ) >= priority ( *( p -> s ) ) )
{
*( p -> t ) = opr ;
p -> t++ ;
opr = pop ( p ) ;
}
push ( p, opr ) ;
push ( p, *( p -> s ) ) ;
}
else
push ( p, *( p -> s ) ) ;
p -> s++ ;
}

if ( *( p -> s ) == ')' )
{
opr = pop ( p ) ;
while ( ( opr ) != '(' )
{
*( p -> t ) = opr ;
p -> t++ ;
opr = pop ( p ) ;
}
p -> s++ ;
}}
while ( p -> top != -1 )
{
char opr = pop ( p ) ;
*( p -> t ) = opr ;
p -> t++ ;
}
*( p -> t ) = '\0' ;
}

/* returns the priority of an operator */


int priority ( char c )
{
if ( c == '$' )
return 3 ;
if ( c == '*' || c == '/' || c == '%' )
return 2 ;
else
{
if ( c == '+' || c == '-' )
return 1 ;
else
return 0 ;
}}

/* displays the postfix form of given expr. */


void show ( struct infix p )
{
printf ( " %s", p.target ) ;
}

/* .C: Program to evaluate an epression entered in postfix form */

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <math.h>

#include <ctype.h>

#define MAX 50

struct postfix

int stack[MAX] ;

int top, nn ;

char *s ;

};

void initpostfix ( struct postfix * ) ;

void setexpr ( struct postfix *, char * ) ;

void push ( struct postfix *, int ) ;

int pop ( struct postfix * ) ;


void calculate ( struct postfix * ) ;

void show ( struct postfix ) ;

void main( )

struct postfix q ;

char expr[MAX] ;

clrscr( ) ;

initpostfix ( &q ) ;

printf ( "\nEnter postfix expression to be evaluated: " ) ;

gets ( expr ) ;

setexpr ( &q, expr ) ;

calculate ( &q ) ;

show ( q ) ;

getch( ) ;

/* initializes data members */

void initpostfix ( struct postfix *p )

p -> top = -1 ;

}
/* sets s to point to the given expr. */

void setexpr ( struct postfix *p, char *str )

p -> s = str ;

/* adds digit to the stack */

void push ( struct postfix *p, int item )

if ( p -> top == MAX - 1 )

printf ( "\nStack is full." ) ;

else

p -> top++ ;

p -> stack[p -> top] = item ;

/* pops digit from the stack */

int pop ( struct postfix *p )

int data ;
if ( p -> top == -1 )

printf ( "\nStack is empty." ) ;

return NULL ;

data = p -> stack[p -> top] ;

p -> top-- ;

return data ;

/* evaluates the postfix expression */

void calculate( struct postfix *p )

int n1, n2, n3 ;

while ( *( p -> s ) )

/* skip whitespace, if any */

if ( *( p -> s ) == ' ' || *( p -> s ) == '\t' )

p -> s++ ;
continue ;

/* if digit is encountered */

if ( isdigit ( *( p -> s ) ) )

p -> nn = *( p -> s ) - '0' ;

push ( p, p -> nn ) ;

else

/* if operator is encountered */

n1 = pop ( p ) ;

n2 = pop ( p ) ;

switch ( *( p -> s ) )

case '+' :

n3 = n2 + n1 ;

break ;

case '-' :

n3 = n2 - n1 ;
break ;

case '/' :

n3 = n2 / n1 ;

break ;

case '*' :

n3 = n2 * n1 ;

break ;

case '%' :

n3 = n2 % n1 ;

break ;

case '$' :

n3 = pow ( n2 , n1 ) ;

break ;

default :

printf ( "Unknown operator" ) ;

exit ( 1 ) ;
}

push ( p, n3 ) ;

p -> s++ ;

}}

/* displays the result */

void show ( struct postfix p )

p.nn = pop ( &p ) ;

printf ( "Result is: %d", p.nn ) ;

3.11 Introduction to Queues

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.

Suppose that at a service counter, t1 units of time are needed to provide service to a
single person and on average a new person arrives at the counter after every t2 units of
time. The following possibilities may arise:
1. If t1<t2, then the service counter will have some free time. Hence, queues will be
of limited lengths.
2. If t1>t2, then the service counter will always be busy. In such a case, a queue will
form very quickly and the queue length will explode.
3. If t1=t2, then, on an average, as a person leaves the service counter, a new person
arrives at the service counter. In this case, there exists the possibility of the queue
length exploding to infinity.

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

Daily Life Example

i) The number of cars at police check


ii) People purchase tickets from cinema make a queue.
iii) People deposits their bills in bank make a queue.

Computer Based Example

i) When number of instructions are given for printing to computer,


then that instructions will be executed first that was first assign to
computer.

Queues are also called " first-in first-out " (FIFO) list. Since the first element in a queue
will be the first element out of the queue. In other words, the order in which elements
enter in a queue is the order in which they leave. The real life example: the people
waiting in a line at Railway ticket Counter form a queue, where the first person in a line
is the first person to be waited on. An important example of a queue in computer science
occurs in timesharing system, in which programs with the same priority form a queue
while waiting to be executed.

In programming jargon, insert and delete operations are known as enqueue and dequeue
operations.
3.13 Operations on Queues

The following are typical operations performed on queues:

• createqueue(q) — to create queue as an empty queue.


• enqueue(q,i) — to insert element i in a q.
• dequeue(q) — to access and remove an element of queue q.
• peek(q) — to access the first element of the queue q without removing it.
• isfull(q) — to check whether the queue q is full.
• isempty(q) — to check whether the queue q is empty

3.14 Array Representation


We use the following declarations in order to represent the queue:

/*Define a queue of capacity 10*/


#define CAPACITY 10
typedef struct
{
int front;
int rear;
int elements[CAPACITY];
} queue;
queue q;

Using this declaration, we can perform the various queue operations.


3.15 Types of Queues

Depending on how the array of queue elements is used, queues can be either

Linear or

Circular

3.15 (a) Linear Queue

Implementation of operations on a linear queue:

Creating an Empty Queue

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 CAPACITY–1.

ALGORITHM FOR THE INSERTION OF ELEMENT IN THE


QUEUE

Algorithm enqueue (Q, N, item, Rear, Front)

[This algorithm is used to insert the element in the queue where

Q = Queue Array

N = Total number of elements

Item = which is to be inserted


Front, Rear = Deletion and Insertion pointers

Step-1 [Check for overflow]

If (Rear >= N) then

Write (“ Over Flow”)

Goto step-5

End if

Step-2 [Increment the Rear Pointer]

Rear = Rear + 1

Step-3 [Insert the element at Rear position]

Q [Rear] = item

Step-4 [Reset Pointer]

If (Front = 0 ) then

Set Front = 1

End if

Goto step-1

Step-5 [Finished]
Exit

ALGORITHM FOR THE DELETION OF ELEMENT FROM THE


QUEUE

Algorithm DelQueue (Q, item, Front, Rear)

[ This algorithm is used for the deletion of element from the queue where

Q = Array

Item = which is to be deleted

Step-1 [Check for under flow]

If ((Front = 0) or (Front > Rear)) then

Write (“ Under Flow”)

Goto step-4

End if

Step-2 [Delete the item from queue]

Item  Q [Front]

Step-3 [Set Front Pointer]

If (Front = Rear) then


Front  Rear 0

Else

Front  Front + 1

End if

Goto step-1

Step-4 [Finished]

Exit

3.16 Circular Queue

Circular queue provide flexible way for the insertion of element in a circular way.In
queue we are nor sure that when the rear pointer goes to an end i.e Rear = N , overflow
condition occur, may be the same element from the front are deleted, it is the major
drawback of queue.

This drawback can be overcome with the help of circular queue.

In circular queue the insertion can be take place by using this sequence

1,2,3, …… N

Check for Underflow

When the front and rear pointer become equal that is


Front  Rear  0

Underflow condition may arise.

Check for Overflow

When Front = 1 and Rear = N then overflow condition may arise. OR

When Front  Rear + 1 then overflow condition may arise.

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.

Empty Queue
Non-Empty Queue

Testing a circular queue for overflow

There are two conditions:

(front=0) and (rear=capacity-1)

front=rear+1

If any of these two conditions is satisfied, it means that circular queue is full.
ALGORITHM FOR THE INSERTION OF ELEMENT IN
CIRCULAR QUEUE

Algorithm Circularinsertion (CQ, N, item, Rear, Front)

[This algorithm is used to insert the element in CQ, where

CQ = Array

N = Final Limit

Item = which is to be inserted

Front, Rear = Deletion and Insertion pointers]

Step-1 [Check for overflow]

If ((Front = 1) && (Rear = N) or (Front = Rear + 1) then

Write (“Over Flow”)

Goto step-4

End if

Step-2 [Reset Rear Pointer]

If (Front = Rear =0) then

Rear = Front = 1

Else if (Rear = N) then

Rear = 1
Else

Rear = Rear + 1

End if

Step-3 [Insert the element]

CQ [Rear] = item

Goto step-1

Step-4 [Finished]

Exit

ALGORITHM FOR THE DELETION OF ELEMENT FROM


CIRCULAR QUEUE

Algorithm DeleteCQ (CQ, item, N, Front, Rear)

[This algorithm is used for the deletion of element from Circular queue where

CQ = Array

Item = which is to be deleted

N = Total number of element

Step-1 [Check for under flow]


If ((Front = Rear = 0)) then

Write (“Under Flow”)

Goto step-4

End if

Step-2 [Delete the item from Circular queue]

Item  CQ [Front]

Step-3 [Set Front Pointer]

If (Front = Rear) then

Front  Rear 0

Else if (Front = N) then

Front =1

Else

Front  Front + 1

End if

Goto step-1

Step-4 [Finished]

Exit
Dequeue

It is a linear data structure. This queue is called double ended queue. In this type of
queue insertion and deletion of elements can be take place from both ends.

In this queue, insertion and deletion of elements from Rear and Front can take place
on the principal of Right and Left pointer.

There are two types of dequeue.

1) input restricted queue


In this type of queue, insertion can take place only from one end and
deletion can take place from both ends.

2) Output restricted queue


In this type of queue, insertion can take place from both ends and deletion
can take place from one end.

Principal for Insertion

When the elements are inserted from Rear/ Right then Rear pointer is incremented
and when elements are inserted from Front/ Left then Front pointer decremented.

ALGORITHM FOR THE INSERTION OF ELEMENT TO RIGHT IN


DEQUEUE

Algorithm DQInsertRight (DQ, N, item, Rear, Front)

[This algorithm is used to insert the element to the right of DQ, where

DQ = Array

N = Final Limit
Item = which is to be inserted

Front, Rear = Deletion and Insertion pointers ]

Step-1 [Check for overflow]

If ((Front = 1) && (Rear = N) or (Front = Rear + 1) then

Write (“Over Flow”)

Goto step-4

End if

Step-2 [Reset Rear Pointer]

If (Rear =0) then

Rear = Front = 1

Else if (Rear = N) then

Rear = 1

Else

Rear = Rear + 1

End if

Step-3 [Insert the element]

DQ [Rear] = item

Goto step-1
Step-4 [Finished]

Exit

ALGORITHM FOR THE INSERTION OF ELEMENT TO LEFT IN


DEQUEUE

Algorithm DQInsertLeft (DQ, N, item, Rear, Front)

[This algorithm is used to insert the element to the left of DQ, where

DQ = Array

N = Final Limit

Item = which is to be inserted

Front, Rear = Deletion and Insertion pointers ]

Step-1 [Set the Front Pointer]

If ( Front = 1 or Front = 0 ) then

Front = N

Else

Front = Front - 1

End if

Step-2 [Check for overflow]


If (Front = Rear) then

Write (“Overflow”)

Goto step-5

End if

Step-3 [Insert the element]

DQ [Front] = item

Step-4 [Set the Rear Pointer]

If (Rear = 0) then

Rear = N

End if

Goto step-1

Step-5 [Finished]

Exit

/* Program that implements queue as a linked list. */

#include <stdio.h>

#include <conio.h>

struct node
{

int data ;

struct node *link ;

};

struct queue

struct node *front ;

struct node *rear ;

};

void initqueue ( struct queue * ) ;

void addq ( struct queue *, int ) ;

int delq ( struct queue * ) ;

void delqueue ( struct queue * ) ;

void main( )

struct queue a ;

int i ;

clrscr( ) ;

initqueue ( &a ) ;

addq ( &a, 11 ) ;

addq ( &a, -8 ) ;
addq ( &a, 23 ) ;

addq ( &a, 19 ) ;

addq ( &a, 15 ) ;

addq ( &a, 16 ) ;

addq ( &a, 28 ) ;

i = delq ( &a ) ;

printf ( "\nItem extracted: %d", i ) ;

i = delq ( &a ) ;

printf ( "\nItem extracted: %d", i ) ;

i = delq ( &a ) ;

printf ( "\nItem extracted: %d", i ) ;

delqueue ( &a ) ;

getch( ) ;

/* initialises data member */

void initqueue ( struct queue *q )

q -> front = q -> rear = NULL ;

}
/* adds an element to the queue */

void addq ( struct queue *q, int item )

struct node *temp ;

temp = ( struct node * ) malloc ( sizeof ( struct node ) ) ;

if ( temp == NULL )

printf ( "\nQueue is full." ) ;

temp -> data = item ;

temp -> link = NULL ;

if ( q -> front == NULL )

q -> rear = q -> front = temp ;

return ;

q -> rear -> link = temp ;

q -> rear = q -> rear -> link ;

/* removes an element from the queue */

int delq ( struct queue * q )

{
struct node *temp ;

int item ;

if ( q -> front == NULL )

printf ( "\nQueue is empty." ) ;

return NULL ;

item = q -> front -> data ;

temp = q -> front ;

q -> front = q -> front -> link ;

free ( temp ) ;

return item ;

/* deallocates memory */

void delqueue ( struct queue *q )

struct node *temp ;

if ( q -> front == NULL )

return ;

while ( q -> front != NULL )


{

temp = q -> front ;

q -> front = q -> front -> link ;

free ( temp ) ;

/*Program that implements circular queue as an array. */

#include <stdio.h>

#include <conio.h>

#define MAX 10

void addq ( int *, int, int *, int * ) ;

int delq ( int *, int *, int * ) ;

void display ( int * ) ;

void main( )

int arr[MAX] ;

int i, front, rear ;

clrscr( ) ;

/* initialise data member */


front = rear = -1 ;

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

arr[i] = 0 ;

addq ( arr, 14, &front, &rear ) ;

addq ( arr, 22, &front, &rear ) ;

addq ( arr, 13, &front, &rear ) ;

addq ( arr, -6, &front, &rear ) ;

addq ( arr, 25, &front, &rear ) ;

printf ( "\nElements in the circular queue: " ) ;

display ( arr ) ;

i = delq ( arr, &front, &rear ) ;

printf ( "Item deleted: %d", i ) ;

i = delq ( arr, &front, &rear ) ;

printf ( "\nItem deleted: %d", i ) ;

printf ( "\nElements in the circular queue after deletion: " ) ;

display ( arr ) ;

addq ( arr, 21, &front, &rear ) ;

addq ( arr, 17, &front, &rear ) ;


addq ( arr, 18, &front, &rear ) ;

addq ( arr, 9, &front, &rear ) ;

addq ( arr, 20, &front, &rear ) ;

printf ( "Elements in the circular queue after addition: " ) ;

display ( arr ) ;

addq ( arr, 32, &front, &rear ) ;

printf ( "Elements in the circular queue after addition: " ) ;

display ( arr ) ;

getch( ) ;

/* adds an element to the queue */

void addq ( int *arr, int item, int *pfront, int *prear )

if ( ( *prear == MAX - 1 && *pfront == 0 ) || ( *prear + 1 == *pfront ) )

printf ( "\nQueue is full." ) ;

return ;

if ( *prear == MAX - 1 )
*prear = 0 ;

else

( *prear )++ ;

arr[*prear] = item ;

if ( *pfront == -1 )

*pfront = 0 ;

/* removes an element from the queue */

int delq ( int *arr, int *pfront, int *prear )

int data ;

if ( *pfront == -1 )

printf ( "\nQueue is empty." ) ;

return NULL ;

data = arr[*pfront] ;

arr[*pfront] = 0 ;

if ( *pfront == *prear )

{
*pfront = -1 ;

*prear = -1 ;

else

if ( *pfront == MAX - 1 )

*pfront = 0 ;

else

( *pfront )++ ;

return data ;

/* displays element in a queue */

void display ( int * arr )

int i ;

printf ( "\n" ) ;

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

printf ( "%d\t", arr[i] ) ;

printf ( "\n" ) ;

}
3.17 Priority Queue

A priority queue is a collection of elements where the elements are stored according to
their priority levels. The order in which the elements should get added or removed is
decided by the priority of the element. Following rules are applied to maintain a priority
queue.

a) the element with a higher priority is processed before any element of lower priority.

b) if there are elements with the same priority, then the element added first in the queue
would get processed

priority queues are used for implementing job scheduling by the operating system where
jobs with higher priorities are to be processed first.

The elements of a priority queue need not be numbers or characters that can be compared
directly. They may be complex structures that are ordered on one or several fields. For
example, telephone-book listings consist of last names, first names, addresses, and phone
numbers and are ordered by last name.

There are two types of priority queues: an ascending priority queue and a descending
priority queue. An ascending priority queue is a collection of items into which items can
be inserted arbitrarily and from which only the smallest item can be removed. If apq is an
ascending priority queue, the operation pqinsert(apq,x) inserts element x into apq and
pqmindelete(apq) removes the minimum element from apq and returns its value.

A descending priority queue is similar but allows deletion of only the largest item. The
operations applicable to a descending priority queue, dpq, are pqinsert(dpq,x) and
pqmaxdelete(dpq). pqinsert(dpq,x) inserts an element x into dpq and is logically identical
to pqinsert for an ascending priority queue. pqmaxdelete(dpq) removes the maximum
element from dpq and returns its value.
/* Program that implements a priority queue using an array. */

#include <stdio.h>

#include <conio.h>

#define MAX 5

struct data

char job[MAX] ;

int prno ;

int ord ;

};

struct pque

struct data d[MAX] ;

int front ;

int rear ;

};

void initpque ( struct pque * ) ;

void add ( struct pque *, struct data ) ;

struct data delete ( struct pque * ) ;

void main( )
{

struct pque q ;

struct data dt, temp ;

int i, j = 0 ;

clrscr( ) ;

initpque ( &q ) ;

printf ( "Enter Job description (max 4 chars) and its priority\n" ) ;

printf ( "Lower the priority number, higher the priority\n" ) ;

printf ( "Job Priority\n" ) ;

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

scanf ( "%s %d", &dt.job, &dt.prno ) ;

dt.ord = j++ ;

add ( &q, dt ) ;

printf ( "\n" ) ;

printf ( "Process jobs prioritywise\n" ) ;

printf ( "Job\tPriority\n" ) ;
for ( i = 0 ; i < MAX ; i++ )

temp = delete ( &q ) ;

printf ( "%s\t%d\n", temp.job, temp.prno ) ;

printf ( "\n" ) ;

getch( ) ;

/* initialises data members */

void initpque ( struct pque *pq )

{ int i ;

pq -> front = pq -> rear = -1 ;

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

strcpy ( pq -> d[i].job, '\0' ) ;

pq -> d[i].prno = pq -> d[i].ord = 0 ;

}}

/* adds item to the priority queue */


void add ( struct pque *pq, struct data dt )

struct data temp ;

int i, j ;

if ( pq -> rear == MAX - 1 )

printf ( "\nQueue is full." ) ;

return ; }

pq -> rear++ ;

pq -> d[pq -> rear] = dt ;

if ( pq -> front == -1 )

pq -> front = 0 ;

for ( i = pq -> front ; i <= pq -> rear ; i++ )

for ( j = i + 1 ; j <= pq -> rear ; j++ )

if ( pq -> d[i].prno > pq -> d[j].prno )


{

temp = pq -> d[i] ;

pq -> d[i] = pq -> d[j] ;

pq -> d[j] = temp ;

else

if ( pq -> d[i].prno == pq -> d[j].prno )

if ( pq -> d[i].ord > pq -> d[j].ord )

temp = pq -> d[i] ;

pq -> d[i] = pq -> d[j] ;

pq -> d[j] = temp ;

}}}

}}}

/* removes item from priority queue */

struct data delete ( struct pque *pq )

struct data t ;
strcpy ( t.job, "" ) ;

t.prno = 0 ;

t.ord = 0 ;

if ( pq -> front == -1 )

printf ( "\nQueue is Empty.\n" ) ;

return t ;

t = pq -> d[pq -> front] ;

pq -> d[pq -> front] = t ;

if ( pq -> front == pq -> rear )

pq -> front = pq -> rear = -1 ;

else

pq -> front++ ;

return t ; }
MODULE IV : Linked list

4.1 Linked List

In computer science, a linked list is one of the fundamental data structures used in
computer programming. It consists of a sequence of nodes, each containing arbitrary data
fields and one or two references ("links") pointing to the next and/or previous nodes. A
linked list is a self-referential data type because it contains a link to another data of the
same type. Linked lists permit insertion and removal of nodes at any point in the list in
constant time, but do not allow random access

Advantages of Linked List:

A linked list is a dynamic data structure and therefore the size of the linked list can grow
or shrink in size during execution of the program. A linked list does not require any extra
space therefore it does not waste extra memory. It provides flexibility in rearranging the
items efficiently.

The limitation of linked list is that it consumes extra space when compared to a array
since each node must also contain the address of the next item in the list to search for a
single item in a linked list is cumbersome and time consuming.

4.2 Operations on linked list

There are several operations that we can think of performing on linked lists. The
following program shows how to build a linked list by adding new nodes at the
beginning, at the end or in the middle of the linked list. It also contains a function
display() which displays all the nodes present in the linked list and a function delete()
which can delete any node in the linked list.

Program to maintain a linked list

#include <stdio.h>

#include <conio.h>

#include <alloc.h>

/* structure containing a data part and link part */

struct node

int data ;

struct node * link ;

};

void append ( struct node **, int ) ;

void addatbeg ( struct node **, int ) ;

void addafter ( struct node *, int, int ) ;

void display ( struct node * ) ;

int count ( struct node * ) ;

void delete ( struct node **, int ) ;

void main( )

{
struct node *p ;

p = NULL ; /* empty linked list */

printf ( "\nNo. of elements in the Linked List = %d", count ( p ) ) ;

append ( &p, 14 ) ;

append ( &p, 30 ) ;

append ( &p, 25 ) ;

append ( &p, 42 ) ;

append ( &p, 17 ) ;

display ( p ) ;

addatbeg ( &p, 999 ) ;

addatbeg ( &p, 888 ) ;

addatbeg ( &p, 777 ) ;

display ( p ) ;

addafter ( p, 7, 0 ) ;

addafter ( p, 2, 1 ) ;

addafter ( p, 5, 99 ) ;

display ( p ) ;

printf ( "\nNo. of elements in the Linked List = %d", count ( p ) ) ;

delete ( &p, 99 ) ;

delete ( &p, 1 ) ;
delete ( &p, 10 ) ;

display ( p ) ;

printf ( "\nNo. of elements in the Linked List = %d", count ( p ) ) ;

/* adds a node at the end of a linked list */

void append ( struct node **q, int num )

struct node *temp, *r ;

if ( *q == NULL ) /* if the list is empty, create first node */

temp = malloc ( sizeof ( struct node ) ) ;

temp -> data = num ;

temp -> link = NULL ;

*q = temp ;

else

temp = *q ;

/* go to last node */

while ( temp -> link != NULL )

temp = temp -> link ;


/* add node at the end */

r = malloc ( sizeof ( struct node ) ) ;

r -> data = num ;

r -> link = NULL ;

temp -> link = r ;

/* adds a new node at the beginning of the linked list */

void addatbeg ( struct node **q, int num )

struct node *temp ;

/* add new node */

temp = malloc ( sizeof ( struct node ) ) ;

temp -> data = num ;

temp -> link = *q ;

*q = temp ;

/* adds a new node after the specified number of nodes */

void addafter ( struct node *q, int loc, int num )

struct node *temp, *r ;


int i ;

temp = q ;

/* skip to desired portion */

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

temp = temp -> link ;

/* if end of linked list is encountered */

if ( temp == NULL )

printf ( "\nThere are less than %d elements in list", loc ) ;

return ;

}}

/* insert new node */

r = malloc ( sizeof ( struct node ) ) ;

r -> data = num ;

r -> link = temp -> link ;

temp -> link = r ;

/* displays the contents of the linked list */

void display ( struct node *q )


{

printf ( "\n" ) ;

/* traverse the entire linked list */

while ( q != NULL )

printf ( "%d ", q -> data ) ;

q = q -> link ;

/* counts the number of nodes present in the linked list */

int count ( struct node * q )

int c = 0 ;

/* traverse the entire linked list */

while ( q != NULL )

q = q -> link ;

c++ ;

return c ;

}
/* deletes the specified node from the linked list */

void delete ( struct node **q, int num )

struct node *old, *temp ;

temp = *q ;

while ( temp != NULL )

if ( temp -> data == num )

/* if node to be deleted is the first node in the linked list */

if ( temp == *q )

*q = temp -> link ;

/* deletes the intermediate nodes in the linked list */

else

old -> link = temp -> link ;

/* free the memory occupied by the node */

free ( temp ) ;

return ;

/* traverse the linked list till the last node is reached */

else
{

old = temp ; /* old points to the previous node */

temp = temp -> link ; /* go to the next node */

printf ( "\nElement %d not found", num ) ;

4.3 Types of Link List

1) Linearly-linked List

2) Singly-linked list

3) Doubly-linked list

4) Circularly-linked list

4.3.1 Singly Linked List

In a singly (one-way) linear linked list, each node is divided into two parts. The first part
contains the information of the element. The second part called the linked field or next
pointer field contains the address of the next node in the list.

A head 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
4.3.1 (a) 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 *head;

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

To insert an element in the list, the first task is to create a new node, assign the element to
be inserted to the info field of the node, and then place the new node at the appropriate
position by adjusting the appropriate pointers. Insertion in the list can take place at the
following positions:

• At the beginning of the list


• At the end of the list
• After a given element
a) Insertion at the Beginning of the List

First, test whether the linked list is initially empty, if yes, then the element is inserted as
the first and only one element by performing the following steps:

• Assign NULL to the next pointer field of the new node.


• Assign the address of the new node to head.

If the list is not empty, then the element is inserted as the first element of the list by
performing the following steps:

• Assign the value of head to the next pointer field of the new node.
• Assign the address of the new node to head.

Programmatically, both cases are equivalent. This is because in both the cases the first
step is to assign the value of head (NULL or otherwise) to the next pointer field of the
new node.

void insertatbeginning(node **head, int item)

node *newNode;

/* allocate memory for the new node and initialize the data in it*/

newNode= malloc(sizeof(node));

newNode->info=item;

/* assign the value of head to the “next” of newNode*/

newNode->next=*head;

/* assign the address of newNode to head */

*head=newNode;

}
b) Inserting at the End of the List
First test whether the linked list is initially empty, if yes, then the element is inserted as
the first and only one element by performing the following steps:

-Assign NULL to the next pointer field of the new node.

-Assign the address of the new node to head.

If the list is not empty, we traverse it to reach the last element, and then the new node is
inserted as the last element of the list by performing the following steps:

-Assign NULL to the next pointer field of the new node.

-Assign the address of the new node to the next pointer field of the last node.

void insertatend(node **head, int item)

node *newNode;

newNode=malloc(sizeof(node));

newNode->info=item;

newNode->next=NULL;

if(*head==NULL)

*head=newNode;

else

node * prev=*head;

while (prev->next!=NULL)

prev=prev->next;

prev->next=newNode;

}
}

c) Inserting after Given Element

To insert a new element after the given element, first we find the location, say loc, of the
given element in the list, and then the element is inserted in the list by performing the
following steps:

• Assign the next pointer field of the node pointed to by loc to the next pointer field
of the new node.
• Assign the address of the new node to the next pointer field of the node pointed to
by loc.

void insertafterelement(node *head, int item,int after)


{
node *newNode, *loc;
loc=searchunsortedlist(head,after);
if(loc==NULL) /*element after not found*/
return;
newNode=malloc(sizeof(node));
newNode->info=item;
newNode->next=loc->next;
loc->next=newNode;
}

4.3.2 Circular Linked List

The linked lists that we have seen so far are often known as linear linked list. All
elements of such a linked list can be accessed by first setting up a pointer pointing to the
first node in the list and then traversing the entire list using this pointer.

A circular linked list does not have a first or last node.


5.4.2 (a) Circular Linked List Implementation

A linked list in which the node at the tail of the list, instead of having a null pointer,
points back to the node at the head of the list. Thus both ends of a list can be accessed
using a single pointer.

In a circularly linked list, all nodes are linked in a continuous circle, without using null.
For lists with a front and a back (such as a queue), one stores a reference to the last node
in the list. The next node after the last node is the first node. Elements can be added to the
back of the list and removed from the front in constant time.

Circularly-linked lists can be either singly or doubly linked.


Both types of circularly-linked lists benefit from the ability to traverse the full list
beginning at any given node. This often allows us to avoid storing firstNode and
lastNode, although if the list may be empty we need a special representation for the
empty list, such as a lastNode variable which points to some node in the list or is null if
it's empty; we use such a lastNode here.
This representation significantly simplifies adding and removing nodes with a non-empty
list, but empty lists are then a special case.

5.4.2 (b) Circular Linked List in C


#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<alloc.h>
#define null 0
struct node
{
int info;
struct node *link;
}*start;
void main()
{
int ch,n,m,position,i;
last=null;
while(1)
{
printf("1.create
2.addat
3.addbt
4.del
5.disp
6.exit
");
printf("er ur ch");
scanf("%d",&ch);
switch(ch)
{
case 1:
printf("er no of itc");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("er the element");
scanf("%d",&m);
create(m);
}break;
case 2:
printf("er the element");
scanf("%d",&m);
addat(m);
break;
case 3:
printf("er the element");
scanf("%d",&m);

printf("er the position");


scanf("%d",&position);
addbt(m,position);
break;
case 4:
if(last==null)
{
printf("list is empty");
continue;
}
printf("er the element for delete");
scanf("%d",&m);
del(m);
break;
case 5:
disp();
break;
case 6:
exit(0);
break;
default:
printf("wrong choice");
}}}
create(int data)
{
struct node *q,*tmp;
tmp=(struct node *)malloc(sizeof(struct node));
tmp->info=data;
tmp->link=null;
if(last==null)
{
last=tmp;
tmp->link=last;
}
else
{
tmp->link=last->link;
last->link=tmp;
last=tmp;
}}
addat(int data)
{
struct node *q,*tmp;
tmp=(struct node *)malloc(sizeof(struct node));
tmp->info=data;
tmp->link=last->link;
last->link=tmp;
}
addbt(int data,int pos)
{
struct node *tmp,*q;
int i;
q=last->link;;
for(i=0;i<pos-1;i++)
{
q=q->link;
if(q==last->link)
{
printf("there r lessthan %d elements",pos);
return;
}
}
tmp=(struct node *)malloc(sizeof(struct node));
tmp->link=q->link;
tmp->info=data;
q->link=tmp;
if(q==last)
last=tmp;
}
del(int data)
{
struct node *tmp,*q;
if(last->link==last&&last->info==data)
{
tmp=last;
last=null;
free(tmp);
return;
}
q=last->link;
if(q->info==data)
{
tmp=q;
last->link=q->link;
free(tmp);
return;
}
while(q->link!=last)
{
if(q->link->info==data)
{
tmp=q->link;
q->link=tmp->link;
free(tmp);

printf("element %d is deleted",data);
}
if(q->link->info=data)
{
tmp=q->link;
q->link=last->link;
free(tmp);
last=q;
return;}
printf("element%d is not found",data);
}
disp()
{
struct node *q;
if(last==null)
{
printf("list isdempty");
return;
}q=last->link;
while(q!=last)
{
printf("%d",q->info);
q=q->link;
}
printf("%d",last->info);
}

4.3.3 Doubly Linked List

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

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.
4.3.3 (a)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.

(b)Inserting an Element in doubly Linked List

To insert an element in the list, the first task is to allocate memory for a new node, assign
the element to be inserted to the info field of the node, and then the new node is placed at
the appropriate position by adjusting appropriate pointers. Insertion in the list can take
place at the following positions:

• At the beginning of the list


• At the end of the list
• After a given element
• Before a given element

a) Insertion at the Beginning of the List

First, test whether the linked list is empty, if yes, then the element is inserted as the first
and only one element by performing the following steps:

• Assign NULL to the next pointer and prev pointer fields of the new node
• Assign the address of the new node to head and tail pointer variables.

If the list is not empty, then the element is inserted as the first element of the list by
performing the following steps:

• Assign NULL to the prev pointer field of the new node.


• Assign the value of the head variable (the address of the first element of the
existing list) to the next pointer field of the new node.
• Assign the address of the new node to prev pointer field of the node currently
pointed by head variable, i.e. first element of the existing list.
• Finally assign the address of the new node to the head variable

b)Inserting at the End of the List

First test whether the linked list is initially empty, if yes, then the element is inserted as
the first and only one element by performing the following steps:

• Assign NULL value to the next pointer and prev pointer field of the new node
• Assign address of new node to head and tail pointer variable.
If the list is not empty, then element is inserted as the last element of the list by
performing the following steps:

• Assign NULL value to the next pointer field of the new node.
• Assign value of the tail variable (the address of the last element of the existing
list) to the prev pointer field of the new node.
• Assign address of the new node to the next pointer field of the node currently
pointed by tail variable i.e. last element of the existing list.
• Finally assign the address of the new node to tail variable.

C CODE

void insertatend (node **head, node **tail, int item)


{
node *ptr;
ptr = malloc(sizeof(node));
ptr->info = item;
if (*head == NULL)
{
ptr->next = ptr->prev=NULL;
*head = *tail = ptr;
}
else
{
ptr->next=NULL;
ptr->prev=*tail;
(*tail)->next=ptr;
*tail=ptr;
}
}
Inserting before a Given Element
void insertbeforeelement (node **head, int item, int before)
{
node *ptr, *loc;
ptr=*head;
loc=search(ptr,before);
if(loc==NULL)
return;
ptr=malloc(sizeof(node));
ptr->info=item;
if(loc->prev==NULL)
{
ptr->prev=NULL;
loc->prev=ptr;
ptr->next=*head;
*head=ptr;
}
else
{
ptr->prev=loc->prev;
ptr->next=loc;
(loc->prev)->next=ptr;
loc->prev=ptr;
}
}

Inserting after a Given Element


void insertafterelement (node **head, node **tail, int item, int after)
{
node *ptr, *loc;
ptr = *head;
loc = search(ptr,after);
if(loc == NULL)
return;
ptr=malloc(sizeof(node));
ptr->info = item;
if(loc->next == NULL)
{
ptr->next = NULL;
loc->next = ptr;
ptr->prev = *tail;
*tail = ptr;
}
else
{
ptr->prev = loc;
ptr->next = loc->next;
(loc->next)->prev = ptr;
loc->next = ptr;
}
}

Deleting an element from a doubly Linked list.

To delete an element from the list, first the pointers are set properly and then the memory
occupied by the node to be deleted is deallocated (freed).
Deletion in the list can take place at the following positions.
a) At the beginning of the list
b) At the end of the list
c) After a given element
d) Before a given element

a) Deleting from the Beginning of the List


An element from the beginning of the list can be deleted by performing the following
steps
Assign the value of head (address of the first element of the list) to a temporary variable
(say temp)
There are two further cases:
-If there is only one element in the existing list, both head and tail are set to NULL.

-If there is more than one element in the list then


-Assign NULL to the prev pointer field of the second node.
-Assign the address of the second node to head.
-Deallocate the memory occupied by the node pointed to by temp.

C Code
void deletefrombeginning( node **head, node **tail)

node *temp;

if(*head==NULL)

return;

temp=*head;

if(*head==*tail) /*one element only*/

*head=*tail=NULL;

else

(temp->next)->prev=NULL;

*head=temp->next;

free(temp);

}
b) Deleting from the End of the List

An element from the end of the list can be deleted by performing the following steps:

Assign the value of tail (address of the last element of the list) to a temporary variable
(say temp)
Further there are two cases:
- If there is only one element in the existing list, set both head and tail to NULL.
-If there is more than one element in the list then
-Assign NULL to the next pointer field of the second last node.
-Assign the address of the second last node to tail.
-Deallocate the memory occupied by the node pointed to by temp.

C Code

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


{
node *temp;
if(*head==NULL)
return;
temp=*tail;
if(*head==*tail) /*one element only*/
*head=*tail=NULL;
else
{
temp->prev->next=NULL;
*tail=temp->prev;
}
free(temp);
}
c) Deleting after a Given Element

void deleteafterelement (node **head, node **tail, int after)

node *temp, *loc;

temp = *head;

loc = search(temp,after);

if (loc == NULL) /*search item not found*/

return;

temp = loc->next;

loc->next = temp->next;

if(temp->next == NULL)

*tail = loc;

else

(temp->next)->prev = loc;

free(temp);

d) Deleting before a Given Element

void deletebeforeelement (node **head, int before)

node *temp, *loc;


temp=*head;

loc=search(temp,before);

if(loc==NULL)

return;

temp=loc->prev;

loc->prev=temp->prev;

if(temp->prev==NULL)

*head=loc;

else

(temp->prev)->next=loc;

free(temp);

4.4 Freeing up the Entire List

The doubly linked list can be deleted either from the beginning or

from the end. To delete from the beginning, use the following steps:

-Assign the head pointer to a temporary variable, say temp.

-Advance the head pointer to the next node.


-Deallocate the memory occupied by the node pointed to by temp.

-Repeat the above steps until the entire list is deleted.

-Finally, set the tail pointer to NULL.

C Code
void deletelist(node **head, node **tail)

node *temp;

while(*head!=NULL)

temp=*head;

*head=(*head)->next;

free(temp);

*tail=NULL;

}
MODULE V : Trees

5.1 Tree

Data frequently contain a hierarchical relationship between various elements. This non-
linear Data structure which reflects this relationship is called a rooted tree graph or, tree.

This structure is mainly used to represent data containing a hierarchical relationship


between elements, e.g. record, family tree and table of contents.

A tree consist of a distinguished node r , called the root and zero or more (sub) tree t1 , t2
, ... tn , each of whose roots are connected by a directed edge to r

In the tree of figure, the root is A, Node t 2 has r as a parent and t 2.1 , t 2.2 and t 2.3 as
children. Each node may have arbitrary number of children, possibly zero. Nodes with no
children are known as leaves.
5.2 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.

It’s 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.

5.3 Binary Tree

A binary tree is a finite set of elements that is either empty or is partitioned into three
disjoint sub-sets. The first sub-set contains a single element called the root of the tree.
The other two sub-sets are themselves binary trees, called the left and right sub trees of
the original tree.

A left or right sub tree can be empty. Each element of a binary tree is called a node of the
tree.

Points:

a) Every node in a binary tree can have at most two children.

b) The two children of each node are called the left child and right child corresponding to
their positions.

c) A node can have only a left child or only a right child or it can have no children at all.
Properties of Proper Binary Trees

Notation

n number of nodes

e number of external nodes

i number of internal nodes

h height

Properties

e = i +1

n =2e -1

h <= i

h <= (n -1)/2

e <= 2h
5.3 (a) Binary Tree Terminology

1) Root: node without parent (A)

2) Internal node: node with at least one child (A, B, C, F)

3) External node: (a.k.a. leaf) node without children (E, I, J, K, G, H, D)

4)Ancestors of a node: parent, grandparent, grand-grandparent, etc

5) Depth of a node: number of ancestors

6) Height of a tree: maximum depth of any node (3)

7) Descendant of a node: child, grandchild, grand- grandchild etc.

8) Degree of an element: no. of children it has

9) Subtree: tree consisting of a node and its descendants

10) Path: traversal from node to node along the edges that results in a sequence

11) Root: node at the top of the tree

12) Parent: any node, except root has exactly one edge running upward to another node.
The node above it is called parent.
13) Child: any node may have one or more lines running downward to other nodes.
Nodes below are children.

14) Leaf: a node that has no children

15) Subtree: any node can be considered to be the root of a subtree, which consists of its
children and its children’s children and so on.

16) Visiting: a node is visited when program control arrives at the node, usually for
processing.

17) Traversing: to traverse a tree means to visit all the nodes in some specified order.

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

19) Keys: key value is used to search for the item or perform other operations on it.

The major advantage of binary search trees over other data structures is that the related
sorting algorithms and search algorithms such as in-order traversal can be very efficient.

Binary search trees are a fundamental data structure used to construct more abstract data
structures such as sets, multisets and associative arrays.

5.3 (b) Operations on Binary Tree

Operations on a binary tree require comparisons between nodes. These comparisons are
made with calls to a comparator, which is a subroutine that computes the total order
(linear order) on any two values. This comparator can be explicitly or implicitly defined,
depending on the language in which the BST is implemented.
There are many operations that can be performed on binary search trees. Searching,
insertion, and deletion of a node are the most basic operations that are required to
maintain a tree.

5.3(c )Searching a node

Searching a binary tree for a specific value can be a recursive or iterative process. This
explanation covers a recursive method.

To search any node in a binary tree, intially the data that is to be searched is compared
with the data of the root node.

If the tree is null, the value we are searching for does not exist in the tree.If the data is
equal to the data of the root node then the searching is successful.

If the data is greater than the root, search the right subtree. This process is repeated until
the value is found or the indicated subtree is null. If the searched value is not found
before a null subtree is reached, then the item must not be present in the tree.
Suppose we try to find a node numbered 7 in it. First go down the right sub-tree (since
value 7 is greater than the root value), and then go down the left sub-tree.

BST searchelement(BST *tree, int value)

if((tree->info==value) ||tree==NULL)

return tree;

else if(value<tree->info)

return searchelement(tree->left, value);

else

return searchelement(tree->right, value);

Finding the Smallest Node

Because of the property of binary search tree, we know that the smallest node in the tree
will be one of the nodes in the left sub tree (if the left sub tree exists) otherwise root node
itself will be the smallest node.

BST smallest(BST *tree)

{ if((tree==NULL)||(tree->left==NULL))

return tree;

else

return smallest(tree->left);

}
Finding the Largest Node

Because of the property of binary search tree, we know that the largest node in the tree
will be one of the nodes in the right sub tree (if the sub tree exists) otherwise root node
itself will be the largest node.

BST largest(BST *tree)

if((tree==NULL)||(tree->right==NULL))

return tree;

else

return largest(tree->right);
}

5.4 Binary search- inserting a node

If the binary search tree is initially empty, then the element is inserted as a root node.
Otherwise the element is inserted as terminal node. Intially the data that is to be inserted
is compared with the data of the root node. If the data is found to be greater than or equal
to the data of the root node then the new node is inserted in the right sub-tree of the root
node, otherwise, the new node is inserted in the left sub-tree of the root node.

Now the root node of the right or left sub tree is taken and its data is compared with the
data that is to be inserted and the same procedure is repeated. This is done till the left or
the right sub-tree where the new node is to be inserted is found empty Otherwise the
element is inserted as terminal node. If the element is less than the element in the root
node, then the element is inserted in the left sub tree else to the right sub tree
void insertelement(BST *tree, int element)
{
BST *ptr,*nodeptr,*parentptr;
ptr = (BST*)malloc(sizeof(BST));
ptr->info = element;
ptr->left = ptr->right = NULL;

if(tree == NULL)
*tree = ptr;
else
{
parentptr = NULL;
nodeptr = *tree;
while(nodeptr != NULL)
{
parentptr = nodeptr;
if(element < nodeptr->info)
nodeptr = nodeptr->left;
else
nodeptr = nodeptr->right;
}
if(element < parentptr->info)
parentptr->left = ptr;
else
parentptr->right = ptr;
}}

5.5 Binary search- deleting a node

In addition to techniques for inserting data in a binary tree and traversing the
tree, practical examples call for deleting data from the binary tree. Assuming
that we will pass the specified data item that we wish to delete to a delete ()
function; there are four possible cases that we need to consider:

a) No node in the tree contains the specified data.


b) The node containing the data has no children.
c) The node containing the data has exactly one child
d) The node containing the data has two children.

Case (a):
Here, we merely need to print the message that the data item is not present in
the tree..
Case (b):
In this case since the node to be deleted has no children the memory
occupied by this should be freed and either the left link or the right link of
the parent of this node should be set to NULL. Which of these set to NULL
depends upon whether the node being deleted is a left child or a right child
of its parent.

Case (c):
In this case since the node to be deleted has one child the solution is again
rather simple. We have to adjust the pointer of the parent of the node to be
deleted such that after deletion it points to the child of the node being
deleted.

Case (d):
In this case since the node to be deleted has two children the solution is more
complex. The whole logic of deleting a node with two children is to locate
the in-order successor, copy its data and reduce the problem to a simple
deletion of a node with none or zero child.

/* Program to insert and delete a node from the binary search tree. */
#include <stdio.h>
#include <conio.h>
#include <alloc.h>
#define TRUE 1
#define FALSE 0
struct btreenode
{
struct btreenode *leftchild ;
int data ;
struct btreenode *rightchild ;
};
void insert ( struct btreenode **, int ) ;
void delete ( struct btreenode **, int ) ;
void search ( struct btreenode **, int, struct btreenode **,
struct btreenode **, int * ) ;
void inorder ( struct btreenode * ) ;
void main( )
{
struct btreenode *bt ;
int req, i = 0, num, a[ ] = { 11, 9, 13, 8, 10, 12, 14, 15, 7 } ;
bt = NULL ; /* empty tree */
clrscr( ) ;

while ( i <= 8 )
{
insert ( &bt, a[i] ) ;
i++ ;
}
clrscr( ) ;
printf ( "Binary tree before deletion:\n" ) ;
inorder ( bt ) ;

delete ( &bt, 10 ) ;
printf ( "\nBinary tree after deletion:\n" ) ;
inorder ( bt ) ;

delete ( &bt, 14 ) ;
printf ( "\nBinary tree after deletion:\n" ) ;
inorder ( bt ) ;

delete ( &bt, 8 ) ;
printf ( "\nBinary tree after deletion:\n" ) ;
inorder ( bt ) ;

delete ( &bt, 13 ) ;
printf ( "\nBinary tree after deletion:\n" ) ;
inorder ( bt ) ;
}
/* inserts a new node in a binary search tree */
void insert ( struct btreenode **sr, int num )
{
if ( *sr == NULL )
{
*sr = malloc ( sizeof ( struct btreenode ) ) ;

( *sr ) -> leftchild = NULL ;


( *sr ) -> data = num ;
( *sr ) -> rightchild = NULL ;
}
else /* search the node to which new node will be attached */
{
/* if new data is less, traverse to left */
if ( num < ( *sr ) -> data )
insert ( &( ( *sr ) -> leftchild ), num ) ;
else
/* else traverse to right */
insert ( &( ( *sr ) -> rightchild ), num ) ;
}
}
/* deletes a node from the binary search tree */
void delete ( struct btreenode **root, int num )
{
int found ;
struct btreenode *parent, *x, *xsucc ;

/* if tree is empty */
if ( *root == NULL )
{
printf ( "\nTree is empty" ) ;
return ;
}
parent = x = NULL ;

/* call to search function to find the node to be deleted */


search ( root, num, &parent, &x, &found ) ;

/* if the node to deleted is not found */


if ( found == FALSE )
{
printf ( "\nData to be deleted, not found" ) ;
return ;
}

/* if the node to be deleted has two children */


if ( x -> leftchild != NULL && x -> rightchild != NULL )
{
parent = x ;
xsucc = x -> rightchild ;
while ( xsucc -> leftchild != NULL )
{
parent = xsucc ;
xsucc = xsucc -> leftchild ;
}

x -> data = xsucc -> data ;


x = xsucc ;
}
/* if the node to be deleted has no child */
if ( x -> leftchild == NULL && x -> rightchild == NULL )
{
if ( parent -> rightchild == x )
parent -> rightchild = NULL ;
else
parent -> leftchild = NULL ;
free ( x ) ;
return ;
}

/* if the node to be deleted has only rightchild */


if ( x -> leftchild == NULL && x -> rightchild != NULL )
{
if ( parent -> leftchild == x )
parent -> leftchild = x -> rightchild ;
else
parent -> rightchild = x -> rightchild ;

free ( x ) ;
return ;
}
/* if the node to be deleted has only left child */
if ( x -> leftchild != NULL && x -> rightchild == NULL )
{
if ( parent -> leftchild == x )
parent -> leftchild = x -> leftchild ;
else
parent -> rightchild = x -> leftchild ;

free ( x ) ;
return ;
}
}
/*returns the address of the node to be deleted, address of its parent and whether the
node is found or not */

void search ( struct btreenode **root, int num, struct btreenode **par, struct btreenode
**x, int *found )
{
struct btreenode *q ;
q = *root ;
*found = FALSE ;
*par = NULL ;
while ( q != NULL )
{
/* if the node to be deleted is found */
if ( q -> data == num )
{
*found = TRUE ;
*x = q ;
return ;
}
*par = q ;
if ( q -> data > num )
q = q -> leftchild ;
else
q = q -> rightchild ;
}
}
/* traverse a binary search tree in a LDR (Left-Data-Right) fashion */
void inorder ( struct btreenode *sr )
{
if ( sr != NULL )
{
inorder ( sr -> leftchild ) ;
/* print the data of the node whose leftchild is NULL or the path has already been
traversed */
printf ( "%d\t", sr -> data ) ;
inorder ( sr -> rightchild ) ;
}}

5.6 Traversal of a Binary Tree

The traversal of a binary tree is to visit each node in the tree exactly one. Binary tree
traversal is useful in many applications. The order in which nodes of a linear list are
visited is clearly from first to last. However, there is no such natural linear order for the
nodes of a tree.

The methods differ primarily in the order in which they visit the nodes. There are three
popular methods of binary tree traversal. These methods are known as in-order traversal,
pre-order traversal and post-order traversal.
To traverse a non empty binary tree in Pre-order, we perform the following three
operations:-

1) Visit the root


2) Traverse the left sub-tree in pre-order.
3) Traverse the right sub-tree in pre-order.

To traverse a non-empty binary tree in in-order (or symmetric order):


1) Traverse the left sub-tree in in-order.
2) Visit the root.
3) Traverse the right sub-tree in in-order.

To traverse a non-empty binary tree in post-order:


1) Traverse the left sub-tree in post-order.
2) Traverse the right sub-tree in post-order.
3) Visit the root.

As you know every tree-node has a leftchild-link:: Value-field :: rightchild-link structure,


in INORDER traversal, for every node in the tree we first visit its left sub-tree (if its
there); then visit the node itself; and then the right sub-tree (if its there).

Follow the example –


We start from the root i.e A. We are supposed to visit its left sub-tree then visit the node
itself and its right sub-tree. Here, A has a left sub-tree rooted at B. So, I move to B and
check for its left sub-tree (as I'm supposed to do it for every node).

Again, B has a left sub-tree rooted at D. So, I check for D's left sub-tree now, but D
doesn't have any left sub-tree and thus I'll visit node D first and check for its right sub-
tree. As D doesn't have any right sub-tree, we'll track back and visit node B; and check
for its right sub-tree. B has a right sub-tree rooted at E and so we move to E.
Well, E doesn't have any left or right sub-trees so we just visit E and track back to B. We
already have visited B, so we track back to A. We are yet to visit the node itself and so
we visit A first; then we go for checking the right sub-tree of A, which is rooted at C. As
C doesn't have any left or right tree we visit C. So, the INORDER becomes - D B E A C

Similarly, in PREORDER, we visit the current node first, then we visit its left sub-tree
and then its right sub-tree. In POSTORDER, for every node, we visit the left sub-tree first
and then the right sub-tree and finally the node itself.

Algorithm:-

Step-1: For the current node check whether it has a left child. If it has then go to step-2
or else step-3
Step-2: Repeat step-1 for this left child

Step-3: Visit (i.e printing in our case) the current node

Step-4: For the current node check whether it has a right child. If it has then go to step-5

Step-5: Repeat step-1 for this right child


This is the recursive algorithm for INORDER traversal.

C implementation:-

struct NODE
{
struct NODE *left;
int value;
struct NODE *right;
}
inorder(struct NODE *curr)
{
if(curr->left != NULL) inorder(curr->left); /*step-1 & step-2*/
printf("%d", curr->value); /*step-3*/
if(curr->right != NULL) inorder(curr->right); /*step-4*/

5.7 Threaded Binary Tree

Both the recursive and non-recursive procedures for binary tree traversal require that
pointers to all of the free nodes be kept temporarily on a stack. It is possible to write
binary tree traversal procedure that does not require any pointers to the nodes be put on
the stack. Such procedures eliminate the overhead (time and memory) involved in
initializing, pushing and popping the stack.

A Threaded Binary Tree is a binary tree in which every node that does not have a right
child has a THREAD (in actual sense, a link) to its INORDER successor. By doing this
threading we avoid the recursive method of traversing a Tree, which makes use of stacks
and consumes a lot of memory and time.

The node structure for a threaded binary tree varies a bit and its like this

struct NODE
{
struct NODE *leftchild;
int node_value;
struct NODE *rightchild;
struct NODE *thread;
}

Let's make the Threaded Binary tree out of a normal binary tree...

The INORDER traversal for the above tree is -- D B A E C. So, the respective Threaded
Binary tree will be --
B has no right child and its inorder successor is A and so a thread has been made in
between them. Similarly, for D and E. C has no right child but it has no inorder successor
even, so it has a hanging thread.

The only problem with threads is that the coding requires that we know whether a pointer
is a normal pointer to a child or a thread that points back to an in-order successor or in-
order predecessor node. One solution to this problem is to add to the data in each tree
node two fields that indicate whether the left and right pointers in that node are normal
pointers or threads.

5.8 AVL Tree

In computer science, an AVL tree is a self-balancing binary search tree, and it is the first
such data structure to be invented.

In an AVL tree, the heights of the two child subtrees of any node differ by at most one;
therefore, it is also said to be height-balanced.

The AVL tree is named after its two inventors, G.M. Adelson-Velskii and E.M. Landis,
who published it in their 1962 paper "An algorithm for the organization of information."

The balance factor of a node is the height of its right subtree minus the height of its left
subtree, and a node with balance factor 1, 0, or -1 is considered balanced. A node with
any other balance factor is considered unbalanced and requires rebalancing the tree. The
balance factor is either stored directly at each node or computed from the heights of the
subtrees.
A

B
C

D E F G

(a)

B C

(b)

To represent a node of an AVL tree four fields are required- one for data, two for storing
the address of the left and right child and an additional field is required to hold the
balance factor. The balance factor of any node is calculated by subtracting the height of
the right sub-tree of the node from the height of the left sub-tree.

The structure of a node of an AVL tree is given below:

Struct AVL
{
Struct AVL * Left;
Int data;
Struct AVL * right;
Int balfact;
};

The value of balfact of any node is -1, 0 or 1. If it is other than these three values then the
tree is not balanced or it is not an AVL. tree.

- if the value of balfact of any node is -1, then the height of the right sub-tree of that node
is one more than the height of its left sub-tree.

- if the value of balfact of any node is 0 then the height of its left and right sub-tree is
exactly same.

-if the value of balfact of any node is 1 then the height of the left sub-tree of that node is
one or more than the height of its right sub-tree.

5.8.1 Insertion of a node in an AVL Tree

We can insert a new node in an AVL tree by finding its appropriate position. But
insertion of a new node involves certain overheads since the tree may become unbalanced
due to the increase in its height.

After inserting a node, it is necessary to check each of the node's ancestors for
consistency with the rules of AVL. For each node checked, if the balance factor remains -
1, 0, or 1 then no rotations are necessary. However, if the balance factor becomes 2 or -2
then the subtree rooted at this node is unbalanced. If insertions are performed serially,
after each insertion, at most two tree rotations are need to restore the entire tree to the
rules of AVL.
There are four cases which need to be considered, of which two are symmetric to the
other two. Let P be the root of the unbalanced subtree. Let R be the right child of P. Let L
be the left child of P.

Right-Right case and Right-Left case: If the balance factor of P is +2, then the right
subtree outweighs the left subtree of the given node, and the balance factor of the right
child (R) must be checked. If the balance factor of R is +1, a left rotation is needed with
P as the root. If the balance factor of R is -1, a double left rotation is needed. The first
rotation is a right rotation with R as the root. The second is a left rotation with P as the
root.

Left-Left case and Left-Right case: If the balance factor of P is -2, then the left subtree
outweighs the right subtree of the given node, and the balance factor of the left child (L)
must then checked. If the balance factor of L is -1, a right rotation is needed with P as
the root. If the balance factor of L is +1, a double right rotation is needed. The first
rotation is a left rotation with L as the root. The second is a right rotation with P as the
root.

5.8.2 Deletion of a Node in AVL Tree

If the node is a leaf, remove it. If the node is not a leaf, replace it with either the largest in
its left subtree (inorder predecessor) or the smallest in its right subtree (inorder
successor), and remove that node. The node that was found as replacement has at most
one subtree. After deletion, retrace the path back up the tree (parent of the replacement)
to the root, adjusting the balance factors as needed.

In addition to the balancing described above for insertions, if the balance factor for the
tree is 2 and that of the right subtree is 0, a left rotation must be performed on P. The
mirror of this case is also necessary.

The retracing can stop if the balance factor becomes -1 or 1 indicating that the height of
that subtree has remained unchanged. If the balance factor becomes 0 then the height of
the subtree has decreased by one and the retracing needs to continue. If the balance factor
becomes -2 or 2 then the subtree is unbalanced and needs to be rotated to fix it. If the
rotation leaves the subtree's balance factor at 0 then the retracing towards the root must
continue since the height of this subtree has decreased by one. This is in contrast to an
insertion where a rotation resulting in a balance factor of 0 indicated that the subtree's
height has remained unchanged.
MODULE VI : Searching , Sorting and Complexity

6.1 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:


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

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

6.2 (a) 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

6.2 (b)C Implementation of the Algorithm

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


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

6.2 (c) 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.
/*C: Linear search in a sorted array. */

#include <stdio.h>
#include <conio.h>
void main( )
{
int arr[10] = { 1, 2, 3, 9, 11, 13, 17, 25, 57, 90 } ;
int i, num ;
clrscr( ) ;
printf ( "Enter number to search: " ) ;
scanf ( "%d", &num ) ;
for ( i = 0 ; i <= 9 ; i++ )
{ if ( arr[9] < num || arr[i] >= num )
{ if ( arr[i] == num )
printf ( "The number is at position %d in the array.", i ) ;
else
printf ( "Number is not present in the array." ) ;
break ;
}
}
getch( ) ;
}

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


6.3 (a) 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
else
set loc=mid
endif
end

6.3 (b) 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;
}

6.3 (c) /* C: Binary search in a sorted array. */

#include <stdio.h>
#include <conio.h>
void main( )
{
int arr[10] = { 1, 2, 3, 9, 11, 13, 17, 25, 57, 90 } ;
int mid, lower = 0 , upper = 9, num, flag = 1 ;
clrscr( ) ;
printf ( "Enter number to search: " ) ;
scanf ( "%d", &num ) ;
for ( mid = ( lower + upper ) / 2 ; lower <= upper ;
mid = ( lower + upper ) / 2 )
{ if ( arr[mid] == num )
{
printf ( "The number is at position %d in the array.", mid ) ;
flag = 0 ;
break ;
}
if ( arr[mid] > num )
upper = mid - 1 ;
else
lower = mid + 1 ;
}
if ( flag )
printf ( "Element is not present in the array." ) ;

getch( ) ;
}

6.4 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
computer’s RAM.
• Internal sorting: This deals with sorting of data held in the RAM of a computer

6.4.1 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
6.4.1 (a)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.
• 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].

(A) 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

(B) /* C: Bubble sort. */

#include <stdio.h>

#include <conio.h>

void main( )

int arr[5] = { 25, 17, 31, 13, 2 } ;

int i, j, temp ;
clrscr( ) ;

printf ( "Bubble sort.\n" ) ;

printf ( "\nArray before sorting:\n") ;

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

printf ( "%d\t", arr[i] ) ;

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

for ( j = 0 ; j <= 3 - i ; j++ )

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

temp = arr[j] ;

arr[j] = arr[j + 1] ;

arr[j + 1] = temp ;

}}}

printf ( "\n\nArray after sorting:\n") ;

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

printf ( "%d\t", arr[i] ) ;

getch( ) ;

}
6.4.2 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 B’s pointer to point to the next element in array B.

/* C: Merge Sort. */

#include <stdio.h>

#include <conio.h>

void main( )

int a[5] = { 11, 2, 9, 13, 57 } ;

int b[5] = { 25, 17, 1, 90, 3 } ;

int c[10] ;
int i, j, k, temp ;

clrscr( ) ;

printf ( "Merge sort.\n" ) ;

printf ( "\nFirst array:\n" ) ;

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

printf ( "%d\t", a[i] ) ;

printf ( "\n\nSecond array:\n" ) ;

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

printf ( "%d\t", b[i] ) ;

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

for ( j = i + 1 ; j <= 4 ; j++ )

if ( a[i] > a[j] )

temp = a[i] ;

a[i] = a[j] ;

a[j] = temp ;

if ( b[i] > b[j] )

{
temp = b[i] ;

b[i] = b[j] ;

b[j] = temp ;

for ( i = j = k = 0 ; i <= 9 ; )

if ( a[j] <= b[k] )

c[i++] = a[j++] ;

else

c[i++] = b[k++] ;

if ( j == 5 || k == 5 )

break ;

for ( ; j <= 4 ; )

c[i++] = a[j++] ;

for ( ; k <= 4 ; )

c[i++] = b[k++] ;
printf ( "\n\nArray after sorting:\n") ;

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

printf ( "%d\t", c[i] ) ;

getch( ) ;

6.4.3 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].


2. Interchange a[n-2] & a[loc]. Then elements a[0],a[1],a[2]….a[n-1] are sorted.
2.4.3 (a) 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

6.4.3(b)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

/* C: Selection sort. */

#include <stdio.h>

#include <conio.h>

void main( )

{ int arr[5] = { 25, 17, 31, 13, 2 } ;

int i, j, temp ;

clrscr( ) ;

printf ( "Selection sort.\n" ) ;

printf ( "\nArray before sorting:\n") ;

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

printf ( "%d\t", arr[i] ) ;

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

{ for ( j = i + 1 ; j <= 4 ; j++ )

{ if ( arr[i] > arr[j] )


{

temp = arr[i] ;

arr[i] = arr[j] ;

arr[j] = temp ;

}}}

printf ( "\n\nArray after sorting:\n") ;

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

printf ( "%d\t", arr[i] ) ;

getch( ) ;

6.4.4 Heap Sort

The heap sort algorithm relies on the connection between an complete binary tree and the
array of values that correspond to it.

The heap sort algorithm has two major steps. The first major step involves transforming
the complete tree into a heap. The second major step is to perform the actual sort by
extracting the largest element from the root and transforming the remaining tree into a
heap.

In this step, the complete tree is transformed into a heap. This process is accomplished by
beginning at the top of the tree. Each subtree is transformed into a heap by an operation
similar to the enqueue operation of a priority queue. .

Once the tree has been transformed into a heap, the root node contains the largest value--
a property that is true of all heaps. The root value is swapped with the value in the right-
most node on the lowest level
/* C: Heap Sort. */

#include <stdio.h>

#include <conio.h>

void makeheap ( int [ ], int ) ;

void heapsort ( int [ ], int ) ;

void main( )

int arr[10] = { 11, 2, 9, 13, 57, 25, 17, 1, 90, 3 } ;

int i ;

clrscr( ) ;

printf ( "Heap Sort.\n" ) ;

makeheap ( arr, 10 ) ;

printf ( "\nBefore Sorting:\n" ) ;

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

printf ( "%d\t", arr[i] ) ;

heapsort ( arr, 10 ) ;

printf ( "\nAfter Sorting:\n" ) ;

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

printf ( "%d\t", arr[i] ) ;

getch( );

}
void makeheap ( int x[ ], int n )

{ int i, val, s, f ;

for ( i = 1 ; i < n ; i++ )

{ val = x[i] ;

s=i;

f=(s-1)/2;

while ( s > 0 && x[f] < val )

x[s] = x[f] ;

s=f;

f=(s-1)/2;

x[s] = val ;

}}

void heapsort ( int x[ ], int n )

int i, s, f, ivalue ;

for ( i = n - 1 ; i > 0 ; i-- )

{ ivalue = x[i] ;

x[i] = x[0] ;
f=0;

if ( i == 1 )

s = -1 ;

else

s=1;

if ( i > 2 && x[2] > x[1] )

s=2;

while ( s >= 0 && ivalue < x[s] )

x[f] = x[s] ;

f=s;

s=2*f+1;

if ( s + 1 <= i - 1 && x[s] < x[s + 1] )

s++ ;

if ( s > i - 1 )

s = -1 ;

x[f] = ivalue ;

}}
Quick Sort

Quick sort is a very popular sorting method. The name comes from the fact that, in
general, quick sort can sort a list of data elements significantly faster than any of the
common sorting algorithms. This algorithm is based on the fact that it is faster and easier
to sort two small arrays than one larger one. The basic strategy of quick sort is to divide
and conquer.

• Divide:
• Pick any element p as the pivot, e.g, the first element
• Partition the remaining elements into

FirstPart, which contains all elements < p

SecondPart, which contains all elements ≥ p

• Recursively sort the FirstPart and SecondPart


• Combine: no work is necessary since sorting is done in place

P
Partition Algorithm

Partition(A, left, right)

1. x ← A[left]
2. i ← left
3. for j ← left+1 to right
4. if A[j] < x then
5. i←i+1
6. swap(A[i], A[j])
7. end if
8. end for j
9. swap(A[i], A[left])

return i
Insertion Sort

Insertion sort is implemented by inserting a particular element at the appropriate position.


In this method, the first iteration starts with comparison of 1st element with the 0th
element. In the second iteration 2nd element is compared with the 0th and 1st element. In
general, in every iteration an element is compared with all elements before it. During
comparison if it is found that the element in question can be inserted at a suitable position
then space is created for it by shifting the other elements one position to the right and
inserting the element at the suitable position. This procedure is repeated for all the
elements in the array.
//Program to sort the given array using Insertion Sort

#include<stdio.h>
#include<conio.h>
void inssort(int[],int);

void main()
{ int ar[50],n,i;
clrscr();
printf("\n\n How many elements do u want to enter...");
scanf("%d",&n);
printf("\n\n Enter Array elements : ");
for(i=0;i<n;i++)
scanf("%d",&ar[i]);
inssort(ar,n);
printf("\n\n The sorted array is as shown below...\n");
for(i=0;i<n;i++)
printf("%d",ar[i]);
getch();
}
void inssort(int a[],int n)
{
int temp,j,k;
for(k=1;k<=n;k++)
{
temp=a[k];
j=k-1;
while((temp<a[j]) && (j>=0))
{
a[j+1]=a[j];
j=j-1;
}
a[j+1]=temp;
printf("\n\n Array after every pass \n");
for(int i=0;i<n;i++)
printf("\t%d",a[i]);
}
}

OUTPUT
How many elements do u want to enter...5

Enter Array elements : 3 9 5 4 2

Array after every pass

3 9 5 4 2

Array after every pass

3 5 9 4 2

Array after every pass

3 4 5 9 2

Array after every pass

2 3 4 5 9

Array after every pass

2 3 4 5 9

The sorted array is as shown below...

23459
MODULE VII : Graph

7.1 Introduction to Graph

Graph is an important non linear data structure. This data structure is used to represent
relationship between pairs of elements, which may not, necessarily, be hierarchical in
nature.

A graph is defined as: “Graph G is an ordered set (V, E), where V(G) represents the set
of elements, called vertices, and E(G) represents the edges between these vertices.”

Graphs can be of either type,

Undirected Graph
Directed Graph

Figures below show sample graphs.


V(G) = { v1, v2, v3, v4, v5 }
E(G) = { e1, e2, e3, e4, e5 }

Undirected Graph – In the undirected graph the pair of vertices representing any edge
is unordered. Thus, the pairs (v1,v2) and (v2,v1) represent the same edge.
Directed Graph - In a directed graph each edge is represented by a directed pair
<v1,v2>. V1 is the tail and v2 the head of the edge. Therefore <v2,v1> and <v1,v2>
represent two different edges.

7.2 Graph Theory Terminologies

Adjacent Vertices
An edge e is represented by a pair of vertices denoted by [u, v]. The vertices u and v are
called endpoints of edge e. These vertices are also called adjacent vertices or neighbors.

Degree of a Vertex
The degree of vertex u, written as degree(u) or d(u), is the number of edges containing
u. If d(u) = 0, this means that vertex u does not belong to any edge, then vertex u is
called an isolated vertex.

Path
A path P of length n from a vertex u to vertex v is defined as sequence of (n + 1)
vertices, i.e., P = (v 1, v 2, v 3 …v n+1). The path is said to be closed if the endpoints or
end-vertices of the path are same, i.e., v 1 = v n+1. The path is said to be simple if all the
vertices in the sequence are distinct, with the exception that v 1 = v n+1. In latter case, it
is known as closed simple path.
Cycle
A cycle is closed simple path with length of 2 or more. Sometimes, a cycle of length k
(i.e., k distinct vertices in the path) is known as k-cycle.

Connected Graph
A graph is said to be connected if there exist a path between any two of its vertices, i.e.,
an isolated vertex does not exist. A connected graph without any cycles is called a tree.
Thus we can say that tree is a special graph.

Complete Graph
A graph G is said to be complete or fully connected if there exist a path from every
vertex to every other vertex. A complete graph with n vertices will have (n (n-1))/2
edges.

Weighted Graph
A graph is said to be a weighted graph if every edge in the graph is assigned some data or
value. The weight is denoted by w(e). w(e) is non negative value that may be
representing the cost of moving along that edge or distance between the vertices.
Multiple Edges
Distinct edges e and e’ are called multiple edges if they connect the same end points, i.e.,
if e = [u, v] and e’ = [u, v].

Loop
An edge is a loop if it has identical endpoints, i.e., if e = [u, u].

7.3 Sequential representation of Graph

The most commonly used representations for graphs are Adjacency matrices and
Adjacency Lists.

7.3.1 Adjacency Matrices

The adjacency matrix is a two dimensional array of size nxn (where n is the number of
vertices in the graph) with the property that a[i][j]=1 if edge (vi,vj) is in the set of edges
and a[i[[j]=0, if there is no such edge.

Logical representation of graph.


The adjacency matrix for our graph is given below.
A B C D E F

A -- 1 1 1 -- --

B 1 -- 1 -- 1 --

C 1 1 -- -- -- --

D 1 -- -- -- 1 1

E -- 1 -- 1 -- --

F -- -- -- 1 -- --

Notice that the matrix shown above has six rows and six columns labeled with the nodes
from the graph. We mark a '1' in a cell if there exists an edge from two nodes that index
that cell. For example, since we have a edge between A and B, we mark a '1' in the cells
indexed by A and B. These cells are marked with a dark gray background in the
adjacency matrix. With our adjacency matrix, we can represent every possible edge that
our graph can have.

7.3.2 Adjacency List

In graph theory, an adjacency list is the representation of all edges or arcs in a graph as a
list. If the graph is undirected, every entry is a set (or multiset) of two nodes containing
the two ends of the corresponding edge; if it is directed every entry is a tuple of two
nodes, one denoting the source node and the other denoting the destinataion node of the
corresponding arc. Typically, Adjacency lists are unordered.
In computer science, adjacency list is a data structure for representing the graphs. In an
adjacency list representation we keep, for each vertex in the graph, a list of all other
vertices which it has an edge to (that vertex's "adjacency list"). One difficulty with the
adjacency list structure is that it has no obvious place to store data associated with the
edges of a graph, such as the lengths or costs of the edges.

There are many ways to implement this adjacency representation. One way is to have the
graph maintain a list of lists, in which the first list is a list of indices corresponding to
each node in the graph. Each of these refer to another list that stores a index of each
adjacent node to this one. It might also be useful to associate the weight of each link with
the adjacent node in this list.

1 2

3 4

Example: An undirected graph contains four nodes 1, 2, 3 and 4. 1 is linked to 2 and 3. 2


is not linked to any other. 3 is linked to 4.

1 → 2 → 3
2 →
3 → 4
4 → 1
5 → 2 → 4
It might be useful to store the list of all the nodes in the graph in a hash table. The keys
then would correspond to the indices of each node and the value would be a reference to
the list of adjacent node indecies.

Another implementation might require that each node keep a list of its adjacent nodes.

7.3.3 Graph Traversal

The breadth first search (BFS) and the depth first search (DFS) are the two algorithms
used for traversing and searching a node in a graph. They can also be used to find out
whether a node is reachable from a given node or not.

Depth First Search (DFS)

The aim of DFS algorithm is to traverse the graph in such a way that it tries to go far
from the root node. Stack is used in the implementation of the depth first search. Let’s see
how depth first search works with respect to the following graph:
As stated before, in DFS, nodes are visited by going through the depth of the tree from
the starting node. If we do the depth first traversal of the above graph and print the visited
node, it will be “A B E F C D”.

DFS visits the root node and then its children nodes until it reaches the end node, i.e. E
and F nodes, then moves up to the parent nodes.

Algorithmic Steps

Step 1: Push the root node in the Stack.

Step 2: Loop until stack is empty.

Step 3: Peek the node of the stack.

Step 4: If the node has unvisited child nodes, get the unvisited child node, mark it as
traversed and push it on stack.

Step 5: If the node does not have any unvisited child nodes, pop the node from the stack.

Breadth First Search (BFS)

This is a very different approach for traversing the graph nodes. The aim of BFS
algorithm is to traverse the graph as close as possible to the root node. Queue is used in
the implementation of the breadth first search. Let’s see how BFS traversal works with
respect to the following graph:
If we do the breadth first traversal of the above graph and print the visited node as the
output, it will print the following output. “A B C D E F”. The BFS visits the nodes level
by level, so it will start with level 0 which is the root node, and then it moves to the next
levels which are B, C and D, then the last levels which are E and F.

Algorithmic Steps

Step 1: Push the root node in the Queue.

Step 2: Loop until the queue is empty.

Step 3: Remove the node from the Queue.

Step 4: If the removed node has unvisited child nodes, mark them as visited and insert the
unvisited children in the queue.
7.4 Spanning Tree

A spanning tree of a graph is an undirected tree consisting of only those edges that are
necessary to connect all the vertices in the original graph. A spanning tree has a property
that for any pair of vertices there exist only one path between them, and the insertion of
any edge to a spanning tree form a unique cycle.The particular spanning tree for a graph
depends on the criteria used for generating it.

The spanning tree is useful in-

1) analysis of electrical circuits

2) shortcut route problems

7.5 Constructing a Spanning Tree

Prim Algorithm

Given a connected weighted graph G, it is often desired to create a spanning tree T for G
such that the sum of the weights of the tree edges in T is as small as possible. Such a tree
is called as minimum spanning tree and represents the most inexpensive way of
connecting all the nodes in G.

There are a number of techniques for creating a minimum spanning tree for a weighted
graph. The first of these, Prim’s algorithm, discovered independently by Prim and
Dijkstra, is very much like Dijkstra’s algorithm for finding shortest paths. An arbitrary
node is chosen initially as the tree root (note that in an undirected graph and its spanning
tree, any node can be considered the tree root and the nodes adjacent to it as its sons).

The nodes of the graph are then appended to the tree one at a time until all nodes of the
graph are included. The node of the graph added to the tree at each point is that node
adjacent to a node of the tree by an arc of minimum weight. The arc of minimum weight
becomes a tree arc connecting the new node to the tree. When all the nodes of the graph
have been added to the tree, a minimum spanning tree has been constructed for the graph.

To see that this technique creates a minimum spanning tree, consider a minimum
spanning tree T for the graph and consider the partial tree PT built by Prim’s algorithm.

Suppose that (a, b) is the minimum-cost arc from nodes in PT to nodes not in PT, and
suppose that (a, b) is not in T. Then, since there is a path between any two graph nodes in
a spanning tree, there must be an alternate path between a and b in T that does not include
arc (a, b). This alternate path P must include an arc (x, y) from a node in PT to a node
outside of PT. Let us assume that P contains sub-paths between a and x and between y
and b.

Now consider what would happen if we replaced arc (x, y) in T with (a, b) to create NT.
We claim that NT is also a spanning tree. To prove this, we need to show two things: that
any two nodes of the graph are connected in NT and that NT does not contain a cycle,
that is, that there is only one path between any two nodes in NT.

Since T is a spanning tree, any two nodes, m and n, are connected in T. If the path
between them in T does not contain (x, y), the same path connects them in NT. If the path
between them in T does contain (x, y), consider the path in NT formed by the sub-path in
T from m to x, the sub-path in P (which is in T) from x to a, the arc (a, b), the sub-path in
P from b to y, and the sub-path in T from y to n. This is a path from m to n in NT. Thus
any two nodes of the graph are connected in NT.

To show that NT does not contain a cycle, suppose that it did. If the cycle does not
contain (a, b) then the same cycle would exist in T. But that is impossible, since T is a
spanning tree. Thus the cycle must contain (a, b). Now consider the same cycle with arc
(a, b) replaced with the sub-path of P between a and x, the arc (x, y), and the sub-path in
P between y and b. The resulting path must also be a cycle and is a path entirely in T.
But, again, T cannot contain a cycle. Therefore NT also does not contain a cycle.

NT has thus been shown to be a spanning tree. But NT must have lower cost than T, since
(a, b) was chosen to have lower cost than (x, y). Thus it is not a minimum spanning tree
unless it includes the lowest weight arc from PT to nodes outside PT. Therefore any arc
added by Prim’s algorithm must be part of a minimum spanning tree.

The crux of the algorithm is a method for efficient determination of the “closest” node to
a partial spanning tree. Initially, when the partial tree consists of a single root node, the
distance of any other node n from the tree, distance[n], is equal to weight(root ,n). When
a new node, current, is added to the tree, distance[n] is modified to the minimum of
distance[n] and weight(current, n). The node added to the tree at each point is the node
whose distance is lowest. For nodes m in the tree, distance[m] is set to infinity, so that a
node outside the tree is chosen as closest. An additional array closest[n] points to the
node in the tree such that distance[n] = weight(closest[n], n); that is, the node in the tree
closest to n. If two nodes, x and y, are not adjacent, weight(x, y) is also infinity.

Kruskal Algorithm

An algorithm which is used to create a minimum spanning tree is attributed to Kruskal's


algorithm. In this algorithm a minimum cost Spanning tree T is built edge by edge. Edges
are considered for inclusion in T in increasing order of their costs. An edge is included in
T if it does not form a cycle with edges already in T.

The nodes of the graph are initially considered as n distinct partial trees with one node
each. At each step of the algorithm, two distinct partial trees are connected into a single
partial tree by an edge of the graph. When only one partial tree exists (after n-1 such
steps), it is a minimum spanning tree.

The concern is what connecting arc to use at each step. The answer is to use the arc of
minimum cost that connects two distinct trees. To do this, the arcs can be placed in a
priority queue based on the weight. The arc of lowest weight is then examined to see if it
connects two distinct trees.
To determine if an arc (x, y) connects distinct trees, we can implement the trees with a
father field in each node. Then we can traverse all ancestors of x and y to obtain the roots
of the trees containing them. If the roots of the two trees are the same node, x and y nodes
are already in the same tree, arc (x, y) is discarded, the arc of next lowest weight is
examined. Combining two trees simply involves setting the father of the root of one to
the root of the other.

Algorithm

a) Forming the initial priority queue, i.e., O(e log e)


b) Removing the minimum weight arc and adjusting the priority queue, i.e., O(log e).
c) Locating the root of a tree, i.e., O(log n).
d) Initial formation of n trees, i.e., O(n).
e) Assume n < e.

Every tree has a root node, and all the other nodes in the tree are children of a perticular
node. The nodes can have many children but only one parent. When we relax these
restrictions, we get the graph data structure. The logical representation of a typical graph
might look something like the figure shown below:
It is not hard to imagine how the graph data structure could be useful for representing the
data. Perhaps each of the nodes above could represent a city and the edges connecting the
nodes could represent the roads. Or we could use a graph to represent a computer
network where the nodes are workstations and the edges are the network connections.
Graphs have so many applications in the computer science and mathematics that several
algorithms have been written to perform the standard graph operations such as searching
the graph and finding the shortest path between nodes of a graph.

Notice that our graph does not have any root node like the tree data structure. Instead, any
node can be connected with any other node in the graph. Nodes do not have any clear
parent-child relationship like we saw in the tree. Instead nodes are called as neighbors if
they are connected by an edge. For example, node A above has three neighbors: B, C, and
D.

A graph consists of a set of nodes (or Vertices ) and a set of arc (or edge ). Each arc in a
graph is specified by a pair of nodes. A node n is incident to an arc x if n is one of the two
nodes in the ordered pair of nodes that constitute x. The degree of a node is the number
of arcs incident to it. The indegree of a node n is the number of arcs that have n as the
head, and the outdegree of n is the number of arcs that have n as the tail

The graph is the nonlinear data structure. The graph shown in the figure represents 7
vertices and 12 edges. The Vertices are { 1, 2, 3, 4, 5, 6, 7} and the arcs are {(1,2), (1,3),
(1,4), (2,4), (2,5), (3,4), (3,6), (4,5), (4,6), (4,7), (5,7), (6,7) }. Node (4) in figure has
indegree 3, outdegree 3 and degree 6.
Problem for Practice

1. What is data structure?

A data structure is a way of organizing data that considers not only the items
stored, but also their relationship to each other. Advance knowledge about the
relationship between data items allows designing of efficient algorithms for the
manipulation of data.

2. List out the areas in which data structures are applied extensively?

 Compiler Design,
 Operating System,
 Database Management System,
 Statistical analysis package,
 Numerical Analysis,
 Graphics,
 Artificial Intelligence,
 Simulation
3. What are the major data structures used in the following areas :
RDBMS, Network data model & Hierarchical data model.

 RDBMS – Array (i.e. Array of structures)


 Network data model – Graph
 Hierarchical data model – Trees

4. If you are using C language to implement the heterogeneous linked


list, what pointer type will you use?
The heterogeneous linked list contains different data types in its nodes and we need a
link, pointer to connect them. It is not possible to use ordinary pointers for this. So we
go for void pointer. Void pointer is capable of storing pointer to any type as it is a
generic pointer type.

5. Minimum number of queues needed to implement the priority


queue?
Two. One queue is used for actual storing of data and another for storing priorities.

6. What is the data structures used to perform recursion?


Stack. Because of its LIFO (Last In First Out) property it remembers its ‘caller’ so
knows whom to return when the function has to return. Recursion makes use of
system stack for storing the return addresses of the function calls.

Every recursive function has its equivalent iterative (non-recursive) function. Even
when such equivalent iterative procedures are written, explicit stack is to be used.
7. What are the notations used in Evaluation of Arithmetic Expressions
using prefix and postfix forms?

Polish and Reverse Polish notations.

8. Convert the expression ((A + B) * C – (D – E) ^ (F + G)) to


equivalent Prefix and Postfix notations.

Prefix Notation:

^ - * +ABC - DE + FG
Postfix Notation:

AB + C * DE - - FG + ^

9. Sorting is not possible by using which of the following methods?

(a) Insertion

(b) Selection

(c) Exchange

(d) Deletion

Using insertion we can perform insertion sort, using selection we can perform selection
sort, using exchange we can perform the bubble sort (and other similar sorting methods).
But no sorting method can be done just using deletion.
10.A binary tree with 20 nodes has null branches?
21

Let us take a tree with 5 nodes (n=5)

Null Branches

It will have only 6 (ie,5+1) null branches. In general,

A binary tree with n nodes has exactly n+1 null nodes.

11. What are the methods available in storing sequential files ?

 Straight merging,
 Natural merging,
 Polyphase sort,
 Distribution of Initial runs.
12.How many different trees are possible with 10 nodes ?

1014

For example, consider a tree with 3 nodes(n=3), it will have the maximum combination
of 5 different (ie, 23 - 3 = 5) trees.

i ii iii iv v

In general:

If there are n nodes, there exist 2n-n different trees.

13.List out few of the Application of tree data-structure?


 The manipulation of Arithmetic expression,
 Symbol Table construction,
 Syntax analysis.

14. List out few of the applications that make use of Multilinked

Structures?
 Sparse matrix,
 Index generation.
15.In tree construction which is the suitable efficient data structure?

(a) Array (b) Linked list (c) Stack (d) Queue (e) none

(b) Linked list

24.Convert the given graph with weighted edges to minimal spanning


tree.

600
1 3 200
612
410 310
2985 5

2 400
1421
4

The equivalent minimal spanning tree is:

1 3

200
410 612 310

2 4 5
25.Does the minimum spanning tree of a graph give the shortest
distance between any 2 specified nodes?

No.

Minimal spanning tree assures that the total weight of the tree is kept at its minimum. But
it doesn’t mean that the distance between any two nodes involved in the minimum-
spanning tree is minimum.

26.What is a spanning Tree?

A spanning tree is a tree associated with a network. All the nodes of the graph appear on
the tree once. A minimum spanning tree is a spanning tree organized so that the total
edge weight between nodes is minimized.

27.Traverse the given tree using Inorder, Preorder and Postorder


traversals.

Given tree:
A

B C

D E F G

H I J
 Inorder : DHBEAFCIGJ
 Preorder:A B D H E C F G I J
 Postorder:H D E B F I J G C A

Reference Books

1. “Data Structure using C, Yashwant Kanetkar, BPB Publications

2. “ Data Structures using C” by A.M. Tanenbaum and Other

S-ar putea să vă placă și