DS Book

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 375

DATA STRUCTURES AND ALGORITHM

ALGORITHMS WITH ANALYSES

MULTIPLE CHOICE QUESTIONS AND ANSWERS

QUESTIONS OF UNIVERSITIES, GATES AND SOLUTIONS

SAMPLE PROGRAMS FOR LINKED REPRESENTATION OF STACK, QUEUE AND TREES

Dr.Arup Kr.Bhaumik
Santanu Haldar
Subhrajit Sinha Roy
CONTENTS

Preface

Acknowledgements

1. INTRODUCTION
1.1 Information and Data
1.2 Basic Operation of a Computer
1.3 Solvability of Problem and Data Structure
1.4 Mathematical Modeling of Data
1.5 Representation of Numbers in Computer
1.6 Group Data or Structured Data
1.7 Algorithm and Notations Used in The Book
1.8 MCQ Chapter 1
1.9 Exercises Chapter 1

2. PRELIMINARIES
2.1 Definition of algorithm
2.2 Properties of an Algorithm
2.3 Algorithm Development Life Cycle
2.4 Example of some Algorithms
2.5 Complexity of Algorithms (Analyzing phase)
2.5.1 Asymptotic notations
2.5.2 How can we estimate the time complexity in big Oh(O) notation?
2.5.3 How can we improve the complexity of algorithms?
2.5.4 Properties of Big Oh
2.5.5 Rate of growth of Big Oh notation
2.6 Some popular Mathematical Notations and Functions
2.7 Algorithmic Notation
2.8 MCQ Chapter 2
2.9 Exercises Chapter 2

3. ARRAY
3.1 Introduction and Definition
3.2 Declaration of Array
3.3 Memory Representation of Array
3.4 Insertion in one dimentional Array
3.5 Deletion from one dimentional Array
3.6 Travarsing one dimentional Array
3.7 Merging two one dimentional Array
3.8 Matrix Addition
3.9 Sparse Matrix
3.10 Polynomial Representation
3.11 MCQ Chapter 3
3.12 Exercises Chapter 3

4 STRUCTURE AND POINTER


4.1 Definition of Structure
4.2 Basic of Pointer
4.3 Structure and Pointer
4.4 Passing Structure to Pointer
4.5 Self Refferential Structure
4.6 Dynamic Memory Allocation
4.7 MCQ Chapter 4
4.8 Exercises Chapter 4

5. STRINGS
5.1 Definition
5.2 Concatenation and substrings
5.3 String operations
5.3.1 Alphabet of a string
5.3.2 String substitution
5.3.3 String homomorphism
5.3.4 String projection
5.3.5 String Right quotient
5.3.6 String Syntactic relation
5.3.7 String Right cancellation
5.3.8 String Prefixes
5.4 String Topology
5.5 String datatypes
5.6 String length
5.7 String Representations
5.8 String as Vectors
5.9 String processing algorithms
5.9.1 String searching algorithms
5.9.1.1 Basic classification
5.9.1.2 Single pattern algorithms
5.9.1.3 Algorithms using finite set of patterns
5.9.1.4 Algorithms using infinite number of patterns
5.9.2 String manipulation algorithms or String Functions
5.9.3 String Sorting algorithms
5.9.4 Regular Expression Algorithms
5.9.5 Parsing a String
5.10 String Implementations
5.11 MCQ Chapter 5
5.12 Exercises Chapter 5

6. STACK
6.1 Definition
6.2 Basic Operations
6.3 Implementation of stack using array
6.4 Implementation of stack using dynamic memory allocation
6.5 Arithmetic Expressions
6.6 Converting Infix expressions to postfix notation
6.7 Converting Infix expression to prefix notation
6.8 Evaluating Postfix Expression
6.9 Evaluating Prefix Expression
6.10 Application of Stack
6.11 Reversing a string
6.12 Checking balanced parenthesis
6.13 Multiple Stacks
6.14 MCQ Chapter 6
6.15 Exercises Chapter 6

7. QUEUE
7.1 Definition
7.2 Implementation of queue using array
7.3 Implementation of Queue using dynamic memory allocation
7.4 Limitation of linear queue
7.5 Circular Queue
7.6 Deque(Double Ended Queue)
7.7 Priority Queue
7.8 Implementation of queue using stacks
7.9 Application of Queue
7.10 MCQ Chapter 7
7.11 Exercises Chapter 7

8. LINKED LIST
Introduction
Definition
Advantages
Disadvantages
Operations on Linked List
Types of Linked List
Singly Linked List
Reversing a Singly Linked List
Polynomial using singly linked list
Advantages and disadvantages of singly linked list
Circular linked list
Double linked list
MCQ Chapter 8
Exercises Chapter 8

9. RECURSION
Definition
Types of recursion
Linear recursion
Binary recursion
Multiple recursion
Some recursive algorithms
Factorial function
GCD of two numbers
Exponential Power
Fibonacci sequence
Towers of Hanoi
Tail of recursion
Disadvantages of recursion
Recursion Vs Iteration
MCQ Chapter 9
Exercises Chapter 9

10. TREE
Definition
Basic terminologies
Binary tree
Types of binary trees
Properties of binary trees
Representation of binary tree
Binary tree traversal methods
Creation of binary tree from preorder and inorder traversal
Creation of binary tree from postorder and inorder traversal
Expression tree
Binary search tree
Heap
Weight balanced binary tree
AVL Search tree / Height balanced binary tree
Threaded binary tree
M-way search tree
B-Tree/Balanced M-way search tree
Red/Black tree
MCQ Chapter 10
Exercises Chapter 10

11. GRAPH
Definition
Basic Terminologies
Representation of a graph
Operations on a graph
Traversal of a graph
Breadth first search
Depth first search
Shortest path calculation (Dijkstra’s algorithm)
Warshall’s algorithm
MCQ Chapter 11
Exercises Chapter 11

12. SORTING
12.1 Definition
12.2 The family of sorting methods
12.3 Bubble Sort
12.4 Insertion Sort
12.5 Selection Sort
12.6 Merge Sort
12.7 Quick Sort
12.8 Heap Sort
12.9 Radix Sort
12.10 Comparison study of different sorting
12.11 MCQ Chapter 12
12.12 Exercise Chapter 12

13. SEARCHING
13.1 Introduction
13.2 Sequential Search
13.3 Binary Search
13.4 MCQ Chapter 13
13.5 Exercises Chapter 13

14. HASHING
14.1 Introduction
14.2 Hash Table
14.3 Hash Functions
14.4 Collision
14.5 Clustering
14.6 Collision resolution Technique
14.7 Load Factor
14.8 Analysis of Open Addressing and Chaining
14.9 MCQ Chapter 14
14.10 Exercises Chapter 14

15. FILES
15.1 Introduction
15.2 Definition
15.3 File Classification
15.3.1 Non Structured file
15.3.1.1 Text File
15.3.1.2 System File
15.3.1.2.1 Command File (.COM):
15.3.1.2.2 Executable File (.EXE)
15.3.1.2.3 Dynamic Link Laboratory File(.DLL)
15.4 Structured File
15.4.1 Sequential File
15.4.2 Index Sequential File
15.5 MCQ Chapter 15
15.6 Exercises Chapter 15

16. Various Questions on Data Structure


Preface

The data nowadays play very important role in computation. Versatile usage of computers in every
spheres of life enhances handling of different kinds of data and it’s representation in computer’s
memory. But a programmer must have clear idea about requirement of data structure and algorithm for
writing solution of a problem in computer. Otherwise, efficiency will never be achieved.

Though there are several books on data structure available in the market but in a particular book all the
topics like algorithms, analyses, equivalent programs in ‘C’ language , Multiple Choice Questions (MCQ
) and excersies are not available. On delivering lectures for 10 years since in theory and lab classes in
this subject we have faced many questions of students and their suggestions. In many occasions we have
used our intuition to make the students understanding in different topics of this subject. We have
incorporated all of these strategies in our book and also programs are written in ‘C’ language to
encourage students to try for testing of algorithms.

The book has been written by covering syllabi of B.Tech, B.E., MCA, BCA & Bsc. Computer sc. courses
of all most all Indian and Foreign Universities. Ho.pe everybody from new learner to expert
programmer may get help from the book.
Acknowledgements

The most inspiaratation that all of us have received from our beloved students, thus we must thank them
at first. Secondly, Mr. S.K.Bagal and Mr. Lahiri of S. Chand & Co.Ltd. both of them have tendered their
help, courage and moral support to write the book, all of us are very thankful to both of them. While
preparation of this manuscript we have received help and criticisims from our colleague Mr. Animesh
Hazra, Lecturer Computer Sc. & Engg. So we convey our thanks to Mr.Hazra.
Chapter 1
Introduction

1.1: Information and Data

Information: The Oxford dictionary meaning is (1) Facts or Knowledge provided or learned. (2)
Computer Data.

Data: The Oxford dictionary meaning is (1) Facts or statistics used for reference or analysis (2) The
quantities, characters or symbols operated on by a Computer.

Thus, from the definitions of the dictionary one can assume that the terms information and data can be used
interchangeably. But in context of the computer any recorded or stored fact, which needs for processing by the
Central Processing Unit (CPU) is called data i.e. for example Student’s name, Address, Marks obtained in
different subjects when recorded is called data and when a program is written to find grades of students on the
basis of marks obtained in various subjects the output will appear as ‘A’, ‘B’, ‘C’ etc. or ‘First Class’, ‘ 2 nd
Class’ etc., which are information. Moreover, if grades are recorded or stored for further computation then
these will be termed as data also.

1.2 Basic Operation of a Computer

Any computer has three distinct units, viz. Input/Output, Memory and Central Processing Unit (CPU).
The input unit is used to feed data to the computer’s memory and the output unit is used to show the result after
computation. The memory unit is used to store data and program. The CPU is the main unit comprising of two
sub units viz. Control unit (CU) and the Arithmetic Logic Unit (ALU).

The CPU does following operations in steps in repeated manner:


Step 1: Fetch an instruction from memory
Step 2: Decode the instruction
Step 3: Fetch the operands needed for the operation
Step 4: Execute the instruction in ALU

It is clear from the steps of operations that first of all computer needs an instruction i.e. a command (to do some
operations) and then operand (or data) is required for completion of operation.
The operations performed by a bare CPU are very basic such as Addition, Subtraction, Multiplication, AND,
OR, NOT etc. on binary data. Thus, if everything i.e. instructions and data are represented in binary then the
binary sequence is called machine code. But to work with machine code is very difficult either to write a full
program or later on to debug it. To make the understanding of machine code just easy the equivalent
hexadecimal representation is used.

Thus, for a bare CPU the data types like integers, real etc. has no meaning, these are the descriptions of data
done through high level language (HLL) and the translator program i.e. either compiler or interpreter takes
responsibility to convert integer, real etc. data type to binary format and vice versa.

1.3 Solvability of Problem and Data Structure


There are various types of problem framed in different aspects of life in our society e.g. computation of salary,
income tax etc. for employees, debit, credit analysis and budgeting of any firm, weather forecasting, designing
and analyzing structures for cars, airplane, building etc., analysis for gene etc. The quicker solutions of these
problems ensure progress of a society. Nowadays, computers are used in every sphere of life to get solutions of
problems in a quickest and accurate way. But computer needs appropriate program for each problem. Problems
like computation of factorial of a positive integer, sorting a list of names, computation of salary of employees,
computation of free memory blocks etc. are required different methods with different data set (values) to solve
each of these. Whenever a problem is to be solved through a computer, a program is required to develop, and in
a program data are passed through variables, where variables are used to represent memory references either in
single location or in a group of locations to store atomic or group data respectively.

1.4 Mathematical Modeling of Data

In our mathematics any real value is classified into:

i) Integer numbers (both +ve and –ve) e.g.


0, ±1, ±2, ±3, . . . etc

ii) Rational numbers: Number derived from division of two integers (x, y) such as x/y, where y ≠ 0. The
number x/y is also called a fraction and if the magnitude of x is less than y then the fraction is proper.
Otherwise, if magnitude of x is greater than y then the fraction is improper fraction.

Thus, 1/2 , 5/2, 1/3, 1/7 etc. are all fractions out of which 5/2 is only improper fraction. But 1/3 and
1/7 do not provide exact values since divisions will never terminate. These are called periodic or
recurring fractions.
In mathematics value of 1/3 is written as 0.3’, which represents 0.333333333 … α and value of
1/7 is written as 0.1428571’, which represents 0.14285711428571… α .

iii) Irrational Numbers: These are the numbers obtained from non-rational roots of algebraic equations
such as √2, √3, √5 etc. These numbers cannot be expressed either as terminating decimal or a
periodic decimal. These are represented geometrically by points, namely the gaps between rational
points on a line.

1.5 Representation of Numbers in Computer

Almost all Compilers / Interpreters written for high level languages e.g. FORTRAN, C, COBOL, PASCAL
etc. allow data to be declared in following types:

A. Numerical

(i) Integer: generally two bytes i.e. 16 bits are considered to represent both negative and positive
integers. With 16 bits or two bytes decimal integers, numbers from -32768 to +32767 only can be represented.
Where for representation of –ve numbers 2’s complement notation is used. Since in 2’s complement notation
-32768 is 1000 0000
0000 0000 & +32767 is 0111 1111 1111 1111. However, in case of only positive numbers the range of decimal
numbers 0 – 65535 is represented.
Therefore it is observed that a very small decimal value can be represented by 16 bits or two bytes of data.
But in our environment often we need large values to represent physical quantities such as net asset value of a
small company ≈ 10,00,000,000/- (Rs.10 Crore), population of any country 100,00,000,000 ( 100 Crore) etc.
Thus further grouping of bytes are required to represent large numbers. The simple integers are called primitive
data types.
Almost all high level languages provide additional data types to accommodate large values in computers. For
example, when 4 bytes are grouped, the range of decimal values -2,147,483,648 to 2,147,483,647 are
represented . In general the following formula is used to establish relationships between binary numbers and the
equivalent decimal integers.

-2n-1 +1 ≤ N ≤ 2n-1 -1 , Where n = no. of bits, N= decimal integers.

Negative numbers are represented by 2’s complement notation.

In C- language the following


data Type Range Bytes Represents types are used to
int -32768 - 32767 2 Whole numbers represent integers.
Unsigned int 0 - 65535 2 -do-
long -2,147,438,648 – 2,147,438,647 4 -do-
Unsigned long 0 - 429,496,7295 4 -do-

In high level languages there is another data type available to represent fractional decimal numbers, which is
called float in ‘C’ Language and Real in FORTRAN.
But in ‘C’ language to accommodate more decimal digits double and long double types are also used, which is
described below:

Type Range Bytes Represents


From To
Float 3.4 x 10-38 - 3.4 x 10-38 4 Fractional numbers
double 1.7 x 10-308 1.7 x 10-308 8 -do-
long double 3.4 x 10-4932 3.4 x 10-4932 10 -do-

B.Character

Single characters e.g. letters, digits, punctuation symbols etc. are represented by character data type in
‘C’ language the keyword ‘char’ is used to declare character data type. Either ASCII or EBCDIC format
is used for depicting a character and one byte of space occupied in memory for storing the same. The
ASCII format has been used in ‘C’ language to describe character data type and if anybody wants to
write ‘a’ in computer, actually the binary sequence 10010111, equivalent to ASCII value ‘97’ is stored
in memory.

1.6 Group Data or Structured Data

The most rigorous usage of computers happen in the areas of banking, insurance, health, university etc. where
data are generally represented in the form of record, which represents a group of molecular or elemental
information. As for example a record of a registered student of any university looks like:
103000532 Manmohan Sing B.Tech CSE 2007

Where the first element represents Registration number, the second element is name, the third one is the course
of study, fourth one is the subject and the last one is the year of registration. All the elements together comprise
a record of a registered student and carry meaningful information. But no single (primitive) data types available
in any HLL to represent the entire record in computer. However, all third generation languages have provision
of declaration of record to support different data types. But do not allow populate data in file with different
structues, e.g. COBOL language allows three kinds of file structure like Sequential, Random and Index
Sequential. On the contrary the 'C' Language does not allow Random and Index Sequential file structure in
general to populate data, special programs are required for these files.

1.7 Algorithm and Notations Used in The Book

We have used 'C' Language and it's notation in this book for writing algorithms, but the full syntaxes of the
language have not been considered. Thus, one can convert algorithms to equivalent programs very easily just by
maintaing syntaxes only. Moreover, very simple conventions have been followed to name variables or
identifiers in our program e.g. if the variables start with i it represents integers data type etc. ----------------------
write for others.

The book has been divided into --- nos of chapters, where in chapter-1, following topis are covered ---------

we have provided questions and solutions of these, where questions are collected from different Universities to
provide guide line for preparation of different examinations.

1.8 MCQ Chapter 1

1. The most widely used method for interpreting bit setting as non-negative integer is
(a)Binary number system (b)BCD system (c)ASCII system (d)None of the above

2. Any string of bits of length n represents a unique non-negative integer between


(a) 0 and 2n-1-1 (b) 0 and 2n-1 (c) 1 and 2n-1 (d) None of the above

3. With 1' s complement method, the range of the numbers that can be represented using n bits
is :
(a) a 1 followed by using n - 1 zeroes to a 0 followed by n - 1 ones
(b) a 0 followed by using n - 1 ones to a 1 followed by n - 1 zeroes
(c) 2n-1-1 to 2n-1-1
(d) None of the above.

4. Using 2's complement method, the range of the numbers that can be represented using n bits
is :
(a) a 1 followed by using n - 1 zeroes to a 0 followed by n - 1 ones
(b) a 0 followed by using n - 1 ones to a 1 followed by n - 1 zeroes
(c) 2n-1-1 to 2n-1-1
(d) None of the above.
5. Key concept for representing a real number is a mantissa times
(a) 10 raised to an exponent (b) 2 raised to an exponent
(c) base raised to an exponent (d) None of the above.

6. The set of native data types that a particular computer can support is determined by :
(a) type of hardware company
(b) what functions have been wired into hardware
(c) what software support is required
(d) None of these.

7. While considering data structure implementation, the factor(s) under consideration is (are) :
(a) time (b) time and space
(c) time, space and processor (d) None of these.

8. Which is not true ?


(a) Abstract data type is the useful tool for specifying the logical properties of the data type
(b) While defining an abstract data type as a mathematical concept, the space and efficiency
is not of major concern
(c) Every abstract data type can be implemented using any programming language
(d) None of these.

9. The number of 1s in the binary representation of 3 * 4096 + IS * 256 + 5 * 16 + 3 are


(a) 8 (b) 9 (GATE-1995)
(c) 10 (d) 12.

10. Consider the following floating point representation (GATE- 1996)


31 24 23 0
Exponent Mantissa

The exponent is in 2' s complement representation and mantissa is in the sign magnitude representation.
The range of the magnitude of .the normalized numbers in this representation is
(a) 0 to 1 (b) 0.5 to 1
-23
(c) 2 to 0.5 (d) 0.5 to (1-2-23).

11. The octal representation of an integer is 3428. If this were to be treated as an eight bit integer,
its decimal equivalent is (GATE-1998)
(a) 226 (b) -98
(c) 76 (d) -30

12. Zero has two representations in: (GATE-1999)


(a) Sign magnitude (b) 2's complement
(c) All of the above (d) None of the above.

13. Consider the values A = 2.0 x 1030, B = - 2.0 x 1030, C = 1.0, and the sequence (GATE-2000)
X := A + B
Y := A + C
X := X + C
Y:= Y + B
executed on a computer where floating point numbers are represented with 32 bits. The values
for X and Y will be

(a) X = 1.0, Y = 1.0 (b) X = 1.0, Y = 0.0 (c) X = 0.0, Y = 1.0 (d) X = 0.0, Y = 0.0

14. The number 43 in 2' s complement representation is


(a) 01010101 (b) 11010101 (d) 10101011 (c) 00101011

15. Which of the following is (are) example(s) of data abstraction?


(a) List of student information
(b) File of employee records
(c) Bank account database
(d) All of the above.

16. Data structure means


(a) The separation of the representation of data from the applications that use the data at a
logical level
(b)the logical picture of a data type, plus the specifications of the operations required to
create and manipulate objects of this data type
(c)a collection of data elements whose organization is characterized by accessing
operations that are used to store and retrieve the individual data elements
(d) None of these.

17. Abstract Data Type means that


(a) The separation of the representation of data from the applications that use the data at a logical level
(b) The logical picture of a data type, plus the specifications of the operations required to create and
manipulate objects of this data type
(c) A collection of data elements whose organization is characterized by accessing operations that are
used to store and retrieve the individual data elements
(d) None of these.
18. Why is writing easily modifiable code important?
a) Easily modifiable code generally has a quicker run time.
b) Most real world programs require change at some time.
c) Most text editors make it easy to modify code.
d) Several people may be writing the same function at the same time.
19. Which phase of the software life-cycle is usually the most expensive?
a) Analysis and specification of the task
b) Design
c) Implementation
d) Maintenance and evolution
20. What will happen if a function is executed and the precondition for the function is not met?
a) An error message will be printed.
b) The program will loop indefinitely.
c) The system will crash.
d) Any of the above results could happen.
21. If the precondition fails, it is a good idea to write a useful error message and then halt the program. Why
is the program halted?
a) Most operating systems forbid continuation.
b) The function is no longer guaranteed to make the postcondition true.
c) The function's memory requires have become exponential (or worse).
d) The function's running time has become exponential (or worse).

22. Which of these is used to stop the program execution when a precondition is not met.
a) assert();
b) exit();
c) return();
d) void();
23. Which of these statements will always cause a program to halt? (x is an int variable).
a) assert(10 > 0);
b) assert(10 < 0);
c) assert(x < 0);
d) None of the above will always cause a program to halt.
24. What does a run-time analysis usually count?
a) The number of arithmetic and other operations required for the program to run
b) The number of megabytes required for the program to run
c) The number of seconds required for the program to run
d) The number of seconds plus the number of megabytes
e) The number of seconds times the number of megabytes
25. Why is it important to test boundary values when testing programs?
a) Calculating by hand, it's easy to find the right answers for boundary values.
b) Debuggers are easier to use when testing boundary values.
c) In practice, a large proportion of errors arise from boundary values.
d) The correct execution of a function on all boundary values proves a function is correct.
26. How may boundary values for a function be found?
a) Pick values that are one step away from different behavior.
b) Pick values that make the precondition equal to the postcondition.
c) Pick values where the precondition is false.
d) Pick values where the postcondition is false.
27. Which software tool will best help you determine whether your test cases are fully exercising your code?
a) Compiler
b) Debugger
c) Make
d) Pine
e) Profiler

28. A text is made up of the characters a, b, c , d, e each occurring with the probability .12, .4, .15, .08 and .25
respectively. The optimal coding technique will have the average length of
(a) 2.15 (b) 3.01 (c) 2.3 (d) 1.78

29. In the previous question, which of the following characters will have codes of length 3?
(a) Only c (b) Only b (c) band c (d) Only d

30. Which of the following abstract data types can be used to represent a many to many relation?
(a) Tree (b) Plex (c) Graph (d) Queue

31. The principle of locality justifies the use of


(a) interrupts (b) DMA (c) polling (d) cache memory

32. Unrestricted use of goto is harmful, because it


(a) makes debugging difficult
(b) increases the running time of programs
(c) increases memory requirement 9f programs
(d) results in the compiler generating longer machine code

33. The main() function is always


(a)a called function
(b) a calling function
(c) recursive function
(d) used at the end of the program
(e) None of these.
34. The first digit of a decimal constant must be
(a) zero
(b) a non-zero number
(e) a negative number
(d) an integer
(e) None of these.
35. Floating point numbers are used instead of integers to
(a) permit the use of decimal points in numbers
(b) avoid being too specific about what value a number has
(c) conceal the true value of the numbers
(d) All of the above
(e) None of these.

36. An expression
(a) is a collection of data objects and operators that can be evaluated to a single value
(b) is a name that substitutes for a sequence of characters
(c) causes the computer to carry out some action
(d) All of the above
(e) None of these.

37. Consider the following arithmetic expression


2 * x / (3 * y)
Suppose x and y are floating-point variables that have been assigned the values x = 8.8 and y = 3.5.
What would be the value of the expression?
(a)1.61373 (b) 20.53333 (c)1.67619 (d) None of these (e) 2.51429

38. The statement (i = (j = 4) + (k = 9):


(a) assigns a value 13 to i
(b) assigns a value 4 to i
(c) gives an error message
(d) assigns a value 7 to i
(e) None of these.

39. If p and q are assigned the values 2 and 3 respectively then the statement
p = q++
(a) gives an error message
(b) assigns a value 4 to p
(c) assigns a value 3 to p
(d) assigns a value 5 to p
(e) None of these.
40. If the variables i,j and k are assigned the values 5, 3 and 2 respectively, then the expression i=j+(k++=6)+7):
(a) gives an error message
(b) assigns a value 16 to i
(c) assigns'a value 18 to i
(d) assigns a value 19 to i
(e) None of these.

41. Which of the following is not a programming control structure?


(a) Repetition (b) selection (c) Sequency (d) Sorting.

42. External documentation includes


(a) a printout of the program's code
(b) flowcharts
(c) IPO charts
(d) pseudocode
(e) All of the above.
43. Errors in a program are called
(a) accidents (b) annoyances (c) bugs(d) mistakes (e) typing errors.

44. Typing the instruction “grosspay = hoursWorked – hourlypay” is an example of


(a) an entry error
(b) a function error
(c) a logic error
(d) a syntax error.

45. The step-by-step instructions that solve a problem are called


(a) an algorithm
(b) a list
(c) a plan
(d) a sequential structure.

46. The set of instructions for how to tie a bow is an example of the structure
(a) control (b) repetition (c) selection (d) sequence (e) switching.

47. Which of the following control structures is used in every program?


(a) Repetition (b) Selection
(c) Sequence (d) Switching.

48. The instruction "If it's raining outside, then take an umbrella to work" is an example of the structure
(a) control
(b) Repetation
(c) selection
(e) switching.

49. The recipe instruction "Beat until smooth" is an example of the structure
(a) control (b) repetition
(c) selection (d) sequence
(e) switching.

50. Sending a copy of data to a program module is called


(a) passing a value
(b) making a reference
(c) recursion
(d) setting a condition.
51. Paying attention to the important properties while ignoring inessential details is known as.
(a) selectiveness
(b) polymorphism
(c) abstraction
(d) summarizing.

52. A program that predicts the exact sequence in which events will take place is said to be
(a) compiled
(b) interpreted
(c) procedural
(d) object-oriented.

53. Using a statement at the wrong time or with an inappropriate object creates a
(a) logical error
(b) syntax error
(c) compiler error
(d) language error.

54. A translator that notes whether you have used a language correctly may be called a
(a) theasurus (c) coder
(b) compiler (d) decoder.

Solutions:
1. a 2. d 3. a 4. a 5. c 6. b 7. c 8. c 9. c 10. d 11. d 12. a 13. d 14. c 15.d
16. c 17. b 18. b 19. d 20. d 21. 22. b 23. d 24. a 25. d 26. 27. b 28. a 29. a 30. b/c
31. d 32. a 33. b 34. b 35. b 36. a 37. b 38. a 39. c 40. a 41. c 42. a 43. c 44. c 45. a
46. d 47. c 48. c 49. b 50. a 51. d 52. c 53. a 54. b

1.10 Exercise Chapter 1


1. Describe one good method for precisely specifying what a function must do, without indicating how
the function accomplishes its work. Provide an example, using a small function.
2. What is a precondition? What is a post-condition?
3. It's recommended that the precondition be checked at the start of a function. What's a good approach
if a function discovers that its precondition is not true?
4. Is it always possible for a function to check that its precondition is true?
5. Suppose that you accidentally call a correctly implemented function, but the precondition is false. Is
the function guaranteed to halt with a nice message? Is the function allowed to erase everything on
your hard drive?
6. Write the first few lines of this function so that it uses the assert facility to check its precondition:

void exam(int i)
// Precondition: i is not equal to 42.
...
7. Give a concise formula that gives the approximate number of digits in a positive integer. The integer
is written in base 10.
8. Why is the order of an algorithm generally more important than the speed of the processor?
9. With about three or four sentences, explain the basic features of your debugger and how they help
you find bugs.
Chapter - 2
PRELIMINARIES
2.1 Definition of algorithm
An algorithm is a set of steps to solve a particular problem. When we write the different steps for the
preparation of a cup of tea or coffee then it is also an algorithm.
The word algorithm is a Persian term derived from the name of a Persian author and great
mathematician “Abu Abd Allah Jafar Mohammad ibn Musba al Khowarizmi”. He was born on 780 A.D. in
Baghdad. He worked on algebra, geometry and astronomy.

2.2 Properties of an Algorithm


Properties of an algorithm include the following criteria:
1) Input: An algorithm should have some inputs.
Example: If we want to write an algorithm to check a given number is odd or even, we can take the number
as its input.
2) Output: At least one output should be returned by the algorithm after the completion of the specific task
based on the input(s) given.
Example: If we want to write an algorithm to check a given number is odd or even, we can return 0
indicating that the number is odd and 1 indicating the number is even.
3) Definiteness: Every statement of the algorithm should be clear and unambiguous.
Example: If we write a statement like “iResult = iNumber % x or y”, then this is not clear that what
operation should be done. That statement should be either “iResult = iNumber % x” or “iResult = iNumber
% y”.
4) Finiteness: No infinite loop should be allowed in an algorithm.
Example: while(1<2)
{
iNumber = iNumber/2;
}
This type of loop should not be allowed in the algorithm as it leads an infinite loop (because 1 is always less
than 2).
5) Effectiveness: Writing an algorithm is a priori process of actual implementation of the algorithm. So, a
person should do analysis of algorithm in finite amount of time with pen and paper to judge the performance for
giving the final version of the algorithm.

2.3 Algorithm Development Life Cycle


The life cycles of an algorithm includes the following phases:
1) Design Phase: Some of the techniques used in the design phase of an algorithm are
(i) Brute-Force Method
(ii) Divide-Conquer Method
(iii) Greedy Method
(iv) Dynamic Programming
(v) Backtracking

2) Writing Phase: Basically algorithm is written in a modular approach. Actually a function written to solve a
particular problem is itself an algorithm. To make clear each and every step in the algorithm, write comments
wherever necessary.
3) Testing Phase: After writing an algorithm, it is necessary to check that the algorithm gives correct result for
every valid input.

4) Analyzing Phase: Suppose for a problem P, ten correct algorithms are designed. Now which one is to be
chosen that depends on the performance of them. Mainly this performance is judged based on how much time
and space the algorithm takes. That is after testing phase of an algorithm it is required to analyze the time
complexity and space complexity of it. In section 2.5 this is discussed in details.

2.4 Example of some Algorithms


Example 1: Write an algorithm to find the sum of two integer numbers.
Inputs : Two numbers.
Formula: sum of two numbers is sum = number1 + number2; input two numbers in number1 & number2
variables and add these two by addition operation and put the result in third variable sum.
Output : Sum of these two input numbers.

1. Algorithm fnSum(iNumber1,iNumber2)
2. // Purpose : This algorithm finds the summation of two numbers.
3. // Inputs : Two numbers are iNumber1 and iNumber2.
4. // Output : iSum = Sum of iNumber1 and iNumber2.; the variables are labled with ‘i’ as prefixed
because //integer numbers are considered.
5. {
6. iSum = iNumber1 + iNumber2; //Find the summation.
7. return(iSum); //Return the value.
8. }//End of Algorithm

Example 2: Write an algorithm to find the roots of a quadratic equation Ax2 + Bx + C = 0.


Inputs: Values of the coefficients A, B and C.
Formula: X = (-B + √(B^2 – 4*A*C)) / 2 * A and X = (-B - √(B^2 – 4*A*C)) / 2 * A
Output: Roots for real cases and for imaginary roots, output is “ Imaginary Root”

1. Algorithm fnQuadraticEquation(fA,fB,fC)
2. // Purpose : This algorithms computes the roots of a quadratic equation.
3. // Input : fA,fB and fC are the variables to hold values of the coefficients A, B and C respectively.
4. // fA, fB and fC are real or float variables, and to represent float variables ‘f’ is used as prefix.
5. // Output : Roots for real cases for imaginary roots, output is “ Imaginary Root”
6. {
7. fY = fB2 – 4*fA*fC;
8. if(fY>0) //If two different roots exist.
9. {
10. fX1 = (-fB + sqrt(fY))/2*fA; //Compute the first root.
11. fX2 = (-fB – sqrt(fY))/2*fA; //Compute the second root.
12. print(fX1,fX2); //Print the calculated values.
13. }
14. elseif(fY=0) //If only one root exists.
15. {
16. fX=-fB/2*fA; //Compute the only root.
17. print(fX); //Print the calculated value of the only root.
18. }
19. else //If no real solution exists.
20. print(Imaginary Root);
21. }//End of Algorithm
Example 3: Write an algorithm to find the largest element from a non-empty array.
Inputs: Total number of element and the array.
Formula: The logic; assume initially first element is the largest one. Check rest n-1 elements of the array with
the first one one by one and if any one is found larger the first update the first the current larger and so
Output: The largest element.

1. Algorithm fnFindLargestElement(iN, arrData[])


2. // Purpose : This algorithms find the largest element from a non-empty array.
3. // Input : iN is total number of elements and arrData[] is the array .
4. // Output : iLargest which contains the largest element.
5. {
6. iLargest = arrData[0]; //Assume initially first element is the largest one.
7. for (iCounter = 1;iCounter<n;iCounter++) //Check rest n-1 elements of the array.
8. if(iLargest<arrData[iCounter])
9. iLargest = arrData[iCounter];
10. return(iLargest); //Return the value of iLargest.
11. }//End of Algorithm

Example 4: Write an algorithm to find the factorial value of a given element.


Inputs: The given element.
Formula: N! = 1.2.3…N
Output: The calculated factorial value.

1. Algorithm fnFactorial(iNumber)
2. // Purpose : This algorithm finds the factorial value of a given number.
3. // Input : iNumber is the required number.
4. // Output : iFactorial which is factorial of iNumber.
5. {
6. iFactorial = 1;
7. for(iCounter = 2;iCounter<=iNumber;iCounter++)
8. iFactorial = iFactorial*iCounter;
9. return(iFactorial);
10. }//End of Algorithm

2.5 Complexity of Algorithms(Analyzing phase):


Generally the complexity of an algorithm is measured in two phases. When one measures the complexity of an
algorithm by pen and paper, he/she can only predict the complexity which gives an idea of how much time or
space this algorithm takes to finish its execution. This phase is called the priory analysis. After implementing
the algorithm in computer, we get the actual time and space. This phase of analyzing the algorithm is called the
posteriori analysis. Complexity of an algorithm can be of two types:
(1) Time complexity: The analysis of algorithm for the prediction of computation time for execution of each
and every instruction in the algorithm is called the time complexity of the algorithm.
(2) Space Complexity: The analysis of algorithm for prediction of memory requirement to run the algorithm is
called the space complexity of the algorithm.

2.5.1 Asymptotic notations


There are some notations to determine the complexity of an algorithm in priory analysis. The term Asymptote
means a line whose distance to a given curve is tends to zero. An asymptote may or may not intersect its
associated curve. The notations are as follows:
Big Oh (O) notation: The function f(n) = O(g(n)) (read as f of n is big Oh of g of n) iff f(n) ≤ c*g(n) for all n, n
≥ n0 where c and n0 are positive constants. The function g(n) indicates the upper bound of f(n) and so g(n)
should be as small as possible for which the statement f(n) = O(g(n)) is true.

Example: Suppose f(n) = 2n3 + 3n2 + n + 10


Now we can write f(n) = 2n3 + 3n2 + n + 10
≤ 5n3 for all n ≥ 2
i.e. here c = 5,n0 = 2 and g(n) = n3.

Hence we can write f(n) = O(g(n)) = O(n3)

See here the statements f(n) = O(n4) or f(n) = O(n5) and so on are also true. But we should not write so because
the values of g(n) for the statements are not as small as possible for which the statement f(n) = Ω(g(n)) is true.

c*g(n)

f(n)
Time
i

n0

n (Problem Size)

Figure 2.1: Upper Bound of an algorithm (Big Oh (O) notation)

Omega (Ω) notation: The function f(n) = Ω(g(n)) (read as f of n is omega of g of n) iff f(n) ≥ c*g(n) for all n, n
≥ n0 where c and n0 are positive constants. The function g(n) is only a lower bound of f(n) and so g(n) should be
as large as possible for which the statement f(n) = Ω(g(n)) is true.

Example: Suppose f(n) = 2n3 + 3n2 + n + 10


Now we can write f(n) = 2n3 + 3n2 + n + 10
≥ 2n3 for all n ≥ 1
3
i.e. here c = 2,n0 = 1 and g(n) = n .

Hence we can write f(n) = Ω(g(n)) = Ω(n3)

Observe that, for the above example the statements f(n) = Ω(n 2) or f(n) = Ω(n) or f(n) = Ω(1) are also correct.
But we should not write so because the values of g(n) for the statements are not as large as possible for which
the statement f(n) = Ω(g(n)) is true.
f(n)

c*g(n)

Time

n0

n(Problem Size)

Figure 2.2: Lower Bound of an algorithm (Omega (Ω) notation)

Theta (Ө) notation: The function f(n) = Ө(g(n)) (read as f of n is theta of g of n) iff c 1*g(n) ≤ f(n) ≤ c2*g(n) for
all n, n ≥ n0 where c1,c2 and n0 are positive constants. The function g(n) is a tight bound of f(n) because g(n) is
both upper and lower bound on f(n).

Example: Suppose f(n) = 2n3 + 3n2 + n + 10


Now we can write, 5n ≤ 2n3 + 3n2 + n + 10 ≤ 2n3
3
for all n ≥ 2
i.e. here c1 = 5, c2 = 2, n0 = 2 and g(n) = n3.
Hence we can write f(n) = Ө(g(n)) = Ө(n3)

c1*g(n)

f(n)

c2*g(n)

Time

n0

n (Problem Size)

Figure 2.3: Tight bound of an algorithm (Theta (Ө) notation)

Little oh (o) notation: The function f(n) =o(g(n)) (read as f of n is little oh of g of n) iff

f(n)
lim ─── =0
n→∞ g(n)
Example: Suppose f(n) = 2n3 + 3n2 + n + 10
Now we can write limn→∞ (2n3 + 3n2 + n + 10)/n4
= limn→∞ (2/n + 3/n2 + 1/n3 + 10/n4)
=0
i.e. here g(n) = n4.
Hence we can write f(n) = o(g(n)) = o(n4)

Little omega (ω) notation: The function f(n) = ω(g(n)) (read as f of n is little omega of g of n) iff
g(n)
lim ─── =0
n→∞ f(n)
Example: Suppose f(n) = 2n3 + 3n2
Now we can write limn→∞ n/(2n3 + 3n2)
= limn→∞ (1/2n2 + 1/3n)
=0
i.e. here g(n) = n.
Hence we can write f(n) = ω(g(n)) = ω(n)

During the execution of a program, estimating the upper bound of time (CPU time) that the program takes to
finish its execution is the main factor. If it takes the less time than our estimation then the CPU performance
will be improved. But if it takes more time than our calculation then it will cause the degradation of CPU
performance. Now we know well that the Big Oh (O) notation gives the upper bound estimation and that’s why
we will discuss about big Oh (O) notation in details through out the book.

2.5.2 How can we estimate the time complexity in big Oh(O) notation?
Assumptions:
1) All the statements take equal time for execution.
2) Each statement takes unit time to execute.
Example 1: Calculate the time complexity for the following algorithm.
1. Algorithm fnSearch(arrData[],iN,iItem)
2. // Purpose : This algorithm searches a particular element in an array.
3. // Input : arrData[] is an array of iN(= n) integers. Search for the element iItem in the array.
4. // Output : The algorithm returns 1 for successful search and 0 for an unsuccessful search.
5. {
6. for(iCounter = 0;iCounter<iN;iCounter++)
7. if(arrData[iCounter]==iItem)
8. return 1; //Successful search.
9. return 0; //Unsuccessful search.
10. }//End of Algorithm
Solution: Line 7 and line 8 take unit time to execute and line 6 takes n time to execute. So lines 6, 7 and 8 take
2n time for execution.
Line 9 takes unit time to execute.
Therefore, f(n) = TL6 * (TL7 + TL8) + TL9
= n(1+1) + 1
= 2n + 1 ≤ 3n for all n ≥ 1
Now from the definition of big Oh(O) notation we can write-
f(n) = O(n)

Example 2: Comment on the time complexity of the following algorithm.


1. Algorithm fnMatrixMultiplication(arrA,arrB,iN)
2. // arrA and arrB are two matrix each of the order nXn. Value of the variable iN = n.
3. {
4. for(iX=0;iX<iN;iX++)
5. {
6. for(iY=0;iY<iN;iY++)
7. {
8. arrC[iX][iY]=0; //arrC is the resultant matrix
9. for(iZ=0;iZ<iN;iZ++)
10. arrC[iX][iY] = arrC[iX][iY] + arrA[iX][iZ]*arrB[iZ][iY];
11. }
12. }
13. //Print the contents of the resultant matrix.
14. for(iX=0;iX<iN;iX++
15. for(iY=0;iY<iN;iY++)
16. print(arrC[iX][iY]);
17. }//End of Algorithm
Solution: For calculating the time complexity of the above algorithm we can divide it into two segments.
Segment 1 is a nested for loop containing line 4 to line12 and segment 2 is a nested for loop containing line14 to
line 16.
In segment 1:
Line 10 takes unit time for execution.
Line 9 takes n times for execution.
Line 8 takes unit time for execution.
Line 6 takes n times for execution.
Line 4 takes n times for execution.
Now segment 1 takes T1 = TL4 * (TL6 * (TL8 + TL9 * TL10))
= n*(n*(1+ n*1)
2 3
=n +n
In Segment 2:
Line 16 takes unit time to execute.
Line 15 takes n times to execute.
Line 14 takes n times to execute.
So segment 2 takes T2 = TL14 * TL15 * TL16
=n*n*1
Therefore the total time f(n) = T1 + T2
2 3 2
= (n + n ) + n
3 2 3
= n + 2n ≤ 3n for all n ≥ 1
3
Now from the definition of the big Oh (O) notation we can conclude f(n) = O(n ).

Example 3: Calculate the time complexity for the following recurrence relation.
T(n) = 2T(n-1) + 1 for n > 1
=1 for n = 1
Solution: T(n) = 2T(n-1) + 1
= 2[2T(n-2) + 1] + 1
2
= 2 T(n-2) + 2 + 1
= ………………….
= ………………….
k
= 2 T(n-k) + …………. + 2 + 1 [up to k terms]
n-1 1 0
=2 + ………………..+ 2 + 2 [Let n-k = 1. So T(1) = 1]
n
= (2 – 1)/(2-1)
n n
=2 –1≤2 for all n ≥ 1
n
Therefore, from the definition of the big Oh (O) notation we can conclude f(n) = O(2 ).

Example 4: Calculate the time complexity for the following recurrence relation.
T(n) = 2T(n/2) + n for n > 1
=1 for n = 1
Solution: T(n) = 2T(n/2) + n
2
= 2[2T(n/2 ) + n/2] + n
2 2
= 2 T(n/2 ) + 2n
= ……………………………
= ……………………………
k k
= 2 T(n/2 ) + kn [up to k terms]
k
= nT(1) + nlogn [ Let 2 = n. So, k = logn]
= n + nlogn [ as T(1) = 1]
≤ 2nlogn for all n ≥ 2
Hence, from the definition of the big Oh (O) notation we can write f(n) = O(nlogn).

2.5.3 How can we improve the complexity of algorithms?


Example 1: Write an algorithm to calculate the sum of first n natural numbers.
Procedure 1:
Inputs: iNumber which contains the value of n
Output: iSum which contains the calculated sum
1. Algorithm Summation(iNumber)
2. {
3. iSum = 1;
4. for(iCounter = 2;iCounter≤iNumber;iCounter++)
5. iSum = iSum + iCounter;
6. return(iSum);
7. }//End of Algorithm
Complexity: Line 3 takes unit time for execution.
Line 4 takes n times for execution.
Line 5 takes unit time for execution.
Line 6 takes unit time for execution.
Therefore f(n) = TL3 + TL4*TL5 + TL6
= 1 + n*1 + 1
= n + 2 ≤ 2n for all n ≥ 2
So, f(n) = O(n)

Procedure 2:
The problem is S = 1+2+3+4+………. + n
= n(n+1)/2
Now we can directly compute the value of S without using any loop.

Inputs: iNumber which contains the value of n


Output: iSum which contains the calculated sum
1. Algo Summation(iNumber)
2. {
3. iSum = iNumber*(iNumber + 1)/2;
4. return(iSum);
5. }//End of Algo
Complexity: Line 3 takes unit time.
Line 4 takes unit time.
Therefore, f(n) = TL3 + TL4
=2
So, f(n) = O(1) i.e. the algorithm of procedure 2 takes constant time and obviously the second
approach is better than the first one in respect of time complexity.

n
Example 2: Write an algorithm to compute the value of x where n is integral power of 2.
Procedure 1:
Inputs: iX = the value of x and iN = the value of n.
Output: iResult = the calculated result.
1. Algorithm Power(iX,iN)
2. {
3. iResult = 1;
4. for(iCounter = 1;iCounter≤iN;iCounter++)
5. iResult = iResult*iX;
6. return(iResult);
7. }//End of Algo
Complexity: Line 3 takes unit time for execution.
Line 4 takes n times for execution.
Line 5 takes unit time for execution.
Line 6 takes unit time for execution.
Therefore, f(n) = TL3 + TL4*TL5 + TL6
= 1 + n*1 + 1
= n + 2 ≤ 2n for all n ≥ 2
So, f(n) = O(n)
Procedure 2:
Suppose n=8.
We can calculate the value I following way:
iResult = x
2
iResult = iResult * iResult = x
4
iResult = iResult * iResult = x
8
iResult = iResult * iResult = x
i.e. after logn time we are getting the desired result.

Inputs: iX = the value of x and iN = the value of n.


Output: iResult = the calculated result.
1. Algo Power(iX,iN)
2. {
3. iResult = x;
4. for(iCounter = 1;iCounter≤log(iN);iCounter++)
5. iResult = iResult*iResult;
6. return(iResult);
7. }//End of Algo
Complexity: Line 3 takes unit time for execution.
Line 4 takes logn times for execution.
Line 5 takes unit time for execution.
Line 6 takes unit time for execution.
Therefore, f(n) = TL3 + TL4*TL5 + TL6
= 1 + (logn)*1 + 1
= logn + 2 ≤ 4logn for all n ≥2
So, f(n) = O(logn). Hence we are getting better time complexity in second approach.

In this section we examine the asymptotic behavior of polynomials in n. In particular, we will see that as n gets
large, the term involving the highest power of n will dominate all the others. Therefore, the asymptotic behavior
is determined by that term.

Theorem:  Consider a polynomial in n of the form

where . Then .

Proof: Each of the terms in the summation is of the form . Since n is non-negative, a particular term will be
negative only if . Hence, for each term in the summation, . Recall too that we have
stipulated that the coefficient of the largest power of n is positive, i.e., .

Note that for integers , for . Thus

Now the constants and , such that for all , . Thus,


.

This property of the asymptotic behavior of polynomials is used extensively. In fact, whenever we have a
function, which is a polynomial in n, we will
immediately ``drop'' the less significant terms (i.e., terms involving powers of n which are less than m), as well
as the leading coefficient, , to write .
2.5.4 Properties of Big Oh
In this section we examine some of the mathematical properties of big oh. In particular, suppose we know that
f1(n) = O(g1(n)) and f2(n) = O(g2(n)).

Theorem 1: If f1(n) = O(g1(n)) and f2(n) = O(g2(n)), then f1(n) + f2(n) = O(max(g1(n), g2(n))).
Proof: From the definition of big O notation, there exist four positive constants n1, n2, c1 and c2 such that
f1(n) ≤ c1*g1(n) for n ≥ n1 and f2(n) ≤ c2*g2(n) for n ≥ n2
Let n0 = max(n1,n2) and c0 = 2*max(c1,c2).

Now, f1(n) + f2(n) ≤ c1*g1(n) + c2*g2(n) for n ≥ n0


≤ c0*(g1(n) + g2(n))/2
≤ c0*max(g1(n), g2(n))

Thus, f1(n) + f2(n) = O(max(g1(n),g2(n)))

Theorem 2:  If f1(n) = O(g1(n)) and f2(n) = O(g2(n)), then f1(n)*f2(n) = O(g1(n)*g2(n)).
Solution: By Definition, there exist four positive constants, n1,n2, c1 and c2 such that f1(n) ≤ c1*g1(n) for all n
≥ n1 and f2(n) ≤ c2*g2(n) for all n ≥ n2.
Let n0 = max (n1, n2) and c0 = c1*c2. Consider the product f1(n)*f2(n) for n ≥ n0.
Now, f1(n) + f2(n) ≤ (c1*g1(n)) * (c2*g2(n)) for n ≥ n0
≤ c0*(g1(n)*g2(n))
Thus, f1(n)*f2(n) = O(g1(n)*g2(n)).

Theorem 3:  If f1(n) = O(g1(n)) and g2(n) is a function whose value is non-negative for integers n≥0, then
f1(n)*f2(n) = O(g1(n)*g2(n)).
Solution: By definition, there exist two positive constants n1 and c1 such that f1(n)≤c1*g1(n) for all n≥n1.
Since g2(n) is never negative,
f1(n)*g2(n) ≤ c1*g1(n)*g2(n) for all n ≥ n1
Thus, f1(n)*f2(n) = O(g1(n)*g2(n))

(Transitive Property) Theorem 4:    If f(n)=O(g(n)) and g(n)=O(h(n)) then f(n)=O(h(n)).


Solution: By definition, there exist four positive constants, n1, n2, c1 and c2 such that f(n) ≤ c1*g(n) for all n ≥
n1 and g(n) ≤ c2*h(n) for all n ≥ n2.
Let n0 = max (n1, n2) and c0 = c1*c2.
Then, f(n) ≤ c1*g(n) for all n ≥ n1
≤ c1*(c2*h(n)) for all n ≥ n0
≤ c0*h(n)
Thus, f(n)=O(h(n)).

2.5.5 Rate of growth of Big O notation


Suppose there are two algorithms A and B. Now we say A is better than B in respect of time complexity if A
takes less time to execute than B. Comparison between algorithms is generally done by some standard functions
described in the following table.
O(1) Constant time
O(logn) Logarithmic time
O(n) Linear time
c Polynomial time
O(n )
n Exponential time
O(c )

and their orders are:


O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n)
Note that, time complexity O(1) of an algorithm does not mean that the algorithm takes unit time to execute
rather it takes some constant time.

2.6 Some popular Mathematical Notations and Functions


1. Floor function (└ ┘): This function takes the nearest lower integer value of a fractional number.
e.g. floor(3/2) = 1.ss
2. Ceiling function (┌ ┐): This function takes the nearest upper integer value of a fractional number.
e.g. ceiling(3/2) = 2.
3. Mod function (%): This function gives the remainder value of a division operation.
e.g. 12 mod 5 = 2.
4. a0 + a1 + a2 + ………….. + an = (an-1 -1)/(a-1).
5. 1 + 2 + 3 + ……………..+ n = n(n+1)/2
6. 12 + 22 + 32 + ………….+ n2 = n(n+1)(2n+1)/6
7. Logax = k(logbx) where k is some constant.

2.7 Algorithmic Notation


This section describes the format that is used to write an algorithm throughout the book. As C is most popular
language, we have followed most syntax of C.

2.7.1 Algorithm number:


Each algorithm is assigned a unique number to identify a particular algorithm. For example Algorithm 10.2
denotes second algorithm in chapter 10.

2.7.2 Line number:


In each algorithm we specify line numbers to analyze or describe it easily.

2.7.3 Comments:
The statement after double slash (//) denotes a comment .At the beginning of each algorithm we have specified
purpose of this algorithm, input(s) needed for this algorithm, output returned by this algorithm and some special
comments if necessary. Each line of an algorithm may contain some comments to specify its purpose.

2.7.4 Variable names:


The name of an integer variable starts with i, name of a float variable starts with f, name of a character variable
starts with c, name of a pointer variable starts with ptr, name of an array starts with arr and name of a function
or algorithm starts with fn. But in some algorithm we have not followed this syntax for simplicity.

2.7.4 Operator: We will use basically three types of operators


1. Assignment operator
2. Relational operator
3. Relational operator

2.7.4.1 Assignment operator: Our assignment operator is = like as C. For example iElement = arrData[3]
assigns the value of arrData[3] to iElement.

2.7.4.2 Relational operator: These operators are used to compare two operands to check whether they are
equal to each other or unequal or one is greater than the other. The following figure shows these operators along
with their works.
Operator Operation
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
== Equal to
!= Not equal to

2.7.4.3 Logical operator: Logical operators are ‘logical and’ denoted by ‘&&’, ‘logical or’ denoted by ‘||’ and
logical not denoted by ‘!’. Their truth tables are described below:

Logical and (&&) Logical or (||) Logical not(!)


Exp1 Exp2 Result Exp1 Exp2 Result Exp1 Result

0 0 0 0 0 0 0 1
0 1 0 0 1 1 1 0
1 0 0 1 0 1
1 1 1 1 1 1
2.7.5 Input and Output:
Data may be taken as input from the user by scan with the following form:
scan(Variable names);
Similarly to print some output print statement is used with the following form:
print(statement and/or name of variables);
2.7.6 Control structures:
Control statements are also similar to C language. Basically there are three types control structures:
1. Sequential:
2. Conditional
3. Repetitive

2.7.6.1 Sequential logic: In a sequential approach all the statements are executed in the same order as they are
written.

2.7.6.2 Conditional logic: In this approach, based on some condition, the different sets of statements are
executed. An ‘if’ statement is a conditional control structure that executes a set of statements based upon some
specified criteria.
7.6.1.1 Simple ‘if’ statement: The syntax is
if(conditions)
{
--------------
Statements
--------------
}
The logic of this structure is if the condition is true then the statements in the ‘if’ block are executed otherwise
the statements are skipped.
7.6.1.2 Simple ‘else’ statement: The syntax is
if(conditions)
{
--------------
Statement 1
--------------
}
else
{
--------------
Statement 2
--------------
}
If the condition of ‘if’ statement is true then statement 1 is executed otherwise statement 2 is executed.
7.6.1.3 Nested ‘if’ statement: This structure has the form:
if(condition 1)
{
--------------
Statement 1
--------------
}
else if(condition 2)
{
--------------
Statement 2
--------------
}
…………………………
…………………………
else if(condition n)
{
--------------
Statement n
--------------
}
else
{
--------------
Statement n+1
--------------
}
Here only one block is executed based on the specified criteria.

7.7.6.3 Repetitive logic:


Here different sets of statements are executed repeatedly for some time. Time of repetitions is called number of
iteration. For this purpose we will use three kinds of loops:
1. ‘for’ loop
2. ‘while’ loop
3. ‘do-while’ loop

2.7.6.3.1 ‘for’ loop: The syntax is:


for(initialize counters; test condition; increment counters)
{
-------------------
Statements;
-------------------
}
2.7.6.3.2 ‘while’ loop: The syntax is:
while(conditions)
{
-------------------
Statements;
-------------------
}

2.7.6.3.3 ‘do-while’ loop: The syntax is:


do
{
-------------------
Statements;
-------------------
} while(conditions);

2.8 MCQ Chapter – 2

1. Example(s) of O(1) algorithms is (are):


(a) printing a character to the screen
(b) incrementing a variable
(c) adding two numbers together
(d) All of the above.

2. Example(s) of O(N) algorithms is (are):


(a) initializing all of the elements in an one-dimensional array to zero
(b) incrementing all the elements in an one-dimensional array
(c) multiplying two numbers by performing successive addition operations
(d) All of the above.
3. Example(s) of O(N2) algorithms is (are)
(a) initializing all the elements in a two dimensional array to zero
(b) printing out all the elements in a two dimensional array
(c) searching for the smallest element in an unsorted two-dimensional array
(d) All of the above.

4. Three algorithms do the same task. Algorithm 1 is O(N2), Algorithm 2 is O(N), and Algorithm 3 is
O(Log2 N). Which algorithm should execute the fastest for large values of N?
(a) O(N) (b)O(N) (c)O(log2 N) (d) None of these.

5. Which of the following algorithm should execute the slowest for large values of N?
(a) O(N)
(b) O(N2)
(c) O(log2 N)
(d) None of these.

6. What should never be found in the top level of a top-down design?


(a) Details
(b) Coding
(c) Decisions
(d) None of these

7. Describe Process Program File in terms of Big-O, if N refers to the number of lines in the Program File
(a) O(N)
(b) O(1)
(c) O(log2 N)
(d) O(N2).

8. Describe Line Status of the Process Program- File in terms of Big-O, if N refers to the number of lines in the
Program File
(a) O(N)
(b) O(1)
(c) O(log2 N)
(d) O(N2).
9. Which of these is the correct big-O expression for 1+2+3+...+n?
a. O(log n)
b. O(n)
c. O(n log n)
d. O(n²)
10. Which of the following formulas in big-O notation best represent the expression n²+35n+6?
a. O(n³)
b. O(n²)
c. O(n)
d. O(42)
11. What term is used to describe an O(n) algorithm.
a. Constant
b. Linear
c. Logarithmic
d. Quadratic
12. Here is some code for an integer variable n:

while (n > 0)
{
n = n/10; // Use integer division

What is the worst-case time analysis for the above loop?


a. O(1)
b. O(log n)
c. O(n)
d. O(n²)
13. Express the formula (n - 2)*(n - 4) using big-O notation:
a. O(1)
b. O(n2)
c. O(log n)
d. O(n)
e. None of the above

14. The running time of an algorithm T(n), where 'n' is the input size is given by
T(n) = 8T(n/2) + qn, if n > 1
= p, if n = 1
where p, q are constants. The order of this algorithm is

(a) n2 (b) nn (c) n3 (d) n

15. Consider the following two functions.


f(n) = n3, if 0 :s; n < 10,000
n2, otherwise
g (n) = n, if 0 :s; n < 100
n2+5n, otherwise
Which of the following is/are true?
(a) f(n) is O(n3) (b) g(n) is O(n3)
(c) O(f(n)) is same as O(g(n)) (d) g(n) is O(n2)

16. The recurrence relation that arises in relation with the complexity of binary search is
(a) T(n) = T(n/2) + k, where k is a constant
(b) T(n) = 2T(n/2) + k, where k is a constant
(c) T(n) = T(n/2) + log(n)
(d) T(n) = T(n/2) + n

17. . The order of an algorithm that finds whether a given Boolean function of 'n' variables, produces a 1 is
(a) constant (b) linear (c) logarithmic (d) exponential

18. The Ackermann's function


(a) has quadratic time complexity (b) has exponential time complexity
(c) can't be solved iteratively (d) has logarithmic time complexity

19. The running time of an algorithm is given by


T(n) = T(n - 1)+ T(n - 2) - T(n - 3), if n > 3
= n, otherwise.
The order of this algorithm is
(a) n (b) log n (c) nn (d) n2

20. What should be the relation between T(l), T(2) and T(3), so that the previous question, gives an algorithm
whose order is constant?
(a) T(l) + T(2) = T(3) (c) T(1) - T(3) = (2)
(b) T(1) + T(3) = 2T(2) (d) T(1) + T(2) = T(3)

21. The running time T(n), where 'n' is the input size of a recursive algorithm is given as follows.
T(n) = c + T(n - 1), if n > 1
d, if n = 1
The order of this algorithm is
(a) n2 (b) n (c) n3 (d) nn

22. There are 4 different algorithms Al, A2 , A3 , A4 to solve a given problem with the order
log(n), log(log(n)), nlog(n), n log(n) respectively. Which is the best algorithm?
(a) Al (b) A2 (c) A4 (d) A3

23. The concept of order (Big 0) is important because


(a) it can be used to decide the best algorithm that solves a given problem
(b) it determines the maximum size of a problem that can be solved in a given system, in a given amount of
time
(c) it is the lower bound of the growth rate of the algorithm
(d) none of the above

24. An algorithm is made up of 2 modules M1 and M2. If order of M1 is f (n) and M2 is g (n) then the order of
the algorithm is
(a) max (f (n) , g (n) ) (b) min (f (n) , g (n) )
(c) f(n) + g(n) (d) f(n) * g(n)

25. Let m, n be positive integers. Define Q(m,n) as


Q(m, n) = 0, if m < n
= Q(m - n, n) + p, if m ≥ n
Then Q(m, 3) is (a div b, gives the quotient when a is divided by b)
(a) a constant (b) p * (m mod 3) (c) p * (m div 3) (d) 3 * p

Solutions:
1. d 2. d 3. d 4. c 5. b 6. c 7. a 8. c 9. b 10. b 11. b 12. b 13. b 14. c 15.c, d
16. a 17. d 18. c 19. a 20. a 21. b 22. b 23. a,b 24. a 25. c

Excersies Chapter – 2

1. Find (a) └ 4.3 ┘, └ - 4.3 ┘(b)┌ 4.3 ┐, ┌ - 4.3 ┐

2. Find (a) 50%6, 80%6 (b) -70%6, -45%10

3. Define Algorithm development life cycle.

4. Define Algorithm and design an algorithm to find out the total number of even and odd number in a
group of 50 numbers.

5. Explain the different ways of analyzing algorithms.

6. What are the differences between bottom up and top down approach of programming?

7. What is structured and modular programming? Differentiate between them.

8. Solve the recurrent relation


T(n) = T(n/2) + 1
T(1) = 1 (GATE 1988)

9. Solve the recurrent relation


T(n) = T(n/2) + √n
T(1) = 1 (GATE 1989)

10. Solve the recurrent relation


Xn = 2Xn-1 – 1, n>1
X1=2
11. Convert each time formula to the best possible big-O notation. Do not include any spurious constants
in your big-O answer.

Time Formula Big-O


10n .
2n² .
3 times log (base 2) of n .
2n² + 10n .
12. Write the simplest big-O expression to describe the number of operations required for the following
algorithm:

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


{
...statements that require exactly i operations...
}

Chapter - 3
ARRAY
3.1 INTRODUCTION & DEFINITION

Depending upon the requirement of data and it’s structures needed to represent various kinds of problems and
deriving out solutions through computer the Data Structure is classified into linear and non linear classes. The
array supports linear representation of homogeneous data elements, which facilitates to put a group of same
data items together and hence it allows to formation of a structure. An array is referred by two things one is
index i.e. the location and the other is value in the location. In Programer’s view point an array is seemed to be
formed in contiguous locations of computer memory but it is not true always in system’s perspective. One of
the major advantages of array is that to use in a program is very easy but the main difficulty lies in the fact as
array is formed in primary memory of a computer thus it is volatile.

DEFINITION
An array is defined as a finite ordered set of homogeneous elements. Finite means there is a specific number of
elements. Ordered means the elements of the array are indexed. Homogeneous means all the elements of the
array must be of same type (e.g. int, float, char etc.).

3.2 DECLARATION OF ARRAY


3.2.1 Declaration:
One dimensional array:
int iarrMarks[100] // Declares array iarrMarks[] with 100 integers
float farrNum[100] // Declares array farrNum[] with 100 floating point numbers.
char carrName[100] // Declares array carrName[] with 100 characters
In General case, consider an array as
a[l1..u1]
Obviously the above array contains (u1 –l1 + 1) elements. In C, the lower index l1 for an array is 0.

Two dimensional array:


int iarrMatrix[20][30] //Declares two dimensional array with order 20X30 (i.e. 20 rows and 30
columns).
Now consider a two dimensional array in general as
a[l1..u1][l2..u2]
Then the above array contains (u1 –l1 + 1) rows and (u2 –l2 + 1) columns. So the total number of elements in the
array is (u1 –l1 + 1) (u2 –l2 + 1).

N dimensional array:
If we interpret the indices to be N-dimensional (i1, i2, i3…, in) then it is called an N-dimensional array.
In the N-dimensional array, if the array is declared as a[l 1…u1][l2…u2] …[ln…un] where li and ui are the lower
bound and upper bound respectively of the ith dimension then –
The total number of elements = (u1-l1+1)(u2-l2+1)…(un-ln+1)
n
= П (ui-li+1)
i=1

3.3 MEMORY REPRESENTATION OF ARRAY


There are two ways to store an array into memory-1) Row major ordering and 2) Column major ordering. They
are described in this section. Here it is considered that each element takes only one byte to store its value.
3.3.1 Row major ordering: In row major ordering the rows of the array are stored first. Consider an one
dimensional array a[l1..u1]. Suppose its base address (address of the first element) is λ.

l1 l1+1 …. i1 …. u1

Then the address of the element a[i1] is λ + i1 – l1.

Now consider a 2-dimensional array a[0..3][0..3]. Its rows and elements in each row are depicted below.

a[0][0]
Row 1
a[0][1]
a[0][2]
a[1][0]
Row 2
a[1][1]
a[1][2]
a[2][0]
Row 3
a[2][1]
a[2][2]
a[3][0]
Row 4
a[3][1]
a[3][2]

For a general case suppose the declaration of a 2-dimensional array be a[l 1…u1][l2…u2] and the base address is
λ.

l2 l2+1 …. i2 …. u2
l1
l2 +1
….
i1 -1
i1
….
u1

Now to calculate the address of the element a[i 1][i2], first traverse the i1-l1 rows (each row contains u2-l2+1
elements ) and then i2-l2 elements. Hence the address of a[i1][i2] = λ + (i1-l1)(u2-l2+1)+(i2-l2).
Now consider a 3-dimensional array a[0..2][0..2][0..1].
a[0][0][0]
Row 1
a[0][0][1]
a[0][1][0] Page 1
Row 2
a[0][1][1]
a[0][2][0]
Row 3
a[0][2][1]
a[1][0][0]
Row 1
a[1][0][1]
a[1][1][0] Page 2
Row 2
a[1][1][1]
a[1][2][0]
Row 3
a[1][2][1]
a[2][0][0]
Row 1
a[2][0][1]
a[2][1][0] Page 3
Row 2
a[2][1][1]
a[2][2][0]
Row 3
a[2][2][1]

If a declaration of a 3-dimensional array be a[l1…u1][l2…u2][l3…u3] and the base address be λ then the address
of the element a[i1][i2][i3] = λ +(i1-l1)(u2-l2+1)(u3-l3+1)+(i2-l2) (u3-l3+1)+(i3-l3)

Hence for an N-dimensional array a[l1…u1][l2…u2]…..[ln…un], if the base address be λ (i.e. the address of a[l1]
[l2]..[ln]), then the address of a[i1][i2]..[in] = λ + (i1-l1)(u2-l2+1)(u3-l3+1)…( un-ln+1)
+ (i2-l2)(u3-l3+1)(u4-l4+1)…( un-ln+1)
+ ….
+ ….
+ (in-ln)
n
= λ + ∑j=1….n (ij-lj)aj with aj= П (uk-lk+1)
k=j+1

an= 1

3.3.2 Column major ordering: In this technique we store the columns of an array first. Consider an one
dimensional array a[l1..u1]. Suppose its base address (address of the first element) is λ.
l1 l1+1 …. i1 …. u1

Then the address of the element a[i1] is λ + i1 – l1.

Consider a 2-dimensional array a[0..3][0..2]. Its columns and elements in each column are depicted below.

a[0][0]
a[1][0]
Column 1
a[2][0]
a[3][0]
a[0][1]
a[1][1]
Column 2
a[2][1]
a[3][1]
a[0][2]
a[1][2]
Column 3
a[2][2]
a[3][2]

Suppose the declaration of a 2-dimensional array be a[l1…u1][l2…u2] and the base address be λ.

l2 l2+1 …. i2-1 i2 …. u2
l1
l2 +1
….
i1
….
u1

Now to calculate the address of a[i1][i2] first traverse the i2-l2 columns (each column has u1-l1+1 elements) and
then i1 – l1 elements. Hence the address of a[i1][i2] = λ + (i1-l1)+(i2-l2) (u1-l1+1).

Now consider a 3-dimensional array a[0..2][0..2][0..1] ;


a[0][0][0]
Column 1
a[0][1][0]
a[0][2][0] Page 1
a[0][0][1]
Column 2
a[0][1][1]
a[0][2][1]
a[1][0][0]
Column 1
a[1][1][0]
a[1][2][0] Page 2
a[1][0][1]
Column 2
a[1][1][1]
a[1][2][1]
a[2][0][0]
Column 1
a[2][1][0]
a[2][2][0] Page 3
a[2][0][1]
Column 2
a[2][1][1]
a[2][2][1]

If a declaration of a 3-dimensional array be a[l1…u1][l2…u2][l3…u3] and the base address be λ then the address
of a[i1][i2][i3] = λ +(i1-l1) +(i2-l2)(u1-l1+1)+(i3-l3)(u2-l2+1)(u1-l1+1)

Hence for an N-dimensional array a[l1…u1][l2…u2]…[ln…un], if the base address be λ (i.e. the address of
a[l1,l2,..,ln]) then the address of a[i1][i2]…[in] = λ + (i1-l1)
+ (i2-l2)(u1-l1+1)
+ ….
+ ….
+ (in-ln) (un-1-ln-1+1)……(u1-l1+1)

1
=λ + ∑j=1….n (ij-lj)aj with aj= П (uk-lk+1)
k=j-1

a1= 1

3.4 INSERTION INTO ONE DIMENSIONAL ARRAY


Algorithm 3.1 describes the process of insertion into one dimensional array.
Algorithm 3.1
1. Algorithm fnInsertion_into_ 1D_Array(arrData, n, k, item)
2. // Purpose : This algorithm inserts an element into one dimensional array.
3. // Input : arrData[] is an one dimensional array with n number of elements. Element item is to be inserted
into the kth position in the array.
4. // Output : None.
5. {
6. for(i=n-1;i>=k-1;i--)
7. arrData [i+1]= arrData [i];
8. arrData[k-1]=item; // Insert the item.
9. n=n+1; // Set size of the array.
10. }// End of Algorithm.

Example:
Consider an array arrData[] ={10,30,40,50};

Index 0 1 2 3
Element 10 30 40 50

We have to insert 20 in to the 2nd position in the array.


So, here now-
n=4, k=2, item=20

fnInsertion_into_ 1D_Array (arrData, 4, 2, 20)


{
for(i=3;i>=1;i--)
i.e. the for loop will be executed for 3 times.
i=3
arrData[3+1]= arrData[3];
Index 0 1 2 3 4
Element 10 30 40 50 50
i=2
arrData[2+1]= arrData[2];
Index 0 1 2 3 4
Element 10 30 40 40 50
i=1
arrData[1+1]= arrData[1];
Index 0 1 2 3 4
Element 10 30 30 40 50
Execution of for loop is stopped here.
arrData[k-1]=item; or arrData[1]=20;
Index 0 1 2 3 4
Element 10 20 30 40 50
n=n+1; or n=5;
}
So finally-
Index 0 1 2 3 4
Element 10 20 30 40 50
3.5 DELETION FROM ONE DIMENSIONAL ARRAY
Algorithm 3.2 describes the process of deletion from one dimensional array.
Algorithm 3.2
1. Algorithm fnDeletion_from_1D_Array (arrData, n, k)
2. // Purpose : This algorithm deletes an element from one dimensional array.
3. // Input : arrData[] is an one dimensional array with n number of elements. Element item is to be deleted
from the kth position of the array.
4. // Output : Deleted element item.
5. {
6. item= arrData[k-1]; //Item deleted.
7. for(i=k-1;i<n-1;i++)
8. arrData[i]= arrData[i+1];
9. n=n-1; //Set size of the array.
10. return item;
11. }//End of Algorithm

Example:
Let array arrData[]={10,20,30,40,50};
Index 0 1 2 3 4
Element 10 20 30 40 50
nd
We have to delete the 2 element from the array. Therefore,
n=5, k=2
fnDeletion_from_1D_Array(arrData,5,2)
{
item = arrData[1] = 20;
for(i=1;i<5-1;i++)
i.e. the for loop will be executed for 3 times.
i=1
arrData[1]= arrData[1+1];
Index 0 1 2 3 4
Element 10 30 30 40 50
i=2
arrData[2]= arrData[2+1];
Index 0 1 2 3 4
Element 10 30 40 40 50
i=3
arrData[3]= arrData[3+1];
Index 0 1 2 3 4
Element 10 30 40 50 50
Execution of for loop stops here.
n=n-1; or n=4;
}
So finally –
Index 0 1 2 3
Element 10 30 40 50

3.6 TRAVERSING ONE DIMENSIONAL ARRAY


Traversing means to access all the elements of the array (starting from the first element up to the last one).
Algorithm 3.3 describes the process.
Algorithm 3.3
1. Algorithm fnTraverse_1D_Array(arrData, n)
2. // Purpose : This algorithm prints all the elements of a one dimensional array.
3. // Input: arrData[] is an one dimensional array with n number of elements.
4. // Output : None.
5. {
6. for(i=0;i<n;i++)
7. print arrData[i];
8. }//End of Algorithm

3.7 MERGING TWO ONE DIMENSIONAL ARRAYS


Merging means combining the elements of two arrays into a third array. The general technique is first copy all
the elements of the first array into third array and then copy the elements of the second array. If you want to get
a sorted array then you can sort the resultant third matrix. Another approach is sorting while merging. For this
technique merging is done of two sorted arrays. This technique is described in chapter 12 (Merge sort) in
details. Here we will discuss the first approach of merging i.e. we are bothered about whether the resultant array
is sorted or not. Suppose we have to merge the elements of the arrays arrA and arrB into the resultant array
arrC.
First Array Second Array Third Array
arrA[0] 10 arrB[0] 15 arrC[0] 10
arrA[1] 55 arrB[1] 12 arrC[1] 55
arrA[2] 45 arrB[2] 27 arrC[2] 45
arrA[3] 30 arrB[3] 24 arrC[3] 30
arrA[4] 20 arrC[4] 20
arrC[5] 15
arrC[6] 12
arrC[7] 27
arrC[8] 24

Algorithm 3.4 describes the procedure.


Algorithm 3.4
1. Algorithm fnMerge(arrA, arrB, arrC, AN, BN)
2. // Purpose : This algorithm merges two one dimensional arrays.
3. // Input : arrA and arrB are two one imensional arrays and contain AN and BN elements respectively.
Elements of arrA and arrB are copied into arrC.
4. // Output : None.
5. {
6. //First copy the elements of arrA into arrC.
7. for(i=0;i<AN;i++)
8. arrC[i] = arrA[i];
9. //Now copy the elements of arrB into arrC.
10. for(i=0;i<BN;i++)
11. arrC[AN+i] = arrC[i];
12. }//End of Algorithm

3.8 MATRIX ADDITION


Consider two matrices A and B.

A= 9 2 B= 8 4
6 5 1 7

Then their addition produces resultant matrix C as

C= 9+8 2+4 = 17 6
6+1 5+7 7 12

Algorithm 3.5 describes the matrix addition procedure.

Algorithm 3.5
1. Algorithm fnMatrix_Addition(arrA,arrB,arrC,iN,iM)
2. // Purpose : This algorithm performs the addition of two matrices.
3. // Input : arrA and arrB are two iN X iM matrices. arrC is resultant matrix.
4. // Output : None.
5. {
6. for(i = 0;i<iN;i++)
7. for(j=0;j<iM;j++)
8. arrC[i][j] = arrA[i][j] + arrB[i][j];
9. }// End of Algorithm

3.9 SPARSE MATRIX


Matrices with a relatively high proportion of zeros are called sparse matrices. If the matrix is sparse we must
consider an alternate way of representing it rather than the normal row major or column major arrangement.
This is because if majority of elements of the matrix are 0 then an alternative through which we can store only
the non-zero elements and keep intact the functionality of the matrix can save a lot of memory space. Example
of a sparse matrix is:

0 1 2 3 4 5 6 7 8 9
0 0 0 0 0 0 0 0 0 0 15
1 0 0 0 0 0 0 -8 0 0 0
2 -3 0 0 0 0 0 0 40 0 0
3 0 0 0 0 10 0 0 0 0 0
4 0 14 0 0 0 0 0 0 -1 0
5 0 0 20 0 0 0 0 0 0 0
6 0 0 0 0 0 31 0 0 0 0
7 0 0 0 -9 0 0 0 0 0 0
8 0 0 0 0 0 0 0 -2 0 0

The above 9 X 10 sparse matrix takes 90 memory spaces to save it into memory according to row major or
column major ordering. A common way of representing non-zero elements of a sparse matrix is the 3-tuple
forms. In this technique the above matrix looks like:
int arrSpmtx[12][3]={
9 10 11 // 9X10 matrix having 11 non-zero elements.
0 9 15
1 6 -8
2 0 -3
2 7 40
3 4 10
4 1 14
4 8 -1
5 2 20
6 5 31
7 3 -9
8 7 -2
};
The first row of the matrix arrSpmtx stores the order of the original matrix and number of nonzero elements.
The next rows store the values of the nonzero elements and their positions in the original matrix. For the given
example 12X3=36 memory spaces are required to store the whole matrix i.e. (90-36) = 54 memory spaces can
be saved.
Algorithm to create a 3-tuple of a given matrix
Algorithm 3.6
1. Algorithm fnSparse_Matrix_Using_3tuple(arrOriginal, iRow, iColumn, arrSpmtx)
2. // Purpose : This algorithm creates a 3-tuple of a given matrix.
3. // Input : arrOriginal is the original matrix with order iRow X iColumn and arrSpmtx is its 3-tuple form.
4. // Output : None.
5. {
6. Index = 0;
7. iNon_Zero_Elements = fnCount_Non_Zero_Elements(arrOriginal, iRow, iColumn);
8. arrSpmtx[Index][0] = iRow;
9. arrSpmtx[Index][1] = iColumn; //Set first row
10. arrSpmtx[Index][2] = iNon_Zero_Elements;
11. Index++;
12. for(i=0;i<iRow;i++)
13. for(j=0;j<iColumn;j++)
14. if(arrOriginal[i][j] != 0)
15. {
16. arrSpmtx[Index][0] = i;
17. arrSpmtx[Index][1] = j; //Set next rows
18. arrSpmtx[Index][2] = arrOriginal[i][j];
19. Index++;
20. }
21. }//End of algorithm

Algorithm 3.7
1. Algorithm fnCount_Non_Zero_Elements(arrOriginal, iRow, iColumn)
2. // Purpose : This algorithm counts number of non-zero elements in a matrix.
3. // Input : arrOriginal matrix with order iRow X iColumn.
4. // Output : Returns number of non-zero elements.
5. {
6. iNon_Zero_Elements = 0;
7. for(i=0;i<iRow;i++)
8. for(j=0;j<iColumn;j++)
9. if(arrOriginal[i][j] != 0)
10. iNon_Zero_Elements++;
11. return iNon_Zero_Elements;
12. }// End of algorithm

Two general types of n-square sparse matrices are triangular matrix and tri-diagonal matrix.
Triangular matrix:
In a matrix where all entries on or below (or on or above) the main diagonal are non-zero is called a lower
(upper) triangular matrix.

15 31 20 -1 -9
-8 40 10 -3 14
10 -3 14 -8 40
31 20 -1 -9 15
Lower triangular matrix Upper triangular matrix

Lower triangular matrix:


Suppose we want to place in memory the lower triangular array a[][]. Clearly it would be wasteful to store those
entries above the main diagonal of a[][], since we know they are all zero; hence we store only the other entries
of a[][] in an one dimensional array b[].

b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9] b[10]
15 -8 40 10 -3 14 31 20 -1 -9
a[1][1] a[2][1] a[2][2] a[3][1] a[3][2] a[3][3] a[4][1] a[4][2] a[4][3] a[4][4]

Observe first that b[] will contain only-


1 + 2 + 3 + …… + n = n(n+1)/2
elements, which is about half as many elements as a 2-dimensional n X n array. Since we will require the value
of a[j][k] in our programs, we will want the formula that gives us the integer x in terms of j and k where-
b[x] = a[j][k]
Observe that x represents the no of elements in the list up to and including a[j][k]. Now there are-
1 + 2 + 3 + ….. + (j-1) = j(j-1)/2
elements in the rows above a[j][k], and there are k elements in the row j up to and including a[j][k].
Accordingly,
x = j(j-1)/2 + k
yields the index that access the value a[j][k] from the linear array b[].

Algorithm 3.8
1. Algorithm fna_to_b(a,b,n)
2. // Purpose : This algorithm stores the elements of a lower triangular matrix to a one dimensional array.
3. // Input : The non-zero values of the n X n lower triangular matrix a is to be stored into the one-
dimensional array b[].
4. // Output : None.
5. {
6. for(j=1;j<=n;j++)
7. {
8. for(k=1;k<=j;k++)
9. {
10. x=j(j-1)/2 + k;
11. b[x] = a[j][k];
12. }
13. }
14. }//End of algorithm

Algorithm 3.9
1. Algorithm fnb_to_a(a,b,n)
2. // Purpose : This algorithm restores the elements of a lower triangular matrix.
3. // Input : The elements of the n X n lower triangular matrix a is to be restored from the one-
dimensional array b[].
4. // Output : None.
5. // Comments : Restore the non–zero elements stored into the one-dimensional array b[] to the n X n
lower triangular matrix a[][] and fills the other entries of the lower triangular matrix a[][] by zeros.
6. {
7. i=1;
8. for(j=1;j<=n; j++)
9. {
10. for(k=1;k<=j; k++)
11. {
12. a[j][k] = b[i]; // stores the non-zero element
13. i++;
14. }
15. for(k=j+1;k<=n; k++) //stores the zero values
16. a[j][k] = 0;
17. }
18. }//End of algorithm

3.10 POLYNOMIAL REPRESENTATION


A polynomial can be represented using array. Suppose we have an integer array of 10 elements:
int arrPoly[10];
Using the above array, the following polynomial is to be stored.
2A7 + 10A5+8A2+ 15A + 21
Consider a general term of a polynomial as CAE. The procedure is to store the coefficient C in the index E of the
array. Thus the array representation of the given polynomial looks like this:

Index 0 1 2 3 4 5 6 7 8 9
Coefficient 21 15 8 0 0 10 0 2 0 0

As array is static in nature, so this process to store a polynomial is not an efficient one. The efficient way is to
store a polynomial in a linked list, which is described in details in chapter 8.

3.11 MCQ Chapter – 3

1. Structured data type made up of finite collection of ordered elements, all of which is of same data type is:
(a) record (b) array
(c) file (d) None of these
2. Elements of array are accessed by
(a) accessing function in built-in data structure
(b) mathematical function
(c) index
(d) None of these.
3. Array is
(a) Linear data structure (b) non-linear data structure
(c) Complex data structure (d) None of these.

4. Row-major order in two dimensional array refers to


(a) all elements of a row are stored in memory in sequence followed by next row in sequence
and so on.
(b) all elements of a row are stored in memory in sequence followed by next column in
sequence and so on.
(c) all elements of a column are stored in memory in sequence followed by next column
in sequence and so on.
(d) None of these

5. Suppose that a two - dimensional arraydeclared ac; "char a [5][6]," is internally stored in
contiguous memory locations starting from the locations "1000" in a column-major manner. The address
where a [i] U] would be stored is
(a) 1000+i+j*5 (b) 1000+i*4+j
(c) 1000 + i + j * 6 (d) 1000 + i * 6 + j

6. A matrix. a, is called lower triangular if and only if for all j > i aij = O. If such a matrix is mapped to I-
dimensional matrix A then it could be mapped to the following index of A .
(a) 1/2 * i (i + 1) + j (b) i + j
(c) i (i + 1) + j (d) None of the above

7. In a compact single dimensional array representation for lower triangular matrices (i.e., an the elements
above the diagonal are zero) of size n x n, nonzero elements (i.e., of the lower triangle) of each row are
stored one after another. starting from the first row, the index cl: the (i,jih element of the lower triangular
matrix in this representation is: (GATE-1994)
(a) i + j (b) i + j - 1
(c) j + i (i - 1)/2 (d) i + j (j - 1)/2

8. Let A = (aij) be an n-rowed square matrix and /12 be the matrix obtained by interchanging
the first and second rows of the n-rowed Identity matrix. Then All2 is such that its first:
(GATE-1997)
(a) row is the same as its second row
(b) row is same as the second row of A
(c) column is the same as the second column of A
(d) row is all zero

9. Let A be a two dimensional array declared as follows: A: array [1..10] [1..15] of integer;
Assuming that each integer takes one memory location the array is stored in row-major order and the first
element of the array is stored at location. 100, what is the address of the element A [l][J] ? (GATE-1998)
(a) 15i + j + 84 (b) 15j + i + 84
(c) Wi + j + 89 (d) 10j + i + 89

10. The information about an array that is used in a program will be stored in
(a) symbol table (b) activation record (c) system table (d) dope vector
11. Which of the following expressions accesses the (i,j)lh entry of a (m x n) matrix stored in column major
form?
(a) n x (i-l) + j (b) m x(j -1) + i
(c) m x (n-j) + j (d) n x (m-i) + j

12. Sparse matrices have


(a) many zero entries (b) many non-zero entries
(c) higher dimension (d) none of the above

Solutions:
1. b 2. c 3. a 4. a 5. a 6. d 7. c 8. c 9. a 10. a 11. a 12. a

3.12 Exercise Chapter – 3

1. Define an array. How can an array be declared?


2. What is ‘row major’ and ‘column major’ ordering of an array? Explain with a suitable example.
3. Write an algorithm to find out the maximum and the 2nd maximum number from an array of integers.
4. Wtite a C function to find out whether an element aij in an array A[I,J} such that aij is the greatest value in
ith row and smallest value in jth column. What is the time complexity of your function?
5. Write a C program to find out whether a matrix is symmetric or not.
6. Consider two single dimention arrays of size 20 and 30 respectively. Write an algorithm tofind out the
elements which are common to both the arrays.
7. An integer array is declared as A[I,J,K]. The starting address of this array is 1000. Considering integer size is
of 4 bytes, find out the location of A[i,j,k] in column major fasion.
8. An array A contains 25 positive integers. Write an algorithm which will find out all pairs of elements whose
sum is 30.
9. An array A contains 25 positive integers. Write an algorithm which will find out the number of odd and even
numbers in that array.
10. Write a C program which will input an integer array of various sizes and compute the average value of the
elements of that array.

Chapter - 4
STRUCTURE AND POINTER
4.1 DEFINITION OF STRUCTURE
We have seen in chapter 3 that an array can be used to store the homogeneous data i.e. the data of same data
types (e.g. int, float, char etc.). But using a structure we can create a user defined data type consisting of
logically related data items of different data types. The general format of a structure definition is as follows:
struct structure_name
{
----------------------
structure elements;
----------------------
};

4.2 BASIC OF POINTER


Consider the following statement:
int iData = 10;
When compiler compiles the above statement, it allocates a memory space from the heap (available memories)
to store the variable iData like the following:

Name of the variable: iData

Value of the variable: 10

Address of the variable: 2020

Now we can access the value 10 by two ways: 1. By name and 2. By address. To store the address of iData into
another variable, we need a pointer variable and the declaration is:
int *iptr1; //Signifies that value at address iptr1 is some integer value.
char *cptr1; //Signifies that value at address cptr1 is some character value.
For pointer operations, two operators are available 1) * (value at) 2) &(address of). Suppose we want to store
the address of iData into iptr1. The declaration is:
iptr1 = &iData;

Name of the variable: iptr1

Value of the variable: 2020

Address of the variable: 4010


Now we can print the value 10 in two different ways:
printf(“%d”,iData);
printf(“%d”,*iptr1);

4.3 STRUCTURE AND POINTER


In the previous section we have seen a pointer pointing to an int or a pointer pointing to a char. Similarly we can
have a pointer pointing to a struct. Consider the following program code:
struct date
{
int dd;
int mm;
int yy;
};
main()
{
struct date d1={06,04,2008}; //Declare d1 as structure variable.
struct date *d2; //Declare d2 as a pointer to a structure.
d2 = &d1;
printf(“%d/%d/%d”,d1.dd,d1.mm,d1.yy);
printf(“%d/%d/%d”,d2->dd,d2->mm,d2->yy);
}

Name of the variable: d1.dd d1.mm d1.yy d2

Value of the variable: 06 04 2008 4050

Address of the variable: 4050 4052 4054 8020

4.4 PASSING STRUCTURE TO FUNCTIONS


struct date
{
int dd;
int mm;
int yy;
};
main()
{
struct date d1; //Declare d1 as structure variable.
fnGetDate(&d1); //Pass the structure by its address.
fnShowDate(&d1); //Pass the structure by its address.
}
void fnGetDate( struct date *d2)
{
*d2->dd = 06;
*d2->mm = 04;
*d2->yy = 2008;
}
void fnShowDate( struct date *d2)
{
printf(“%d/%d/%d”,d2->dd,d2->mm,d2->yy);
}

4.5 SELF REFERENTIAL STRUCTURE


Consider the following structure declaration:
struct node
{
int iData;
struct node *ptrNext;
};
Notice that here the structure element ptrNext contains the address of a node similar to itself. Such type of
structure is called self referential structure.
4.6 DYNAMIC MEMORY ALLOCATION
The dynamic or run time memory allocation helps us to make efficient use of by allocating the required amount
of memory whenever is needed. C provides the following dynamic allocation and de-allocation functions:
(I) malloc() (II) calloc() (III) realloc() (IV) free()
(I) malloc(): The general syntax of malloc() function is:
(data_type *) malloc(size of one block * number of blocks)
Suppose we want to allocate the memory space for previously defined structure node in run time using malloc()
function. Then the statement is:
struct node *ptrNode = (struct node*)malloc(sizeof(struct node));

(II) calloc():The general syntax of calloc() function is:


(data_type *) calloc(number of blocks , size of one block)
Suppose we want to allocate the memory space for the structure node using calloc() function. Then the
statement is:
struct node *ptrNode = (struct node*)calloc(sizeof(struct node));

(III) realloc(): In some situation, the previously allocated memory is insufficient to run the correct application.
It is also possible that the amount of memory allocated is large enough than the required memory. In both the
cases it is necessary to reallocate the memory spaces and this can be done using realloc() function. The general
syntax of realloc() function is:
(data_type *) realloc(ptr, new size)
ptr is a pointer holding the starting address of the allocated memory block. Suppose now we want to allocate 2
block of node in the pointer variable ptrNode. Then the statement is:
ptrNode = (struct node*)realloc(ptrNode, 2* sizeof(struct node));

(IV) free(): Now to de-allocate the memory spaces we can use the function free() and the declaration is:
free(name of pointer variable);

4.7 MCQ Chapter – 4

1. Dynamic memory allocation use


a) Calloc
b) Malloc
c) Free
d) all of these
2. Self referential structure refers
a) own instance
b) own address
c) own variables
d) none of these
3. Default allocation of variables in malloc() is equal to
a) zero
b) garbage
c) any –ve value
d) any +ve value
4. Default allocation of variables in calloc() is equal to
a) zero
b) garbage
c) any –ve value
d) any +ve value
5. A pointer contains
a) address of other variables
b) address of other pointers
c) both a) and b)
d) none of these
6. when compiled a structural code changes to
a) Executable code
b) Non executable code
c) Assignment code
d) None of these
7. A record structure can be represented as
a) Hierarchical structure
b) Canonical structure
c) Linear structure
d) All of these
8. Dynamic memory allocation is used to
a) For program efficiency
b) For space management
c) For time management
d) All of these
9. Advantage of using structure is
a) To use logically related data items but of different datatypes.
b) To represent the records in a hierarchical manner
c) To create an user defined data type
d) None of these
10. In C, size of a structure variable depend on
a) The variable of the maximum size used in the structure
b) The numbers of variables used in the structure
c) Both a) and b)
d) None of these

Solutions:
1. d 2. a 3. b 4. a 5. c 6. c 7. d 8. b 9. a 10. b

4.8 Exercises Chapter – 4

1. What is a Structure? What is a Pointer?

2. How can a structure be passed by a pointer?

3. What is dynamic memory allocation? what is its advantages?

4. What is self refferntial structure? Discuss with an example.

5. How can a structure be passed to a function?


6. The following is a list of entries, with level numbers, in a file of employee record:
1. Employee(200)
2. Name
3. Last
4. First
5. Middle
6. Address
7. Street
8. Area
9. City
10. State
11. Country
12. Pin Code
13. Age
14. Salary
15. Dependents
a) Draw the corresponding hierarchical structure.
b) Which of the items are elementary items?
Describe and write down a record in C to implement this structural representation of data.

Chapter – 5
STRINGS
5.1 Definition:
Let Σ be an alphabet, a non-empty finite set. Elements of Σ are called symbols or characters. A string (or word)
over Σ is any finite sequence of characters from Σ. For example, if Σ = {0, 1}, then 0101 is a string over Σ.

The length of a string is the number of characters in the string (the length of the sequence) and can be any non-
negative integer. The empty string is the unique string over Σ of length 0, and is denoted ε or λ.

The set of all strings over Σ of length n is denoted Σn. For example, if Σ = {0, 1}, then Σ2 = {00, 01, 10, 11}.
Note that Σ0 = {ε} for any alphabet Σ.

The set of all strings over Σ of any length is the Kleene closure of Σ and is denoted as Σ*. In terms of Σn,

For example, if Σ = {0, 1}, Σ* = {ε, 0, 1, 00, 01, 10, 11, 000, 001, 010, 011, …}. Although Σ* itself is countably
infinite, all elements of Σ* have finite length.

A set of strings over Σ (i.e. any subset of Σ*) is called a formal language over Σ. For example, if Σ = {0, 1}, the
set of strings with an even number of zeros ({ε, 1, 00, 11, 001, 010, 100, 111, 0000, 0011, 0101, 0110, 1001,
1010, 1100, 1111, …}) is a formal language over Σ.

5.2 Concatenation and substrings


Concatenation is an important binary operation on Σ*. For any two strings s and t in Σ*, their concatenation is
defined as the sequence of characters in s followed by the sequence of characters in t, and is denoted st. For
example, if Σ = {a, b, …, z}, s = data, and t = structure, then st =datastructure and ts = structuredata.

String concatenation is an associative, but non-commutative operation. The empty string serves as the identity
element; for any string s, εs = sε = s. Therefore, the set Σ* and the concatenation operation form a monoid, the
free monoid generated by Σ. In addition, the length function defines a monoid homomorphism from Σ* to the
non-negative integers.

A string s is said to be a substring or factor of t if there exist (possibly empty) strings u and v such that t = usv.
The relation "is a substring of" defines a partial order on Σ*, the least element of which is the empty string.

5.3 String operations


A number of additional operations on strings commonly occur in Data Structure. They are explained below:

5.3.1 Alphabet of a string


The alphabet of a string is a list of all of the letters that occur in a particular string. If s is a string, its alphabet
is denoted by Alph(s)

5.3.2 String substitution

Let L be a language, and let Σ be its alphabet. A string substitution or simply a substitution is a mapping f that
maps letters in Σ to languages (possibly in a different alphabet). Thus, for example, given a letter a € ∑, one has
f(a) = La where is some language whose alphabet is Δ. This mapping may be extended to strings
as f(ε) = ε for the empty string , and f(sa) = f(s)f(a) for nonempty string . String substitution may be extended to
the entire language as

An example of string substitution occurs in regular languages, which are closed under string substitution. That
is, if the letters of a regular language are substituted by other regular languages, the result is still a regular
language.

5.3.3 String homomorphism

A string homomorphism is a string substitution such that each letter is replaced by a single string. That is, f(a)
= s, where s is a string, for each letter a. String homomorphisms are homomorphisms, preserving the binary
operation of string concatenation. Given a language L, the set f(L) is called the homomorphic image of L. The
inverse homomorphic image of a string s is defined as

f-1(s) = { w | f(w) = s}

while the inverse homomorphic image of a language L is defined as

f-1(L) = { s | f(s) Є L}

Note that, in general, f(f-1(L)) ≠ L, while one does have

f(f-1(L)) L and L f-1(f(L))

for any language L. Simple single-letter substitution ciphers are examples of string homomorphisms.

5.3.4 String projection

If s is a string, and Σ is an alphabet, the string projection of s is the string that results by removing all letters
which are not in Σ. It is written as . It is formally defined by removal of letters from the right hand side:

π∑ (s) = ε if s = ε, the empty string

= π∑ (t) if s = ta and a ∑

= π∑ (t) if s = ta and a Є ∑
Here ε denotes the empty string. The projection of a string is essentially the same as a projection in relational
algebra.

String projection may be promoted to the projection of a language. Given a formal language L, its projection is
given by

π∑ (L) = { π∑ (s) | s Є L}

5.3.5 String Right quotient

The right quotient of a letter a from a string s is the truncation of the letter a in the string s, from the right hand
side. It is denoted as s / a. If the string does not have a on the right hand side, the result is the empty string.
Thus:

(sa) / b =s if a = b

=ε if a ≠ b

The quotient of the empty string may be taken: ε / a = ε

Similarly, given a subset S M of a monoid M, one may define the quotient subset as

S / a = { s Є M | sa Є S}

Left quotients may be defined similarly, with operations taking place on the left of a string.

5.3.6 String Syntactic relation

The right quotient of a subset S M of a monoid M defines an equivalence relation, called the right syntactic
relation of S. It is given by

~s = { (s, t) Є M x M | S / s = S / t }

The relation is clearly of finite index (has a finite number of equivalence classes) if and only if the family right
quotients is finite; that is, if {S / m | m Є M } is finite. In this case, S is a recognizable language, that is, a
language that can be recognized by a finite state automaton. This is discussed in greater detail in the article on
syntactic monoids.

5.3.7 String Right cancellation

The right cancellation of a letter a from a string s is the removal of the first occurrence of the letter a in the
string s, starting from the right hand side. It is denoted as s ÷ a and is recursively defined as

(sa) ÷ b =s if a=b

= ( s ÷ b )a if a ≠ b

The empty string is always cancellable: ε ÷ a = ε


Clearly, right cancellation and projection commute:

π∑ (L) ÷ a = π∑ (L) ( s ÷ a )

5.3.8 String Prefixes

The prefixes of a string is the set of all prefixes to a string, with respect to a given language:

Pref L(s) = { t | s = tu for u Є L }

The prefix closure of a language is

A language is called prefix closed if Pref (s) = L. Clearly, the prefix closure operator is idempotent:

Pref (Pref (L)) = Pref (L)

The prefix relation is a binary relation s t such that if and only if s Є Pref L(t).

Prefix grammars generate languages that are prefix-closed.

5.4 String Topology


Strings admit the following interpretation as nodes on a graph:

 Fixed length strings can be viewed as nodes on a hypercube;


 Variable length strings (of finite length) can be viewed as nodes on the k-ary tree, where k is the number
of symbols in Σ;
 Infinite strings can be viewed as infinite paths on the k-ary tree.

The natural topology on the set of fixed length strings or variable length strings is the discrete topology, but the
natural topology on the set of infinite strings is the limit topology, viewing the set of infinite strings as the
inverse limit of the sets of finite strings. This is the construction used for the p-adic numbers and some
constructions of the Cantor set, and yields the same topology.

5.5 String datatypes


A string datatype is a datatype modeled on the idea of a formal string. Strings are such an important and useful
datatype that they are implemented in nearly every programming language. In some languages they are
available as primitive types and in others as composite types. The syntax of most high-level programming
languages allows for a string, usually quoted in some way, to represent an instance of a string datatype; such a
meta-string is called a literal or string literal.

5.6 String length


Although formal strings can have an arbitrary (but finite) length, the length of strings in real languages is often
constrained to an artificial maximum. In general, there are two types of string datatypes: fixed length strings
which have a fixed maximum length and which use the same amount of memory whether this maximum is
reached or not, and variable length strings whose length is not arbitrarily fixed and which use varying amounts
of memory depending on their actual size. Most strings in modern programming languages are variable length
strings. Despite the name, even variable length strings are limited in length; although, generally, the limit
depends only on the amount of memory available.

5.7 String Representations


Representations of strings depend heavily on the choice of character repertoire and the method of character
encoding. Older string implementations were designed to work with repertoire and encoding defined by ASCII,
or more recent extensions like the ISO 8859 series. Modern implementations often use the extensive repertoire
defined by Unicode along with a variety of complex encodings such as UTF-8 and UTF-16.

Most string implementations are very similar to variable-length arrays with the entries storing the character
codes of corresponding characters. The principal difference is that, with certain encodings, a single logical
character may take up more than one entry in the array. This happens for example with UTF-8, where single
characters can take anywhere from one to four bytes. In these cases, the logical length of the string differs from
the logical length of the array.

The length of a string can be stored implicitly by using a special terminating character; often this is the null
character having value zero, a convention used and perpetuated by the popular C programming language[1].
Hence, this representation is commonly referred to as C string. The length of a string can also be stored
explicitly, for example by prefixing the string with the length as a byte value — a convention used in Pascal;
consequently some people call it a P-string.

In terminated strings, the terminating code is not an allowable character in any string.

The term bytestring usually indicates to strings of bytes — rather than bits or the wider concept of characters,
which may take more space than a byte — that are not terminated in this way, and in bytes may take any value.

Here is an example of a null-terminated string stored in a 10-byte buffer, along with its ASCII representation:

F R A N K NUL k e f w

46 52 41 4E 4B 00 6B 66 66 77

The length of a string in the above example is 5 characters, but it occupies 6 bytes. Characters after the
terminator do not form part of the representation; they may be either part of another string or just garbage.
(Strings of this form are sometimes called ASCIZ strings, after the original assembly language directive used to
declare them.)

Here is the equivalent (old style) Pascal string stored in a 10-byte buffer, along with its ASCII representation:
length F R A N K k e f w

05 46 52 41 4E 4B 6B 66 66 77

Both character termination and length codes limit strings: for example, C character arrays that contain Nul
characters cannot be handled directly by C string library functions: strings using a length code are limited to the
maximum value of the length code.

Both of these limitations can be overcome by clever programming, of course, but such workarounds are by
definition not standard.

Historically, rough equivalents of the C termination method appear in both hardware and software. For example
"data processing" machines like the IBM 1401 used a special word mark bit to delimit strings at the left, where
the operation would start at the right. This meant that while the IBM 1401 had a seven-bit word in "reality",
almost no-one ever thought to use this as a feature, and override the assignment of the seventh bit to (for
example) handle ASCII codes.

It is possible to create data structures and functions that manipulate them that do not have the problems
associated with character termination and can in principle overcome length code bounds. It is also possible to
optimize the string represented using techniques from run length encoding (replacing repeated characters by the
character value and a length) and Hamming encoding.

While these representations are common, others are possible. Using ropes makes certain string operations, such
as insertions, deletions, and concatenations more efficient.

5.8 String as Vectors


While character strings are very common uses of strings, a string in computer science may refer generically to
any vector of homogenously typed data. A string of bits or bytes, for example, may be used to represent data
retrieved from a communications medium. This data may or may not be represented by a string-specific
datatype, depending on the needs of the application, the desire of the programmer, and the capabilities of the
programming language being used.

5.9 String processing algorithms


There are many algorithms for processing strings, each with various trade-offs. Some categories of algorithms
include

 string searching algorithms for finding a given substring or pattern;


 string manipulation algorithms;
 sorting algorithms;
 regular expression algorithms; and
 parsing a string.

Advanced string algorithms often employ complex mechanisms and data structures, among them suffix trees
and finite state machines.
5.9.1 String searching algorithms

String searching algorithms, sometimes called string matching algorithms, are an important class of string
algorithms that try to find a place where one or several strings (also called patterns) are found within a larger
string or text.

Let Σ be an alphabet (finite set). Formally, both the pattern and searched text are concatenations of elements of
Σ. The Σ may be a usual human alphabet (for example, the letters A through Z in English). Other applications
may use binary alphabet (Σ = {0,1}) or DNA alphabet (Σ = {A,C,G,T}) in bioinformatics.

In practice how the string is encoded can affect the feasible string search algorithms. In particular if a variable
width encoding is in use then it is slow (time proportional to N) to find the Nth character. This will significantly
slow down many of the more advanced search algorithms. A possible solution is to search for the sequence of
code units instead, but doing so may produce false matches unless the encoding is specifically designed to avoid
it.

5.9.1.1 Basic classification

The various String algorithms can be classified by the number of patterns each uses.

5.9.1.2 Single pattern algorithms

Let m be the length of the pattern and let n be the length of the searchable text.

Algorithm Preprocessing time Matching time1

Native string search algorithm 0 (no preprocessing) Θ(n m)

average Θ(n+m),
Rabin-Karp string search algorithm Θ(m)
worst Θ(n m)

Finite state automaton based search Θ(m |Σ|) Θ(n)

Knuth-Morris-Pratt algorithm Θ(m) Θ(n)

Boyer-Moore string search algorithm Θ(m + |Σ|) Ω(n/m), O(n)

Bitap algorithm (shift-or, shift-and, Baeza-Yates-Gonnet) Θ(m + |Σ|) Θ(n)

The Boyer–Moore string search algorithm has been the standard benchmark for the practical string search
literature.

5.9.1.3 Algorithms using finite set of patterns


 Aho-Corasick algorithm
 Commentz-Walter algorithm
 Rabin-Karp string search algorithm

5.9.1.4 Algorithms using infinite number of patterns

Naturally, the patterns can not be enumerated in this case. They are represented usually by a regular grammar or
regular expression.

5.9.2 String manipulation algorithms or String Functions

String functions are used to manipulate a string or change or edit the contents of a string. They also are used to
query information about a string. They are usually used within the context of a computer programming
language.

The most basic example of a string function is the length(string) function, which returns the length of a
string (not counting any terminator characters or any of the string's internal structural information) and does not
modify the string. For example, length("hello world") returns 11.

There are many string functions which exist in other languages with similar or exactly the same syntax or
parameters. For example in many languages the length function is usually represented as len(string). Even
though string functions are very useful to a computer programmer, a computer programmer using these
functions should be mindful that a string function in one language could in another language behave differently
or have a similar or completely different function name, parameters, syntax, and results.

Popular String Functions used in C and their jobs are given below:

1. char *strcpy( char *s1, const char *s2)

 copies the string s2 into the character array s1.  The base address of s1 is returned.

2. char *strncpy( char *s1, const char *s2, int n)

 copies at most n characters of the string s2 into the character array s1.  The base address of s1 is
returned.

3. char *strcat( char *s1, const char *s2)

 appends the string s2 to the end of character array s1.  The first character from s2 overwrites the '\0' of
s1. The base address of s1 is returned.

4. char *strncat( char *s1, const char *s2, int n)

 appends at most n characters of the string s2 to the end of character array s1.   The first character from
s2 overwrites the '\0' of s1. The base address of s1 is returned.

5. char *strchr( const char *s,  int c)

 returns a pointer to the first instance of c in s.  Returns a NULL pointer if c is not encountered in the
string.
6. char *strrchr( const char *s,  int c)

 returns a pointer to the last instance of c in s.  Returns a NULL pointer if c is not encountered in the
string.

7. int strcmp( const char *s1, const char *s2)

 compares the string s1 to the string s2.  The function returns 0 if they are the same, a number < 0 if s1 <
s2, a number > 0 if s1 > s2.

8. int strncmp( const char *s1, const char *s2, size_t n)

 compares up to n characters of the string s1 to the string s2.  The function returns 0 if they are the same,
a number < 0 ifs1 < s2, a number > 0 if s1 > s2.

9. int strspn( char *s1, const char *s2)

 returns the length of the longest substring of s1 that begins at the start of s1and consists only of  the
characters found in s2.

10. int strcspn( char *s1, const char *s2)

 returns the length of the longest substring of s1 that begins at the start of s1and contains none of the
characters found in s2.

11. int strlen( const char *s)


 determines the length of the string s.  Returns the number of characters in the string before the '\0'.

12. char *strpbrk( const char *s1,  const char *s2)

 returns a pointer to the first instance in s1 of any character found in s2.  Returns a NULL pointer if no
characters from s2 are encountered in s1.

13. char *strstr( const char *s1,  const char *s2)

 returns a pointer to the first instance of string s2 in s1.  Returns a NULL pointer if s2 is not encountered
in s1.

14. char *strtok(char *s1, const char *s2)

 repeated calls to this function break string s1 into "tokens"--that is the string is broken into substrings,
each terminating with a '\0', where the '\0' replaces any characters contained in string s2. The first call
uses the string to be tokenized as s1; subsequent calls use NULL as the first argument. A pointer to the
beginning of the current token is returned; NULL is returned if there are no more tokens.

5.9.3 String Sorting algorithms

In computer science and mathematics, a sorting algorithm is an algorithm that puts elements of a list in a
certain order. The most-used orders are numerical order and lexicographical order. Efficient sorting is important
to optimizing the use of other algorithms (such as search and merge algorithms) that require sorted lists to work
correctly; it is also often useful for canonicalizing data and for producing human-readable output. More
formally, the output must satisfy two conditions:

 The output is in increasing or decreasing order (each element is no smaller/greater than the previous
element according to the desired total order);
 The output is a permutation, or reordering, of the input.

Wellknown Sorting Algorithms are as follows:

1. Bubble Sort
2. Insertion Sort
3. Selection Sort
4. Merge Sort
5. Quick Sort
6. Heap Sort
7. Radix Sort

This wellknown sort algorithms are discussed in details in chapter 12.

5.9.4 Regular Expression Algorithms

In computing, regular expressions provide a concise and flexible means for identifying strings of text of
interest, such as particular characters, words, or patterns of characters. Regular expressions (abbreviated as
regex or regexp, with plural forms regexes, regexps, or regexen) are written in a formal language that can be
interpreted by a regular expression processor, a program that either serves as a parser generator or examines text
and identifies parts that match the provided specification.We will not discuss this algorithms as this part is
beyond the scope of this book.

5.9.5 Parsing a String

In computer science and linguistics, parsing (more formally: syntactic analysis) is the process of analyzing a
sequence of tokens to determine grammatical structure with respect to a given (more or less) formal grammar. A
parser is thus one of the components in an interpreter or compiler, where it captures the implied hierarchy of
the input text and transforms it into a form suitable for further processing (often some kind of parse tree,
abstract syntax tree or other hierarchical structure) and normally checks for syntax errors at the same time.

5.11 String Implementations


Some languages like C implement strings as templates that can be used with any primitive type, but this is the
exception, not the rule.

If an object-oriented language represents strings as objects, they are called mutable if the value can change at
runtime and immutable if the value is frozen after creation. For example, Ruby has mutable strings, while
Python's strings are immutable.

Other languages, most notably Prolog and Erlang, avoid implementing a string datatype, instead adopting the
convention of representing strings as lists of character codes.

Algorithms of some popular string functions:


Algorithm myStrlen(s1)
// Purpose : This algorithm determines the length of the string.  
// Input : String s1.
// Output : Returns the number of characters in the string s1 before the '\0'.
{
iLength = 0;
while(*s1!=’\0’)
{
iLenght++;
s1++;
}
return iLength;
}// End of Algorithm.

Algorithm myStrcpy(s1,s2)
// Purpose : This algorithm copies a string into a character array.
// Input : s1 is the string and s2 is a character array.
// Output : None.
{
while(*s1!=’\0’)
{
s2[iCounter] = *s1;
iCounter++;
s1++;
}
s2[iCounter] = *s1;
}// End of Algorithm

Algorithm myStrcat(s1,s2)
// Purpose : This algorithm appends the string to the end of character array.
// Input : s1 is the string and s2 is a character array.
// Output : None.
{
iLength = myStrlen(s2);
while(*s1!=’\0’)
{
s2[iLength] = *s1;
iLength++;
s1++;
}
}// End of Algorithm

Algorithm myStrcmp(s1,s2)
// Purpose : This algorithm compares two strings.
// Input : s1 and s2 are the string.
// Output : The algorithm returns 0 if they are the same, a number < 0 if s1 < s2, a number > 0 if s1 > s2.
{
String1 = s1;
String2 = s2;
while(*String1!=’\0’ && * String2!=’\0’)
{
if(*String1!=* String2)
break;
String1 ++;
String2 ++;
}
if(*s1 == ‘\0’ && *s2==’\0’)
return 0;
else
return (myStrlen(s1) – myStrlen(s2));
}// End of Algorithm

MCQ Chapter 5

1. The null character is represented by


(a) \n (b) \0
(c) NULL (d) \r

2. The null character needs a space of


(a) zero bytes (b) one byte
(c) three bytes (d) four bytes

3. When we concatenate two string of size m and n, the resultant string will be of size
(a) m + n (b) less than m + n (c) m * n (d) max(m, n)

4. To find an instance of a String of length l, in another string of length m, the Kunth – Morrues – Pratt
algorithm’s time is proportional to (at worst case)
(a) m + l (b) m (c) l (d) m * l

5. Which string operation is not available in C?


(a) Concatenation
(b) Pattern Matching
(c) Reversing the string
(d) none of these

6. String concatenation means


(a) Combining two strings
(b) Extracting to substring out of a string
(c) Partitioning a string
(d) None of these

7. Pattern matching refers to


(a) finding position where a string pattern P first appears in a given string
(b) Checking wheteher two strings are identical
(c) Checking whether a pattern occurs in a given string
(d) None of these

8. Macro expansion of C is an example of


(a) String matching and manipulation
(b) Application of hashing for a practical program
(c) String operation which does not require string matching
(d) None of these
9. In C strings are stored in
(a) Linked list of character
(b) Linkedlist of usignned character
(c) Array of character
(d) array of integer

10. Problem of garbage collection arises with which method of string representation?
(a) Fixed length method
(b) Index table method
(c) Linked list method
(d) None of these

11. String Homomorphism is one kind of


(a) String Substitution
(b) String Concatenation
(c) Partitioning a string
(d) String matching

12. String Projection is one kind of


(a) String Substitution
(b) String Concatenation
(c) String representation
(d) String matching

13. String right cancellation is one kind of


(a) String Substitution
(b) String Concatenation
(c) String representation
(d) String matching

14. Strlen() function returns


(a) integer value
(b) float value
(c) charcter value
(d) None of these

15. Byte String is made of


(a) array of bits
(b) array of Bytes
(c) array of chatracters
(d) none of these

16. Strcat() function returns


(a) Character value
(b) integer value
(c) pointer value
(d) None of these

Solutions:
1. b 2. b 3. b 4. a 5. c 6. a 7. c 8. a 9. c 10. b 11. a 12. c 13. a 14. a 15.b
16. c
Exercise Chapter 5

1. What is a string? How can it be implemented in C?

2. Give some data structure which is used to implement string. Which of these is the best and why?

3. Write an Algorithm to insert a string x to a master string X. Use data structure of your choice suitably.

4. Write an algorithm to concatenate between two strings str1 and str2. Use array to represent sting.

5. It is required to find a string s in a text p. write an algorithm and a C program to implement that.

6. Consider a string X=’abcde’. List all substrings of X. write a C function to compute all the substrings
from a given string.

7. Check a string W=’abccba’ if it is a palindrome string. Write a C program to find if a string is a


palindrome or not.

8. Write a C program to collect the garbage value from a string i.e to collect the unused portion of a string.
Use both array and linked list representations

9. What will be the string stored in the next figure?


Char link
1 Read 7
2 Love 11
3
4
Start 5 We 2
6
10. Write a C program to 7 Data 9 comput if a string is a particular type X x
Y where X is a 8 arbitrary string of 0 and 1, Y is the reverse
of X and x is any 9 Structure 12 character value. (Ex- 0110a0110,
110b011) 10
11 To 1
12 NULL

Chapter - 6
STACK
6.1 Definition
A stack is a linear data structure in which insertion of new data element and deletion of existing data element is
done from only one end, called top of the stack. As all the insertion and deletion operations are done from one
end of the stack, the last inserted item will be deleted first. That is why stack is called LIFO (Last In First Out)
list or FILO (First In Last Out) list.
In our daily life, a common model of such structure is a pile of plates or a stack of coins.
6.2 Basic Operations
The followings are the basic operations associated with a stack:
1) fnInitialize(S): Initialize the stack S.
2) fnPush(S, iData): Insert the element iData on top of the stack S.
3) fnPop(S): Delete the top most item from the stack S and return the removed item.
4) fnFull(S): This function checks whether the stack S is full or not. To indicate S is full this function
returns TRUE and FALSE otherwise. During PUSH operation Full(S) condition should be checked if
there is any restriction on the maximum number of elements in the stack. Generally if we implement the
stack using array then stack full condition is checked. But in case of implementation of stack using
dynamic memory allocation such type of checking is not needed at all as here we can insert any number
of data in the stack.
5) fnEmpty(S): This is also a Boolean function and returns TRUE for empty stack and FALSE for non-
empty stack. During POP operation Empty(S) condition is checked.

fnPush(S, 10) fnPush(S, 15) 15 fnPush(S, 5) 15

fnFull(S) returns 10 fnFull(S) returns 10 fnFull(S) returns 10


FALSE FALSE TRUE

15 fnPop(S) fnPop(S) fnPop(S)

10 fnEmpty(S) returns 10 fnEmpty(S) returns fnEmpty(S) returns


FALSE FALSE TRUE

Figure 6.1: PUSH and POP operations on a Stack S

6.3 Implementation of stack using array


In this section we will create a stack of integers using array. For this purpose an integer array arrSTACK of size
MAXSIZE may be used and the array declaration is:
int arrSTACK[MAXSIZE];
Here top is global integer variable and the initial value of top is -1. From the array declaration the maximum
value of top may be MAXSIZE. So, top = MAXSIZE indicates stack is full and top = -1 indicates stack is
empty.

Algorithm for PUSH operation on the stack


Algorithm fnPush( arrSTACK[], iData)
//Purpose : This algorithm inserts an item into the stack.
//Input : arrSTACK[] is the array in which the data item iData is to be inserted.
//Output : None
{
if(fnFull() == TRUE) // Checking for stack full condition.
stack is full;
else
{
top = top + 1; // Increase the current length of the stack..
arrSTACK[top] = iData; // insert the item iData.
}
}//End of Algorithm
Algorithm for POP operation from the stack
Algorithm fnPop(arrSTACK[])
//Purpose : This algorithm deletes the top element from the stack.
// Input : The array arrSTACK[] on which the stack is being implemented.
//Output : The top most item iData that has been deleted.
{
if(fnEmpty() == TRUE) //Checking for stack empty condition.
stack is empty;
else
{
iData = arrSTACK[top] // item deleted.
top = top – 1; // Shorten the current length of the stack.
return iData; // Return the value of the item that has been deleted.
}
}//End of Algorithm

Remember: During a POP operation, the item is not deleted from the array, but current length of the stack is
reduced by decrementing top.
Suppose the value of MAXSIZE is 2. Now consider the following sequence of push and pop operations:
Initially the value of top is -1.

fnPUSH(arrSTACK[],25) fnPUSH(arrSTACK[],15) fnPOP(arrSTACK[])

1 1 15 top 1 15

0 25 top 0 25 0 25 top

arrSTACK[0] = 25 arrSTACK[1] = 15 return 15

Now in this situation, one more push operation will overwrite the value of arrSTACK[1].

fnPUSH(arrSTACK[],20)

1 20 top

0 25

arrSTACK[1] = 20

Algorithm to display the elements of the stack


Stack elements reside in the range from 0 to top of the array. So it is required to display the elements of the
array from index 0 to index top.
Algorithm fnDisplay(arrSTACK[])
// Purpose : This algorithm shows the elements of the stack.
// Input : arrSTACK[] is the name of the stack.
// Output : None.
{
if(fnEmpty() == TRUE) // If stack is empty.
There is nothing to display;
else
for(iCounter=0;iCounter<=top;iCounter++)
print arrSTACK[iCounter];
}// End of Algorithm

Algorithm for checking the stack full condition


The stack is full if the value of top reaches MAXSIZE - 1 i.e. the maximum size of the stack.
Algorithm fnFull()
// Purpose : This algorithm checks whether the stack is full or not.
// Input : None.
// Output : This algorithm returns TRUE if stack is full and returns FALSE if stack is not full.
{
if (top == MAXSIZE - 1)
return TRUE;
else
return FALSE
}// End of Algorithm

Algorithm for checking the stack empty condition


The stack is empty if the value of top reaches -1.
Algorithm fnEmpty()
// Purpose : This algorithm checks whether the stack is empty or not.
// Input : None.
// Output : This algorithm returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == -1)
return TRUE;
else
return FALSE
}// End of Algorithm

6.4 Implementation of stack using dynamic memory allocation

If we implement stack using array, then insertion of new data elements may cause an overflow and so the stack
full condition is to be checked during each push operation. But stack using dynamic memory allocation does not
demand to check the stack full condition in each push operation as here we can insert any number of data
elements depending upon the available memory.
Figure 6.2 shows a stack using dynamic memory allocation:

NULL

ptrTop
Figure 6.2: Stack using dynamic memory allocation

Each node of the above list has two fields: data field and pointer field. Data field contains the data element and
the pointer field contains the address of the next node in the list. So from the concept of self referential structure
the structure definition of such a node is:
struct Node
{
int iData;
struct Node *ptrNext;
};
typedef struct Node StackNode;
Here, top is a StackNode type pointer variable and initially the value of top is NULL indicating that the list is
empty.
StackNode *top = NULL;

Algorithm for Push Operation on the stack


Algorithm fnPush(iData)
// Purpose : This algorithm inserts an item on the top the stack.
// Input : The item iData that is to be inserted into the stack.
// Output : None.
{
StackNode *ptrNewNode; // Assign a pointer to new node.
ptrNewNode = (StackNode*) malloc(sizeof(StackNode)); //Create the new node.
ptrNewNode->iData = iData; //Load the data field of the newly created node.
ptrNewNode->ptrNext = top; // Now new node will point to the current top node.
top = ptrNewNode; //New node is now the top node of the list.
}// End of Algorithm

Algorithm for Pop Operation from the stack


Algorithm fnPop()
// Purpose : This algorithm deletes an item from the top of the stack.
// Input : None.
// Output : The item iData that has been deleted.
{
StackNode *ptrDeleteNode;
if(fnEmpty() == TRUE) //Check the stack empty condition.
Stack is Empty;
else
{
iData = top->iData; // The item to be deleted.
ptrDeleteNode = top; //Set the pointer to the node to be deleted.
top = top->ptrNext; //Set the new value of top.
free(ptrDeleteNode); //Free the node that has been deleted.
return(iData); //Return the data of the deleted node.
}
}// End of Algorithm
Algorithm to display the elements of the stack
Algorithm fnDisplay()
// Purpose : This algorithm displays the items of the stack.
// Input : None.
// Output : None.
{
StackNode *ptrDisplayNode;
if(fnEmpty() == TRUE) //Check stack empty condition.
There is nothing to display;
else
{
ptrDisplayNode = top; //Initialize the pointer to top node.
while(ptrDisplayNode!=NULL) //Until list is empty.
{
print (ptrDisplayNode->iData); //Print the data.
ptrDisplayNode = ptrDisplayNode -> ptrNext; //Go to next node.
}
}
}// End of Algorithm

Algorithm for checking the stack empty condition


Algorithm fnEmpty()
// Purpose : This algorithm checks whether the stack is empty or not.
// Input : None.
// Output : This algorithm returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == NULL)
return TRUE;
else
return FALSE
}// End of Algorithm

Example: Suppose we want to create a stack using dynamic memory allocation. The following statements show
how the different operations are performed on the stack.
Initially the value of top is NULL.

Statement 1: fnPush(20) 20 NULL


top

Statement 2: fnPush(25) 25 20 NULL


top

Statement 3: fnPop() 20 NULL


top

Statement 4: fnPush(15)
15 20 NULL
top
Statement 5: fnPop()
20 NULL
top
6.5 Arithmetic Expressions
An expression is defined as combination of some operands and operators. Basically three types of notation for
an expression are available and the notations are as follows:
1) Infix Notation: In this notation operators are written in between the operands. The expression to
multiply two numbers X and Y is written in infix notation as:
X*Y
2) Prefix Notation: In this notation operators are written before the operands. Prefix notation is also called
polish notation as the polish mathematician Jan Lukasiewicz developed this notation. The expression to
multiply two numbers X and Y is written in prefix notation as:
*XY
3) Postfix Notation: In this notation operators are written after the operands. This notation is also called
the reverse polish notation. The expression to multiply two numbers X and Y is written in postfix
notation as:
XY*
6.6 Converting Infix expressions to postfix notation
For this conversion, only the operators of the infix expression are pushed onto the stack according to the
following rule:
1. If the priority of the stack top operator is greater than or equal to the priority of the incoming operator then
the new operator can not be pushed onto the stack. In other words, the stack top operator is to be popped up
until the priority of the stack top operator is less than the priority of the incoming operator.

The following table shows the order of priority of some commonly used operator:

Operators Priority
Unary (-,+,!,log, sin etc.) 5
↑,^ (Exponentiation Operator),$ 4
*, / 3
+,- 2
<,<=,>,>=,!= 1

Table 6.1: Priority of some commonly used operator


Here a stack Oprtstk is used to push the operators of the infix expression I and a string P is used to store the
equivalent postfix notation. The operands of I are directly added to P.

Example 1: Convert the following infix expression to its corresponding postfix notation:
I = A+B*C/(D+E)

Step 1: First input character is A, which is an operand. So add A to P.


P: A

Oprtstk

Step 2: Second input character + is an operator. The operator stack is empty. So push + onto the stack.
P: A

+ top

Oprtstk
Step 3: Third input character B is an operand. So add B to P.
P: AB

+ top

Oprtstk

Step 4: Next input symbol * is an operator and the stack top operator is +. As priority of + is less than the
priority of *, push * onto the operator stack without any pop operation.
P: AB

* top
+

Oprtstk

Step 5: Next input symbol C is an operand. So add C to P.


P: ABC

+ top

Oprtstk

Step 6: Next input symbol / is an operator and the stack top operator is *. The priority of * is equal to the
priority of /. So pop * from operator stack and add * to P
P: ABC*

+ top

Oprtstk
Now the priority of the stack top operator (+) is less than the priority of the current symbol /. So, no more pop
operation takes place. Push / onto the operator stack.

/ top

Oprtstk

Step 7: Next input symbol is (. Push ( onto the stack.


P: ABC*

( top
/

Oprtstk

Step 8: Next input character D is an operand. So add D to P.


P: ABC*D

( top
/

Oprtstk

Step 9: Next input symbol is + which is an operator and the stack top element is (. So there is no need to
compare the priorities of operator. Push + onto the stack.
P: ABC*D
+

+ top

Oprtstk
Step 10: Next input symbol E is an operand. So add E to P.
P: ABC*DE
+ top
(

Oprtstk
Step 11: Now the last input symbol is ). So pop all the operators from the operator stack until a left parenthesis
( is encountered.
P: ABC*DE+

( top
/

Oprtstk

Now pop ( from the stack, but don’t add it to P.

/ top

Oprtstk

Step 12: End of expression. So pop operators from the operator stack until stack is empty and add the symbols
to P.
Finally, P: ABC*DE+/+
Algorithm to convert an infix expression to postfix notation:
Algorithm fnInfixToPostfix (I, P)
// Purpose : This algorithm finds the postfix notation of an infix expression.
// Input : I is an arithmetic expression in infix notation. P is the equivalent postfix notation.
// Output : None.
// Comments : Oprtstk is the operator stack.
{
while(not end of I)
{
symbol = next input character from I;
if(symbol is an operand)
Add it to P;
elseif(symbol is an operator)
{
while(fnTop(Oprtstk)>=symbol and !Empty(Oprtstk) and fnTop(Oprtstk)!=’(‘)
{
n = pop(Oprtstk);
add n to P;
}
push(Oprtstk , symbol);
}
elseif(symbol is ‘(‘)
push(Oprtstk , symbol);
else // right parenthesis is encountered.
{
while(Oprtstk[top] != ‘(‘ ) //Pop from stack until left parenthesis is encountered.
{
n = pop(Oprtstk);
add n to P;
}
n = pop(Oprtstk); //only pop left parenthesis ‘(‘. Don’t add it to P.
}
}// end of while
while(not stack empty)
{
n = pop(Oprtstk);
add n to P;
}
}// End of algorithm

Example 2: Find postfix notation for A$B*C-D+E/F*(G+H)


Solution:

Symbol Comments Stack Postfix Expression


Scanne Contents (P)
d
A A is an operand. Add A to P. Empty A
$ Stack is empty. So push $ onto stack. $ A
B B is an operand. Add B to P. $ AB
* Priority($) > Priority(*). So pop $ from stack and add it to * AB$
P. Then push * onto the stack.
C C is an operand. Add C to P. * AB$C
- Priority(*) > Priority(-). So pop * from stack and add it to - AB$C*
P. Then push – onto the stack.
D D is an operand. Add D to P. - AB$C*D
+ Priority(-) = Priority(+). So pop – from stack and add it to + AB$C*D-
P. Push + onto stack.
E E is an operand. Add E to P. + AB$C*D-E
/ Priority(+)<Priority(/). So no pop operation takes place. +/ AB$C*D-E
Push / onto stack.
F F is an operand. Add F to P. +/ AB$C*D-EF
* Priority(/) = Priority(*). So pop / and add / to P. Push * +* AB$C*D-EF/
onto stack.
( Push ( onto the stack. +*( AB$C*D-EF/
G G is an operand and add G to P. +*( AB$C*D-EF/G
+ Push + onto stack. +*(+ AB$C*D-EF/G
H H is an operand. Add it to P. +*(+ AB$C*D-EF/GH
) Pop all the elements from the stack until ( is encountered +* AB$C*D-EF/GH+
and execute one more pop operation to delete ( from the
stack.
End Pop all the elements from the stack until stack is empty. Empty AB$C*D-EF/GH+*+

Example 3: Find postfix notation for A+B*logC/D


Solution:
Symbol Comments Stack Postfix
Scanned Contents Expression
(P)
A A is an operand. Add A to P. Empty A
+ Push + onto stack. + A
B B is an operand. Add it to P. + AB
* Priority(+)<Priority(*). So no pop operation. Push * onto +* AB
the stack.
log Priority(*)<Priority(log). So no pop operation. Push log + * log AB
onto the stack.
C C is an operand. Add C to P. + * log ABC
/ Priority(log)>Priority(/). Pop log and add it to P. +/ ABClog*
Again, Priority(*)=Priority(/). Pop * and add * to P.
Now Priority(+) < Priority(/). So no more pop operation.
Push / onto the stack.
D D is an operand. Add D to P. +/ ABClog*D
End Pop all the elements from stack until it is empty. Empty ABClog*D/+

6.7 Converting Infix expression to prefix notation


Rule for pop operation from the operator stack:
1. If the priority of the stack top operator is greater than the priority of the incoming operator then the new
operator can not be pushed onto the stack. In other words the stack top operator is to be popped up until the
priority of the stack top operator is less than or equal to the priority of the incoming operator.

Example 1: Convert the following infix expression to its corresponding prefix notation:
I = A+B*C/(D+E)
Solution:
First take the reverse of I.
I’ = reverse(I) = (E+D)/C*B+A

Step 1: First input character is (. Push ( onto operator stack.


P’:

(
top

Oprtstk

Step 2: Second input character E is an operand. So add E to P.


P’: E

( top

Oprtstk

Step 3: Third input character + is an operator. Push + onto stack.


P’: E

+ top
(

Oprtstk

Step 4: Next input symbol D is an operand. So add D to P.


P: ED

+ top
(

Oprtstk

Step 5: Next input symbol is ). So pop all the operators from the stack until a left parenthesis is encountered and
add them to P.
P’: ED+

( top
Oprtstk

Now pop ( from the stack, but don’t add it to P.

P’: ED+

Oprtstk

Step 6: Next input symbol / is an operator and the stack is now empty. Push / onto the stack.
P’: ED+

/ top

Oprtstk

Step 7: Next input symbol C is an operand. Add C to P.


P’: ED+C

/ top

Oprtstk

Step 8: Next input character * is an operator. Priority(/) = Priority(*). So push * onto stack without any pop
operation.
P’: ED+C

* top
/
Oprtstk
Step 9: Next input symbol is B which is an operand. Add B to P.
P’: ED+CB

* top
/

Oprtstk

Step 10: Next input symbol + is an operator. Priority(*) > Priority(+). So pop * and add it to P.
Now stack top operator is /.
Again Priority(/) > Priority(+). So pop / and add it to P.
Now Stack is empty.
So push + onto the stack without any comparison.
P’: ED+CB*/

+ top

Oprtstk

Step 11: Now the last input symbol A is an operand. Add A to P.


P’: ED+CB*/A

+ top

Oprtstk

Step 12: End of expression. So pop operators from the operator stack until stack is empty and add the symbols
to P.
P’: ED+CB*/A+
Finally P = reverse(P’) = +A/*BC+DE

Algorithm to convert infix expression to prefix notation:


Algorithm fnInfixToPrefix(I,P)
// Purpose : This algorithm finds the prefix notation of an infix expression.
// Input : I is an arithmetic expression in infix notation. P is the equivalent prefix notation.
// Output : None.
// Comments : P’ = reverse(P) and I’ = reverse(I). Oprtstk is the operator stack.
{
while(not end of I’)
{
Symbol = Next input character of I’;
if(Symbol is an operand)
Add it to P’;
elseif(Symbol is an operator)
{
while(Oprtstk[top] > Symbol and !Empty(Oprtstk) and Oprtstk[top]!=’(‘)
{
n = pop(Oprtstk);
add n to P’;
}
push(Oprtstk , Symbol);
}
elseif(Symbol is ‘(‘)
push(Oprtstk , Symbol);
else // right parenthesis is encountered
{
while(Oprtstk[top] != ‘(‘ ) //Pop from stack until left parenthesis is encountered
{
n = pop(Oprtstk);
add n to P’;
}
pop(Oprtstk); //only pop ‘(‘. Don’t add it to P’
}
} // end of while
while(Empty(Oprtstk)==FALSE) // Until Stack is empty
{
n = pop(Oprtstk);
add n to P’;
}
P = reverse(P’);
}// End of Algorithm

Example 2: Find prefix notation for A/B$C+ (D*E-log F)


Solution:
I = A/B$C+ (D*E-log F)
I’ = Reverse of I = (log F – E*D)+C$B/A
Symbol Comments Stack Prefix Expression
Scanned Contents (P’)
( Push ( onto the stack. (
log Push the unary operator log onto the stack. ( log
F F is an operand. Add F to P’. ( log F
- Priority(log) > Priority(-). So pop log and add log to P’. (- Flog
Push – onto stack.
E E is an operand. Add E to P’. (- FlogE
* Priority(-) < Priority(*). So no pop. Push * onto the stack. (-* FlogE
D D is an operand. Add D to P’. (-* FlogED
) Pop * , - from stack and add them to P’. Empty FlogED*-
Then pop ( , but don’t add it to P’.
+ Now stack is empty. Push + onto the stack. + FlogED*-
C C is an operand. Add C to P’. + FlogED*-C
$ Priority(+)<Priority($). So no pop operation. Push $ onto stack. +$ FlogED*-C
B Add operand B to P’. +$ FlogED*-CB
/ Priority($)>Priority(/). So pop $ from stack and add it to P’. +/ FlogED*-CB$
Next, Priority(+) < Priority($). Hence no more pop.
Push / onto the stack.
A Add operand A to P’. +/ FlogED*-CB$A
End Pop all the elements from the stack until stack is empty. Empty Flog ED*-CB$A/+
Finally, P = reverse of P’ = +/A$BC-*DElogF
6.8 Evaluating Postfix Expression
To evaluate a postfix expression an operand stack (Oprndstk) is used rather than an operator stack i.e. here the
operands of the postfix expressions are pushed onto the stack.
Example 1:
Evaluate the following postfix expression:
234*+8–
Step 1: First input element is 2 which is an operand. So push 2 onto the operand stack.

2 top

Oprndstk
Step 2: Next input element is 3 which is also an operand. So push 3 onto the operand stack.

3 top
2

Oprndstk
Step 3: Third input element 4 is again an operand. So push 4 onto the operand stack.

4 top
3

2
Oprndstk

Step 4: Next input symbol is * which is an operator. Now pop two elements from the stack and calculate the
result as-
result = Element returned from the second pop operation * Element returned from the first pop operation
=3*4
= 12.
Now push the result 12 onto the operand stack.

12 top
2

Oprndstk

Step 5: Next + is also an operator. So result = 2 + 12 = 14. Push result onto the stack.

14 top

Oprndstk

Step 6: Next input symbol is operand 8. Push 8 onto the operand stack.

8 top
14

Oprndstk
Step 7: Next input symbol is operator -. So compute the result as 14 -8 = 6 and push 6 into the stack.

6 top
Oprndstk

Step 8: End of expression. So finally pop the only element from the stack and thus 6 is the final result.

Algorithm to evaluate a postfix expression:


Algorithm fnPostfixEvaluation (P,I)
// Purpose : This algorithm evaluates a postfix expression.
// Input : P is an arithmetic expression in postfix notation. I is finally calculated result.
// Output : This algorithm returns the result after evaluating the postfix expression.
// Comments : Oprndstk is the operand stack.
{
while(not end of Q)
{
Symbol = Next input character of P;
if(Symbol == Operand)
push(Oprndstk, Symbol);
else
{
Operand2 = pop(Oprndstk);
Operand1 = pop(Oprndstk);
Value = result of applying Symbol to Operand1 and Operand2;
push(Oprndstk, Value);
}
}
I = pop(Oprndstk);
return(I);
}// End of Algorithm

6.9 Evaluating Prefix Expression

Here also an operand stack is used rather than an operator stack. For this evaluation, reverse of the given prefix
expression is taken and scanned until the end of P’ to find the result.

Example 1:
Evaluate the following prefix expression:
P =-+2*348
P’ = reverse(P) = 8 4 3 * 2 + -

Step 1: First input element is 8 which is an operand. So push 8 onto the operand stack.

8 top
Oprndstk

Step 2: Next input element is 4 which is also an operand. So push 4 onto the operand stack.

4 top
8

Oprndstk

Step 3: Third input element is 3 which is an operand. So push 3 onto the operand stack.

3 top
4

Oprndstk

Step 4: Next input symbol is * which is an operator. Now pop two elements from the stack and calculate the
result as-
result = Element returned from the first pop operation * Element returned from the second pop operation
=3*4
= 12.
Now push the result 12 onto the operand stack.

12 top
8

Oprndstk
Step 5: Next input symbol 2 is an operand. Push 2 onto the stack.

12

8 top
Oprndstk

Step 6: Next input symbol is operator +. So result = 2 + 12 = 14. Push 14 onto the operand stack.

14 top
8

Oprndstk

Step 7: Next input symbol - is an operator. So compute the result as 14 -8 = 6 and push 6 onto the stack.

6 top

Oprndstk

Step 8: End of expression. So finally pop the only element from the stack and thus 6 is the final result.

Algorithm to evaluate a prefix expression


Algorithm fnPrefixEvaluation (P, I)
// Purpose : This algorithm evaluates a prefix expression.
// Input : P is an arithmetic expression in prefix notation. I is finally calculated result.
// Output : This algorithm returns the result after evaluating the prefix expression.
// Comments : P’ = reverse (P). Oprndstk is operand stack.
{
while(not end of P’)
{
Symbol = Next input character of P’;
if(Symbol == Operand)
push(Oprndstk, Symbol);
else
{
Operand1 = pop(Oprndstk);
Operand2 = pop(Oprndstk);
Value = Result of applying Symbol to Operand1 and Operand2;
push(Oprndstk, Value);
}
}
I = pop(Oprndstk);
return(I);
}// End of Algorithm

6.10 Application of Stack


The applications of stack are as follows:
1. Reversing a string.
2. Checking the balanced parenthesis.
3. Conversion from infix to postfix and prefix expression.
4. Evaluation of postfix and prefix expression.
5. Simulating recursion.

6.11 Reversing a string


Consider the string strName[] = “DATA”. Now using stack, reverse of the string strName can be obtained by
executing the following sequence of statements.
After all the push operations, pop the data until stack is empty.
fnPush(arrSTACK[], D) 3 A top strName[0] = fnPop(arrSTACK[]) = A
fnPush(arrSTACK[], A) strName[1] = fnPop(arrSTACK[]) = T
fnPush(arrSTACK[], T) 2 T strName[2] = fnPop(arrSTACK[]) = A
fnPush(arrSTACK[], A) strName[3] = fnPop(arrSTACK[]) = D
1 A
Now the string strName contains reverse of the original information.
0 D

Algorithm to reverse a string using stack


Algorithm fnReverse(strName)
// Purpose : This algorithm reverse a given string.
// Input : strName is the required string.
// Output : None.
// Comments : arrSTACK is a character array to implement the stack.
{
iCounter = 0;
while(strName[iCounter]!=’\0’) //Until end of string.
{
fnPush(arrSTACK[], strName[iCounter]); //Push the character into the stack.
iCounter = iCounter + 1;
}
iCounter = 0;
while(fnEmpty() != TRUE) //Until the stack is not empty.
{
strName[iCounter] = fnPop(arrSTACK[]); //Pop the character and store into array strName[].

iCounter = iCounter + 1;
}
}//End of Algorithm
6.12 Checking balanced parenthesis
Suppose a string arrExpression[] = “((()()))”. Now we can check easily by stack whether the parenthesis is
balanced or not. If left parenthesis (‘(‘) is encountered then push it onto the stack. When a right parenthesis (‘)’)
is encountered then pop one left parenthesis from the stack. At the end, if the stack is empty then conclude that
the parenthesis is balanced.
Algorithm to check balanced parenthesis
Algorithm fnCheck_balanced_parenthesis(arrExpression)
// Purpose : This algorithm checks balanced parenthesis.
// Input : arrExpression is the required expression.
// Output : This algorithm returns TRUE if parenthesis is balanced and returns FALSE otherwise.
// Comments : arrSTACK is a character array to implement the stack.
{
iCounter = 0;
while(strExpression[iCounter]!=’\0’) //Until end of string.
{
if(strExpression[iCounter] == ‘(‘)
fnPush(arrSTACK[], strExpression[iCounter]); //Push ‘(’ onto the stack.
if(strExpression[iCounter] == ‘)‘)
fnPop(arrStack[]); //Pop ‘(‘ from the stack.
iCounter = iCounter + 1;
}
return(fnEmpty()); //If stack is empty return TRUE otherwise return FALSE.
}// End of Algorithm

6.13 Multiple Stacks


We can implement two stacks in the same array where one stack grows from left to right and other from right to
left. The value of the top (top1) for the first stack is initialized from -1 and for the second stack the top (top2) is
initialized from the MAXSIZE of the array. So top1 is incremented by 1 during the push operation onto the first
stack and in case of push operation onto the second stack, the value of top2 is to be decremented by 1.

Stack 1 Stack 2

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

top1 top2
Figure 6.3: Two stack using same array

For the two-stack of the figure 6.3 the initial value of top1 is -1 and for top2 the initial value is 13.
The stack full condition for two-stack is top2 = top1 + 1. Stack empty condition for the first stack is top1 = -1
and for the second stack the condition is top2 = MAXSIZE.

Algorithm of insertion into two-stack


Algorithm fnPushTwoStack(iStackNo, idata)
// Purpose : This algorithm inserts an item into the two-stack.
// Input : iStackNo denotes the stack number. iData is the data item to be inserted onto the either stack.
// Output : None.
// Comments : top1 (top of the first stack) is initialized from -1 and top2 (top of the second stack) is initialized
from MAXSIZE.
{
if (top2 == top1 + 1)
Stack is full;
else
{
if (iStackNo == 1)
{
top1 = top1 + 1;
arrStack[top1] = iData;
}
elseif (iStackNo == 2)
{
top2 = top2 -1;
arrStack[top2] = iData;
}
else
Invalid Stack number;
}
}//End of Algorithm

Algorithm of deletion from the two-stack


Algorithm fnPopTwoStack(iStackNo)
// Purpose : This algorithm deletes an item from two-stack.
// Input : iStackNo is the stack number from which the item is to be deleted.
// Output : The item iData which has been deleted.
{
if (iStackNo == 1)
{
if(top1 == -1)
First stack is empty;
else
{
iData = arrStack[top1];
top1 = top1 - 1;
return iData;
}
}
elseif (iStackNo == 2)
{
if (top2 == MAXSIZE)
Second stack is empty;
else
{
iData = arrStack[top2];
top2 = top2 + 1;
return iData;
}
}
else
Invalid stack number;
}// End of Algorithm

/****Implementation of stack using C****/


/****File name : stack.c****/
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50

//Prototype Declaration
void fnPush( int[], int );
int fnPop(int[]);
void fnDisplay(int[]);
int fnFull();
int fnEmpty();

int top=-1;

void fnPush( int arrSTACK[], int iData)


//Purpose : This function inserts an item into the stack.
//Input : arrSTACK[] is the array in which the data item iData is to be inserted.
//Output : None
{
if(fnFull() == 1) // Checking for stack full condition.
printf("stack is full");
else
{
top = top + 1; // Increase the current length of the stack..
arrSTACK[top] = iData; // insert the item iData.
}
}//End of function

int fnPop(int arrSTACK[])


//Purpose : This function deletes the top element from the stack.
// Input : The array arrSTACK[] on which the stack is being implemented.
//Output : The top most item iData that has been deleted.
{
int iData;
if(fnEmpty() == 1) //Checking for stack empty condition.
{
//printf("stack is empty");
return -1;
}
else
{
iData = arrSTACK[top]; // item deleted.
top = top - 1; // Shorten the current length of the stack.
return iData; // Return the value of the item that has been deleted.
}
}//End of function
void fnDisplay(int arrSTACK[])
// Purpose : This function shows the elements of the stack.
// Input : arrSTACK[] is the name of the stack.
// Output : None.
{
int iCounter;
if(fnEmpty() == 1) // If stack is empty.
printf("There is nothing to display");
else
for(iCounter=0;iCounter<=top;iCounter++)
printf("%d\n",arrSTACK[iCounter]);
}// End of function
int fnFull()
// Purpose : This function checks whether the stack is full or not.
// Input : None.
// Output : This function returns TRUE if stack is full and returns FALSE if stack is not full.
{
if (top == MAXSIZE - 1)
return 1;
else
return 0;
}// End of function
int fnEmpty()
// Purpose : This function checks whether the stack is empty or not.
// Input: None.
// Output : This function returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == -1)
return 1;
else
return 0;
}// End of function
void main()
{
int arrSTACK[50],iChoice,iData;
clrscr();
do
{
printf("1. Push\n");
printf("2. Pop\n");
printf("3. Display\n");
printf("4. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element");
scanf("%d",&iData);
fnPush(arrSTACK,iData);
break;
case 2:
iData = fnPop(arrSTACK);
if(iData == -1)
printf("Stack is empty\n");
else
printf("Item deleted = %d\n",iData);
break;
case 3:
printf("\n******Stack Content******\n");
fnDisplay(arrSTACK);
printf("\n*************************\n");
break;
case 4:
exit(1);
}
}while(1);
getch();
}
/****C code to implement a stack using dynamic memory allocation****/
/*File Name : DMASTACK.C */
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50;
struct Node
{
int iData;
struct Node *ptrNext;
};
typedef struct Node StackNode;
StackNode *top = NULL;

//Prototype Declaration
void fnPush(int);
int fnPop();
void fnDisplay();
int fnEmpty();

void fnPush(int iData)


// Purpose : This function inserts an item on the top the stack.
// Input : The item iData that is to be inserted into the stack.
// Output : None.
{
StackNode *ptrNewNode; // Assign a pointer to new node.
ptrNewNode = (StackNode*) malloc(sizeof(StackNode)); //Create the new node.
ptrNewNode->iData = iData; //Load the data field of the newly created node.
ptrNewNode->ptrNext = top; // Now new node will point to the current top node.
top = ptrNewNode; //New node is now the top node of the list.
}// End of function

int fnPop()
// Purpose : This function deletes an item from the top of the stack.
// Input: None.
// Output : The item iData that has been deleted or -1 for an empty stack.
{
int iData;
StackNode *ptrDeleteNode;
if(fnEmpty() == 1) //Check the stack empty condition.
//Stack is Empty;
return -1;
else
{
iData = top->iData; // The item to be deleted.
ptrDeleteNode = top; //Set the pointer to the node to be deleted.
top = top->ptrNext; //Set the new value of top.
free(ptrDeleteNode); //Free the node that has been deleted.
return(iData); //Return the data of the deleted node.
}
}// End of Function

void fnDisplay()
// Purpose : This function displays the items of the stack.
// Input: None.
// Output : None.
{
StackNode *ptrDisplayNode;
if(fnEmpty() == 1) //Check stack empty condition.
printf("There is nothing to display\n");
else
{
ptrDisplayNode = top; //Initialize the pointer to top node.
while(ptrDisplayNode!=NULL) //Until list is empty.
{
printf("%d\n",ptrDisplayNode->iData); //Print the data.
ptrDisplayNode = ptrDisplayNode -> ptrNext; //Go to next node.
}
}
}// End of Function

int fnEmpty()
// Purpose : This function checks whether the stack is empty or not.
// Input : None.
// Output : This function returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == NULL)
return 1;
else
return 0;
}// End of Function

void main()
{
int iChoice,iData;
clrscr();
do
{
printf("1. Push\n");
printf("2. Pop\n");
printf("3. Display\n");
printf("4. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element");
scanf("%d",&iData);
fnPush(iData);
break;
case 2:
iData = fnPop();
if(iData == -1)
printf("Stack is empty\n");
else
printf("Item deleted = %d\n",iData);
break;
case 3:
printf("\n******Stack Content******\n");
fnDisplay();
printf("\n*************************\n");
break;
case 4:
exit(1);
}
}while(1);
}
/****Reverse a string using stack****/
/* File Name: REVERSE.C */
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50

void fnPush(char[],char);
char fnPop(char[]);
int top=-1;

void fnReverse(char strName[])


// Purpose : This function reverse a given string.
// Input: strName is the required string.
// Output : None.
// Comments : arrSTACK is a character array to implement the stack.
{
int iCounter = 0;
int arrSTACK[50];
while(strName[iCounter]!='\0') //Until end of string.
{
fnPush(arrSTACK, strName[iCounter]); //Push the character into the stack.
iCounter = iCounter + 1;
}
iCounter = 0;
while(fnEmpty() != 1) //Until the stack is not empty.
{
strName[iCounter] = fnPop(arrSTACK); //Pop the character and store into array strName[].
iCounter = iCounter + 1;
}
}//End of Function

void fnPush( char arrSTACK[], char cData)


//Purpose : This function inserts an item into the stack.
//Input : arrSTACK[] is the array in which the data item iData is to be inserted.
//Output : None
{
if(fnFull() == 1) // Checking for stack full condition.
printf("stack is full");
else
{
top = top + 1; // Increase the current length of the stack..
arrSTACK[top] = cData; // insert the item iData.
}
}//End of function

char fnPop(char arrSTACK[])


//Purpose : This function deletes the top element from the stack.
// Input: The array arrSTACK[] on which the stack is being implemented.
//Output : The top most item iData that has been deleted.
{
char cData;
if(fnEmpty() == 1) //Checking for stack empty condition.
{
//printf("stack is empty");
return -1;
}
else
{
cData = arrSTACK[top]; // item deleted.
top = top - 1; // Shorten the current length of the stack.
return cData; // Return the value of the item that has been deleted.
}
}//End of function

int fnFull()
// Purpose : This function checks whether the stack is full or not.
// Input: None.
// Output : This function returns TRUE if stack is full and returns FALSE if stack is not full.
{
if (top == MAXSIZE - 1)
return 1;
else
return 0;
}// End of function

int fnEmpty()
// Purpose : This function checks whether the stack is empty or not.
// Input: None.
// Output : This function returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == -1)
return 1;
else
return 0;
}// End of function

void main(void)
{
char strMyString[20];
clrscr();
printf("Enter your string");
gets(strMyString);
fnReverse(strMyString);
printf("%s",strMyString);
getch();
}
/****C code to find the postfix and prefix notation of an infix expression ****/
/* File Name: CONVERSION.C */
#include<stdio.h>
#include<conio.h>
#include<string.h>
#define MAXSIZE 50

void fnPush(char[],char);
char fnPop(char[]);
char fntop(char[]);
void fnResize(char *);
int top=-1;//Oprtstk[50];

void fnInfixToPostfix (char I[20], char P[20])


// Purpose : This function finds the postfix notation of an infix expression.
// Input: I is an arithmetic expression in infix notation. P is the equivalent postfix notation.
// Output : None.
// Comments : Oprtstk is the operator stack.
{
int iIndexI=0,iIndexP=0;
char Oprtstk[50];
char symbol,n;
while(I[iIndexI]!='\0')
{
symbol = I[iIndexI]; //Get next character of I
if(symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/')
{
while(fnPriority(fntop(Oprtstk))>=fnPriority(symbol) && !fnEmpty() &&
fntop(Oprtstk)!='(')
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
fnPush(Oprtstk , symbol);
}
else if(symbol == '(')
fnPush(Oprtstk , symbol);
else if(symbol == ')') // right parenthesis is encountered.
{
while(fntop(Oprtstk) != '(' ) //Pop from stack until left parenthesis is encountered.
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
n = fnPop(Oprtstk); //only pop left parenthesis ‘(‘. Don’t add it to P.
}
else
{
P[iIndexP] = symbol;
iIndexP++;
}
iIndexI++;
}// end of while
while(!fnEmpty())
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
P[iIndexP]='\0';
}// End of Function

void fnInfixToPrefix (char I[20], char P[20])


// Purpose : This function finds the postfix notation of an infix expression.
// Input: I is an arithmetic expression in infix notation. P is the equivalent postfix notation.
// Output : None.
// Comments : Oprtstk is the operator stack.
{
int iIndexI=0,iIndexP=0;
char Oprtstk[50];
char symbol,n,*temp;
I = strrev(I);
fnResize(I);
while(I[iIndexI]!='\0')
{
symbol = I[iIndexI]; //Get next character of I
if(symbol == '+' || symbol == '-' || symbol == '*' || symbol == '/')
{
while(fnPriority(fntop(Oprtstk))>fnPriority(symbol) && !fnEmpty() && fntop(Oprtstk)!
='(')
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
fnPush(Oprtstk , symbol);
}
else if(symbol == '(')
fnPush(Oprtstk , symbol);
else if(symbol == ')') // right parenthesis is encountered.
{
while(fntop(Oprtstk) != '(' ) //Pop from stack until left parenthesis is encountered.
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
n = fnPop(Oprtstk); //only pop left parenthesis ‘(‘. Don’t add it to P.
}
else
{
P[iIndexP] = symbol;
iIndexP++;
}
iIndexI++;
}// end of while
while(!fnEmpty())
{
n = fnPop(Oprtstk);
P[iIndexP] = n;
iIndexP++;
}
P[iIndexP]='\0';
P = strrev(P);
fnResize(I);
I = strrev(I);
}// End of Function

void fnResize(char *temp)


{
while(*temp!='\0')
{
if(*temp=='(')
*temp = ')';
else if(*temp==')')
*temp='(';
temp++;
}
}

int fnPriority(char operator1)


{
switch(operator1)
{
case '+':
case '-':
return 1;
break;
case '*':
case '/':
return 2;
break;
}
}

char fntop(char Oprtstk[])


{
return Oprtstk[top];
}

void fnPush( char arrSTACK[], char cData)


//Purpose : This function inserts an item into the stack.
//Input : arrSTACK[] is the array in which the data item iData is to be inserted.
//Output : None
{
if(fnFull() == 1) // Checking for stack full condition.
printf("stack is full");
else
{
top = top + 1; // Increase the current length of the stack..
arrSTACK[top] = cData; // insert the item iData.
}
}//End of function

char fnPop(char arrSTACK[])


//Purpose : This function deletes the top element from the stack.
// Input : The array arrSTACK[] on which the stack is being implemented.
//Output : The top most item iData that has been deleted.
{
char cData;
if(fnEmpty() == 1) //Checking for stack empty condition.
{
//printf("stack is empty");
return -1;
}
else
{
cData = arrSTACK[top]; // item deleted.
top = top - 1; // Shorten the current length of the stack.
return cData; // Return the value of the item that has been deleted.
}
}//End of function

int fnFull()
// Purpose : This function checks whether the stack is full or not.
// Input : None.
// Output : This function returns TRUE if stack is full and returns FALSE if stack is not full.
{
if (top == MAXSIZE - 1)
return 1;
else
return 0;
}// End of function

int fnEmpty()
// Purpose : This function checks whether the stack is empty or not.
// Input : None.
// Output : This function returns TRUE if stack is empty and returns FALSE if stack is not empty.
{
if (top == -1)
return 1;
else
return 0;
}// End of function

void main(void)
{
char I[20],P[20];
int iChoice;
clrscr();
do
{
printf("1. Infix to Postfix\n");
printf("2. Infix to Prefix\n");
printf("3. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the infix expression\n");
gets(I);
fnInfixToPostfix (I,P);
printf("\nEquivalent postfix expression is:%s\n",P);
break;
case 2:
printf("Enter the infix expression\n");
gets(I);
fnInfixToPrefix (I,P);
printf("Equivalent postfix expression is:%s\n",P);
break;
case 3:
exit(1);
default:
printf("Wrong Choice\n");
}
}while(1);
}
6.14 MCQ Chapter – 6

1. Stack can be implemented by


(a) Array (b) Linked List (c) Both (a) and (b) (d) none of these

2. Consider the following four statements.


(i) A stack is an ordered collection of items into which new items may be inserted and from which items
may be deleted at the one end called top of the stack.
(ii) A stack is an ordered collection of items into which new items may be inserted into an arbitrary
location.
(iii) Dynamic implementation stacks using Java language are practically impossible.
(iv) One of the stack applications is used in complier design.
Which one of the following is correct in relation to the stacks?

(a) (i) and (iv) only


(b) (i), (ii) and (iii) only
(c) (i) only
(d) (iv) only
(e) (i), (ii) and (iv) only

3. Consider the following infix expression:


(( A+B) * C – (D-E)) ^ (F + G)
Which of the following is a / are correct equivalent expression(s) for the above?
(a) ^ - * + A B C – D E F G+
(b) ^ - * + A B C – D E F+ G
(c) ^ - * + A B C – D E + F G
(d) - ^ * + A B C – D E + F G

4. Which of the following statements is correct in connection with stacks?


(a) Return and remove the most recently inserted item from the stack, if stack is not empty.
(b) Insert a new item to the top of the stack, if stack is full.
(c) Elements can be deleted from both ends.
(d) Return and remove the least recently inserted item from the stack, if stack is not empty.

5. Which of the following is a / are possible operation(s) in connection with stacks?


(a) Reverse the order of elements on stack S using two additional stacks.
(b) Reverse the order of elements on stack S using additional variables.
(c) Reverse the order of elements on stack S using one stack and some additional queues.
(d) Sort the elements of stacks using one additional stack.

Questions No. 6 and No. 7 are based on the following postfix expression S and the initial values of
the variables.
S=AB-C+DEF-+^
Assume that A=3, B=2, C=1, D=1, E=2, F=3
6. If the above S is evaluated using a stack, what is/are the intermediate value(s) on the top of the stack?
(a) 3 (b) 0 (c) 2 (d) -1

7. What would be the final output of the stack?


(a) 1 (b) 2 (c) 0 (d) -1

8. Evaluate the following prefix expression + * 2 + / 1 4 2 5 1


(a) 13
(b) 14
(c) 15
(d) 27

9. Evaluate the following prefix expression - * 6 3 – 4 1


(a) 25
(b) 15
(c) 23
(d) 12

10. Evaluate the following prefix expressions + + 2 6 + - 1 3 2 4


(a) 25
(b) 15
(c) 23
(d) 12

11. Convert the following infix expression to postfix notation (A + 2) * (B + 4) - 1


(a) A 2 + B 4 + * 1 -
(b) – 1 * + 4 B + 2 A
(c) A 2 B 4 + + X l -
(d) A 2 B 4 1 + + * -.

12. Convert the following infix expression to post fix notation Z – (((X + I) * 2) - 5)/Y
(a) Z X I + * 2 5 – / Y
(b) Z X – I + 2 * 5 – Y I -
(c) Z X I + 2 * 5 – Y / -
(d) Z X I + 2 5 * - Y /-.

13. A data structure in which an element is added and removed only from one end. is known
(a) queue
(b) stack
(c) in-built data structure
(d) None of these

14. “Get a node, store new element and insert the new node at the top" refers to insert operation in non empty
(a) stack
(c) array
(b) queue
(d) None of these.

15. A data structure in which elements are added or removed only at a fixed end is known as
(a) queue
(b) array
(c) stack
(d) Linked List.

16. One can convert an infix expression to a postfix expression using a


(a) stack (b) queue
(c) dequeue (d) none of these
17. The integers 1, 2, 3, 4 are pushed into the stack in that order. They may be popped out of the stack in any
valid order. The integers which are popped out produce a permutation of the numbers 1,2,3,4. Which of
the following permutation can never be produced in such a way?
(0) 1,2,3,4. (b) 4,3,2, 1.
(c) 4, 2, 3. 1. (d) 3, 2,4, 1.

18. Which of the following types of expressions do not require precedence rules for evaluation?
(a) Fully parenthesized infix expression
(b) Postfix expression
(c) Partially parenthesized infix expression
(d) More than one of the above

19. Consider the following statements. Which of the following is TRUE.


(a) An expression in prefix form is the reverse of the same expression in postfix form.
(b) An expression in prefix form is the reverse of the same expression in postfix form if the operators are
+, * and /.
(c) None of the above.
(d) Both (a) & (b).

20. Consider the following recursive "C" function to compute the l1-th term in the Fibonacci
sequence which is denoted by F (11).
int fib (int n)

if n(==O)
return 0;
else if (n == 1)
return 1;
else return (Fib (n-1) + fib (n-2));

}
Number of additions performed by fib (n) is :

(a) F(n + 1)
(b) F(n + 1) - 1
(c) 2n
(d) None of the above

21. The prefix expression for the following infix expression


a*(b+c)/e-f is :
(a) /*a+bc-e-f (b) -/*abcef
(e) -/* a + bce (d) none of the above

22. The following sequence of operations are performed on a stack


PUSH(10), PUSH(20), POP, PUSH(10), PUSH(20), POP, POP. POP, PUSH(20), POP
The sequence of values popped out is : (GATE-1991)
(a) 20, 10, 20, 10, 20 (b) 20, 20, 10, 10, 20
(c) 10, 20, 20, 10, 20 (d) 20, 20, 10, 20, 10

23. Σ i≤k≤n O(n), where O(n) stands for order n is : (more than 1 choice may be correct).
(a) O(n2) (b)O(n3) (c)O(3n2) (d) all of these

24. Which of the following permutation can be obtained in the output (in the same order) using a stack
assuming that the input is the sequence 1,2,3,4,5 in that order? (GATE-1994)
(a) 3, 4, 5, 1, 2 (b) 3, 4, 5, 2, 1
(e) 1, 5, 2. 3, 4 (d) 5, 4, 3, 1, 2

25. The postfix expression for the infix expression A + B * (C + D)/ F + D * E is: (GATE-1995)
(a) AB+CD+*F/D+E* (e) A*B+CD/F*DE++
(b) AB+ CD+ * F/D+E* (d) A + * BCD/F* DE++

26. Which of the following is essential for converting an infix expression to the postfix form efficiently.
(GATE-1997)
(a) An operator stack (b) An operand stack
(e) An operand stack and operator stack (d) a parse tree

27. If the sequence of operations - push (1), push (2), pop, push (1) , push (2), pop, pop, pop, push (2), pop, are
performed on a stack, the sequence of popped out values are
(a) 2, 2, 1, 1, 2 (b) 2, 2, 1, 2, 2
(c) 2, 1, 2, 2, 1 (d) 2, 1, 2, 2, 2

28. In evaluating the arithmetic expression 2 * 3 - (4 + 5), using stacks to evaluate its equivalent post-fix form,
which of the following is stack output?
(a) -3 (b) 7
(c) 3 (d) 0

29. Stack A has the entries a, b, c (with a on top). Stack B is empty. An entry popped out of stack A can be
printed immediately or pushed to stack B. An entry popped out of stack B can only be printed. In this
arrangement, which of the following permutations of a, b, c is not possible?
(a) b a c (b) b c a (c) cab (d) a b c

30. In the previous problem, if the stack A has 4 entries, then the number of possible permuta
tions will be
(a) 24 (b) 12 c) 21 (d) 14

31. Stacks cannot be used to


(a) evaluate an arithmetic expression in postfix form
(b) implement recursion
(c) convert a given arithmetic expression in infix form to its equivalent postfix form.
(d) allocate resources (like CPU) by the operating system

32. The postfix expression for the infix expression A + B * (C + D) / F + D * E is:


(a) AB + CD + * F / D + E *
(b) ABCD + *F / + DE* +
(c) A*B + CD / F*DE ++
(d) A + *BCD / F*DE ++

33. The infix priorities of + , *, ^, / could be


(a) 5, 1,2, 7 (b) 7,5,2, 1 (c) 1, 2, 5, 7 (d) 5,2,2,4

34. The expression 1 * 2 ^ 3 * 4 ^ 5 * 6 will be evaluated to


(a) 3230 (b) 16230 . (c) 49152 (d) 173458

35. Stack is useful for implementing


(a) radix sort (b) breadth first search
(c) recursion (d) depth first search

36. An item that is read as input can be either pushed to a stack and later popped and printed, or printed directly.
Which of the following will not definitely be the output if the input is the sequence of items - 1, 2 , 3 , 4 , 5?
(a) 3, 4, 5, 1, 2 (b) 3, 4, 5, 2, 1
(c) 1, 5, 2, 3, 4 (d) 5, 4, 3, 1, 2

37. Which of the following is essential for converting an infix expression to the' postfix form efficiently?
(a) An operator stack (b) An operand stack
(c) An operator stack and an operand stack (d) A parse tree

38. Which of the following can be describe as a record?


(a) Stack (b) List^
(c) List^.Info (d) Stack^.Info.

39. Which data structure would you mostly likely see in a nonrecursive implementation of a recursive
algorithm?
(a) Link list (b) Queue
(c) Stack (d) Trees.

40. Preorder is same as


(a) depth-first order (b) breadth-first order
(c) topological order (d) linear order

41. Which of the following can be describe as a pointer?


(a) Stack (b) List^
(c) List^.Info (d) Stack^.Info

42. Which of the following can be describe as a array?


(a) Stack (b) List^
(c) List^.Info (d) Stack^.Info

43. Which of the following can be describe as a integer?


(a) Stack (b) List^
(c) List^.Info (d) Stack^.Info

Solutions:
1. c 2. a 3. c 4. a 5. a 6. c 7. a 8. b 9. b 10. d 11. a 12. c 13. b 14. a 15. c
16. a 17. c 18. d 19. d 20. b 21. b 22. b 23. d 24. b 25. b 26. a 27. a 28. a 29. c 30. b
31. d 32. b 33. d 34. c 35. c,d 36. d 37. a 38. b 39. c 40. a 41. a 42. c 43. d
6.15 Exercise Chapter – 6

1. What is a stack? State one real life example for stack and show how it can be called as stack.

2. Calculate the value of the following postfix expression:


a) 5 7 8 + * 4 6 9 / + *
b) 2 4 7 - / 5 3 5 * + /
3. Convert the following infix notation to postfix expression
((X + Y) – (W + Z) * K / L ) * M

4. Convert the following postfix notation to infix expression


abc–def+*-

5. Write a C program to implement a stack using (i) Array, (ii) Linked List

6. What is Dynamic Memory allocation related to stack? write a C function using Dynamic Memory
allocation by which you can implement a Stack.

7. How can you represent a polynomial expression using stack? Give one example and show how you can
compute the result of the expression by stack.

8. How can you check a string for palindromeness by a stack? Give one example and show how you can
compute that a string is palindrome or not by stack.

9. How can you reverse a string using stack? Give one example and show how you can reverse a given
string by stack.

10. What is a multiple stack? State one application of multiple stack and show how multiple stack is
implemented there.

Chapter - 7
QUEUE
7.1 Definition
A queue is a linear data structure in which deletion can take place only at one end, called the front, and
insertions can take place only at the other end called the rear.
Rear Rear Rear
30 30 30
Rear Front
20 20 20
Rear Front
10 10 10
Front Front Front

INSERT(10) INSERT(20) INSERT(30) DELETE() DELETE()

Figure 7.1: Insertion and deletion operations in a queue

Thus here the first inserted item is deleted first. So queue is called a FIFO (First In First Out) list.

7.2 Implementation of queue using array


In this section we will create a queue of integers using array. For this purpose an integer array arrQueue of size
MAXSIZE is used and the array declaration is:
int arrQueue[MAXSIZE];
Here front and rear are two integer variables and the initial value of both is 0. From the above array declaration
the maximum value of rear may be MAXSIZE. So, rear = MAXSIZE indicates queue is full and front = rear
indicates queue is empty.

Algorithm 7.1
Algorithm for insertion into queue
1. Algorithm fnQInsertion (arrQueue[], iData)
2. // Purpose : This algorithm inserts an item in a linear queue.
3. // Input : arrQueue is the array to implement the queue. iData is the item to be inserted into the queue.
4. // Output : None.
5. // Comments : rear and front both are initialized from 0.
6. {
7. if(fnQFull() == TRUE) // Check for queue full condition.
8. Queue is full;
9. else
10. {
11. arrQueue[rear] = iData; //Insert the item iData onto queue.
12. rear = rear+1; //Extend the length of queue.
13. }
14. }// End of Algorithm

Algorithm 7.2
Algorithm for deletion from the linear queue
1. Algorithm fnQDeletion (arrQueue[])
2. // Purpose : This algorithm deletes an item from the linear queue.
3. // Input : arrQueue is the array to implement the queue.
4. // Output : The item iData that has been deleted.
5. // Comments : rear and front both are initialized from 0.
6. {
7. if(fnQEmpty() == TRUE) // Check for queue empty condition.
8. Queue is empty;
9. else
10. {
11. iData = arrQueue[front]; // item deleted.
12. front = front+1; // Shorten the length of queue.
13. return iData; //Return the data.
14. }
15. }// End of Algorithm

Algorithm 7.3
Algorithm to display elements of queue
Queue elements are in the range from front to rear of the array arrQueue[]. So elements of the array arrQueue[]
from the index front to index rear is to be displayed.
1. Algorithm fnQDisplay (arrQueue[])
2. // Purpose : This algorithm displays the elements of a linear queue.
3. // Input : arrQueue is the array to implement the queue.
4. //Output : None.
5. {
6. if (fnQEmpty() == TRUE) // Check for queue empty condition.
7. Nothing to display;
8. else
9. {
10. for(iCounter = front;iCounter<rear;iCounter++)
11. print arrQueue[iCounter];
12. }
13. }// End of Algorithm

Algorithm 7.4
Algorithm for checking the queue full condition
The queue is full if the value of rear reaches MAXSIZE i.e. the maximum size of the queue.
1. Algorithm fnQFull()
2. // Purpose : This algorithm checks whether the queue is full or not.
3. // Input : None.
4. // Output : This algorithm returns TRUE if queue is full and returns FALSE if queue is not full.
5. {
6. if (rear == MAXSIZE)
7. return TRUE;
8. else
9. return FALSE;
10. }// End of Algorithm

Algorithm 7.5
Algorithm for checking the queue empty condition
The queue is empty if the value of front reaches rear.
1. Algorithm fnQEmpty()
2. // Purpose : This algorithm checks whether the queue is empty or not.
3. // Input : None.
4. // Output : This algorithm returns TRUE if queue is empty and returns FALSE if queue is not empty.
5. {
6. if (front == rear)
7. return TRUE;
8. else
9. return FALSE;
10. }// End of Algorithm

The following example shows how the above algorithms work:


Example 7.1:
Let MAXSIZE=2.
Initially: front = rear = 0
2
1
0 front=rear

fnQInsertion(arrQueue[],10)

2
1 rear
0 10 front

fnQInsertion(arrQueue[],20)
2 rear
1 20
0 10 front

fnQInsertion(arrQueue[],30)

2 rear
1 20
0 10 front
As rear = MAXSIZE, queue is full.
So, no further insertion will take place.

fnQDeletion(arrQueue[])
2 rear
1 20 front
0 10
item deleted = 10

fnQDeletion(arrQueue[])
2 front=rear
1 20
0 10
item deleted = 20

fnQDeletion(arrQueue[])
2 front=rear
1 20
0 10
As front = rear, queue is empty.
So, no further deletion will take place.
7.3 Implementation of Queue using dynamic memory allocation
If we implement queue using array, then insertion of new data elements may cause an overflow and so the
queue full condition is to be checked during each insertion operation. But queue using dynamic memory
allocation does not demand to check the queue full condition at all in each push operation as here we can insert
any number of data elements depending upon the available memory.
Figure 6.2 shows a queue using dynamic memory allocation:

NULL

front rear
Figure 7.2: Queue using dynamic memory allocation

Here new data item is inserted at the end of the list and existing data item is deleted from the beginning of the
list. Each node of the above list has two fields: data field and pointer field. Data field contains the data element
and the pointer field contains the address of the next node in the list. So from the concept of self referential
structure the structure definition of such a node is:
struct Node
{
int iData;
struct Node *ptrNext;
};
typedef struct Node QueueNode;
We can implement the linear queue using only one pointer pointing to the first node in the list. But for
simplicity we are using two pointer variables front and rear. Here, front and rear both are QueueNode type
global pointer variable and their initial value is NULL.
QueueNode *front,*rear;
front = NULL;
rear = NULL;
The NULL value of front (or rear) indicates empty queue.

Algorithm for insertion operation


Algorithm 7.6
1. Algorithm fnQInsertion(iData)
2. // Purpose : This algorithm inserts an element into linear queue using dynamic memory allocation.
3. // Input : Value of the item iData that is to be deleted.
4. // Output : None.
5. // Comments : Initial value of both rear and front is NULL.
6. {
7. QueueNode *ptrNewNode; //Assign a pointer to new node.
8. ptrNewNode = (QueueNode*) malloc(sizeof(QueueNode)); //Create the new node.
9. ptrNewNode->iData = iData; //Load the data field of the newly created node.
10. ptrNewNode->ptrNext = NULL; // Now new node is the last node in the list.
11. if(fnQEmpty() == TRUE) // If list is empty.
12. front = rear = ptrNewNode; //Set both the value of front and rear.
13. else
14. {
15. rear->ptrNext = ptrNewNode; //Now current rear points to the new node.
16. rear = ptrNewNode; //Set the rear to new node..
17. }
18. }// End of Algorithm
Algorithm for deletion Operation
Algorithm 7.7
1. Algorithm fnQDelete()
2. // Purpose : This algorithm deletes an item from a linear queue.
3. // Input : None
4. // Output : The data item iData that has been deleted.
5. // Comments : Initial value of both rear and front is NULL.
6. {
7. QueueNode *ptrDeleteNode;
8. if(fnQEmpty() == TRUE) //Check the queue empty condition.
9. Queue is Empty;
10. else
11. {
12. iData = front->iData; //The data item to be deleted.
13. ptrDeleteNode = front; //Set the pointer to the node to be deleted.
14. front = front->ptrNext; //Set the new value of top.
15. free(ptrDeleteNode); //De-allocate memory for the node that has been deleted.
16. return(iData); //Return the data of the deleted node.
17. }
18. }// End of Algorithm

Algorithm to display the elements of linear queue using dynamic memory allocation
In this implementation, front points to the first node in the list. So to display all the elements of the queue we
have to traverse the list starting from the front node till a NULL value (i.e. end of list) is encountered. The
following algorithm describes the procedure.
Algorithm 7.8
1. Algorithm fnQDisplay()
2. // Purpose : This algorithm displays the elements of a linear queue using dynamic memory allocation.
3. // Input : None.
4. // Output : None.
5. {
6. QueueNode *ptrDisplayNode;
7. if(fnQEmpty() == TRUE) // If queue is empty.
8. There is nothing to display;
9. else
10. {
11. ptrDisplayNode = front; //Initialize the pointer to front node.
12. while(ptrDisplayNode != NULL) //Until list is empty.
13. {
14. print (ptrDisplayNode->iData); //Print the data.
15. ptrDisplayNode = ptrDisplayNode -> ptrNext; //Go to next node.
16. }
17. }
18. }// End of Algorithm

Algorithm for checking the queue empty condition


Algorithm 7.9
1. Algorithm fnQEmpty()
2. // Purpose : This algorithm checks whether the queue is empty or not.
3. // Input : None.
4. // Output : This algorithm returns TRUE if queue is empty and returns FALSE if queue is not empty.
5. {
6. if (front == NULL || rear == NULL)
7. return TRUE;
8. else
9. return FALSE;
10. }// End of Algorithm

The following example shows how the above algorithms work:


Example 7.2:
Initially: front = rear = NULL

fnQInsertion(10)

ptrNewNode = (QueueNode*) malloc(sizeof(QueueNode))

ptrNewNode

ptrNewNode->iData = 10;
10

ptrNewNode

ptrNewNode->ptrNext = NULL
10 NULL

ptrNewNode

if(rear = NULL or front = NULL) // this is true for our case


front = rear = ptrNewNode
10 NULL

front = rear
fnQDelete()

10 NULL

front
iData = front->iData = 10
ptrDeleteNode = front

10 NULL

ptrDeleteNode = front

front = front -> ptrNext = NULL


free(ptrDeleteNode)
return 10

7.4 Limitation of linear queue


5 rear
4 50
3 40 front
2
1
0

Consider the snapshot above in a linear queue. The queue can contain maximum 5 elements. Currently the
queue has only 2 elements. But no more elements can be inserted into the queue as the value of rear is 5 i.e. 3
spaces is being simply wasted. This limitation can be solved using circular queue.

7.5 Circular Queue


In a circular queue when rear (or front) reaches MAXSIZE - 1 then rear (or front) will be 0 i.e. the increment of
rear (or front) is
rear = (rear + 1) % MAX, and also
front = (front + 1) % MAX
4 50
3 40 front
2
1
0 rear

Now we can insert a new element in the array index 0.

4 .
.

2 n-1

1 0

Figure 7.3: Circular queue of n elements

Here also the initial value of both front and rear is 0.


So, queue empty condition for a circular list is front = rear.
Now suppose MAXSIZE = 3 and execute the following sequence of statements:
fnCQInsert(arrCQueue[],10)
arrCQueue[0] = 10
rear = (rear + 1)%3 =1 2
10 10 rear
front
fnCQInsert(arrCQueue[],20)
arrCQueue[1] = 20
rear = (rear + 1)%3 = 2 2 rear
1 20
0 10 front

fnCQInsert(arrCQueue[],30)
arrCQueue[2] = 30
rear = (rear + 1)%3 = 0 2 30

1 20

0 10 front rear

Now the queue is full. Unfortunately here also front = rear. So it is difficult to differentiate between the queue
full and queue empty condition. But notice one thing, if front reaches rear during the deletion operation then
obviously it indicates that the queue is empty and if rear be equal to front during the insertion operation into
queue then this means queue is full. To implement this logic two Boolean variables boolQfull (to indicate queue
is full) and boolQempty (to indicate queue is empty) are taken. Initially boolQfull is set to FALSE and
boolQempty is set to TRUE.

Algorithm for insertion into circular queue


Algorithm 7.10
1. Algorithm fnCQInsert(arrCQueue[], iData)
2. // Purpose : This algorithm inserts an item into circular queue.
3. //Input : arrCQueue is the array to implement the circular queue and iData is the item to be inserted.
4. //Output : None.
5. // Comments : Boolean variable boolQfull = TRUE indicates that queue is full.
6. {
7. if(boolQfull == TRUE) //If queue is full.
8. Queue is full;
9. else
10. {
11. arrCQueue[rear] = iData; //Insert the data.
12. rear = (rear + 1) % MAXSIZE; //Increment rear.
13. boolQempty = FALSE; //As an insertion takes place, so now queue is not empty.
14. if (rear == front)
15. boolQfull = TRUE; //if rear be equal to front then queue is full.
16. }
17. }// End of Algorithm

Algorithm for deletion into circular queue


Algorithm 7.11
1. Algorithm fnCQDelete(arrCQueue[])
2. // Purpose : This algorithm deletes an item from circular queue.
3. //Input : arrCQueue is the array to implement the circular queue.
4. //Output : This algorithm returns the item iData that has been deleted.
5. // Comments : Boolean variable boolQempty = TRUE indicates that queue is empty.
6. {
7. if(boolQempty == TRUE) //If queue is empty.
8. Queue is empty;
9. else
10. {
11. iData = arrCQueue[front] //item deleted.
12. front = (front + 1) % MAXSIZE; //Increment rear.
13. boolQfull = FALSE; //Set boolQfull to false as now queue is not full.
14. if (front == rear)
15. boolQempty = TRUE; //if front be equal to rear then queue is empty.
16. }
17. }//End of Algorithm

7.6 Deque(Double Ended Queue)


Deque: It is a linear list where items can be inserted or deleted at either end but not from the middle. That is,
here we can insert or delete elements from both the rear and front end. Hence it is also called a double ended
queue.
Deletion Insertion
10 20 5 30 20 40
Insertion Deletion
front rear
There are two types of deque due to the restrictions of performing insertion and deletion only at one end. They
are:
1) Input restricted deque: Here insertion can be done at one end of the list but allows deletion from both
the rear and front end.
2) Output restricted deque: Here deletion can be done at one end of the list but allows insertion from both
the rear and front end.
Since both insertion and deletion are performed from either end, it is necessary to design four algorithms to
perform the following four operations:
1) Insertion of new element into the rear end of the queue: This operation is similar to normal insertion
into a linear queue discussed in the section 7.2.
2) Insertion of new element into the front end of the queue: This operation is a tricky one. Here the new
element is inserted at the front end of the queue. As here a new data item is being inserted so queue full
condition is to be checked. The general logic to implement this operation is first decrement the value of front
and then insert the data item at the current position of the front. So if the value of front is 0, then no further
insertion can take place as after decrementing new value of front will be -1 which is not feasible (because array
index -1 does not exist). Hence in this case queue full condition is front = 0.
3) Deletion of existing data item from the rear end of the queue: Here first store the data value of the
index rear from the array and then decrement the value of rear. The queue empty condition is similar to linear
queue discussed in the section 7.2.
4) Deletion of existing data item from the front end of the queue: This is similar to the deletion
operation from the linear queue discussed in the section 7.2.
Algorithm for insertion into the rear end of the queue
Algorithm 7.12
1. Algorithm fnDQInsert_At_Rear(arrDeque[], iData)
2. // Purpose : This algorithm inserts an element at the rear of a queue.
3. // Input : arrDeque[] is the array to implement the queue and iData is the data item to be inserted.
4. // Output : None.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if (fnQFull() == TRUE)
8. Queue is full;
9. else
10. {
11. arrDeque[rear] = iData;
12. rear = rear + 1;
13. }
14. }//End of Algorithm

Algorithm for insertion into the front end of the queue


Algorithm 7.13
1. Algorithm fnDQInsert_At_Front(arrDeque[], iData)
2. // Purpose : This algorithm inserts an element at the front of a queue.
3. // Input : arrDeque[] is the array to implement the queue and iData is the data item to be inserted.
4. // Output : None.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if (front == 0 )
8. Queue is full;
9. else
10. {
11. front = front - 1;
12. arrDeque[front] = iData;
13. }
14. }//End of Algorithm

Algorithm for deletion from the rear end of the queue


Algorithm 7.14
1. Algorithm fnDQDelete_From_Rear(arrDeque[])
2. // Purpose : This algorithm deletes an element from the rear end of a queue.
3. // Input : arrDeque[] is the array to implement the queue.
4. // Output : The data item iData that has been deleted.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if (fnQEmpty() == TRUE)
8. Queue is empty;
9. else
10. {
11. rear = rear - 1;
12. iData = arrDeque[rear];
13. return iData;
14. }
15. }//End of Algorithm

Algorithm for deletion from the front end of the queue


Algorithm 7.15
1. Algorithm fnDQDelete_From_Front(arrDeque[])
2. // Purpose : This algorithm deletes an element from the front end of a queue.
3. // Input : arrDeque[] is the array to implement the queue.
4. // Output : The data item iData that has been deleted.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if (fnQEmpty() == TRUE)
8. Queue is empty;
9. else
10. {
11. iData = arrDeque[front];
12. front = front + 1;
13. return iData;
14. }
15. }//End of Algorithm

7.7 Priority Queue


A priority queue is a collection of elements such that each element has been assigned a priority such that the
order in which elements are deleted and processed comes from the following rules:
1. An element of higher priority is processed before any element of lower priority.
2. Two elements with same priority are processed according to the order in which they are added to the queue.
To implement the priority queue minimum two queues are needed – one to store the elements and the other to
assign the corresponding priorities.
There are two types of priority queue:
1) Ascending priority queue: Here the elements can be inserted arbitrarily but only the element with the
smallest priority can be deleted.
2) Descending priority queue: Here the elements can be inserted arbitrarily but only the element with highest
priority can be deleted.
Here we will discuss the insertion and deletion algorithm for ascending priority queue. From the properties of
ascending priority queue we can insert data in any where of the array but the data with smallest priority can be
deleted. For this purpose, during insertion first find the position of the new data element in the array according
to its priority and store the data in that position. Hence the array becomes a sorted one in ascending order
according to the priorities of the elements and hence the first data value of the array is the data item with
smallest priority which is to be deleted.

Algorithm for insertion into ascending priority queue


Algorithm 7.16
1. Algorithm fnAPQInsert(arrQueue[], arrPQueue[], iData, iPriority)
2. // Purpose : This algorithm inserts an element in an ascending priority queue.
3. // Input : arrQueue[] is the array to store the data value and arrPQueue[] is the array to store
the //corresponding priorities. iData and iPriority are the data item and its priority respectively.
4. // Output : None.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if(rear == MAXSIZE)
8. Queue is full;
9. else
10. {
11. iPosition = fnFind_Position(arrPQueue[], iPriority);
12. fnInsertion_into_Array(arrQueue[], rear, iPosition, iData);
13. fnInsertion_into_Array(arrPQueue[], rear, iPosition, iPriority);
14. rear = rear + 1;
15. }
16. }// End of Algorithm

Algorithm for deletion from the ascending priority queue


Algorithm 7.17
1. Algorithm fnAPQDelete(arrQueue[], arrPQueue[])
2. // Purpose : This algorithm inserts an element in an ascending priority queue.
3. // Input : arrQueue[] is the array to store the data value and arrPQueue[] is the array to store
the //corresponding priorities.
4. // Output : The data item iData that has been deleted.
5. // Comments : Value of both front and rear are initialized from 0.
6. {
7. if(front == rear)
8. Queue is empty;
9. else
10. {
11. iData = arrQueue[front];
12. iPriority = arrPQueue[front];
13. front = front + 1;
14. return iData;
15. }
16. }// End of Algorithm

7.8 Implementation of queue using stacks


Stack is a Last In First Out list. But queue follows the rule First In First Out. Now a queue can be implemented
using two stacks. Suppose two stacks Stack1 and Stack2 are available. Now inset the items in the first stack
Stack1. But for deletion use Stack2. First pop all the elements from Stack1 and push them into Stack2. Now the
top element of Stack2 is the element that has been inserted first into the Stack1. So now pop the top element of
Stack2 and display it. Then again pop all the elements of Stack2 and push them into Stack1 and perform the
similar kind of operation for more insertion and deletion operations. The following example describes this
process.
Suppose we have two stacks Stack1 and Stack2 with same size. Consider the following statements:
PUSH (Stack1, 10)
PUSH (Stack1, 20)
PUSH (Stack1, 30)
PUSH (Stack1, 40)
After executing the above statements, the content of Stack1 and Stack2 is shown in figure7.4.

40
30
20
10

Stack1 Stack2
Figure 7.4
Now pop all the elements from Stack1 and insert them into Stack2 (shown in figure7.5).
PUSH (Stack2, POP (Stack1)) // Pop 40 from Stack1 and Push 40 onto Stack2
PUSH (Stack2, POP (Stack1)) // Pop 30 from Stack1 and Push 30 onto Stack2
PUSH (Stack2, POP (Stack1)) // Pop 20 from Stack1 and Push 20 onto Stack2
PUSH (Stack2, POP (Stack1)) // Pop 10 from Stack1 and Push 10 onto Stack2

10
20
30
40

Stack1 Stack2
Figure 7.5
Now pop from Stack2 deletes element 10 which is first inserted element. Thus we can implement a queue.
After deleting the element from Stack2, again push all the elements of Stack2 onto Stack1 (shown in figure
7.6). That is, external Push operation is performed on Stack1 and Final Pop operation is performed from Stack2.

40
30
20
Stack1 Stack2
Figure 7.6

Algorithm 7.18
1. Algorithm fnQ_Using_Stack_Insert(arrStack1[], iData)
2. // Purpose : This algorithm inserts an item in a queue implemented using two stacks.
3. // Input : arrStack1[] is the array to implement Stack1 and iData is the data item to be inserted.
4. // Output : None
5. {
6. if (top1 == MAXSIZE-1)
7. Queue is full;
8. else
9. fnPush(arrStack1[], iData);
10. }//End of Algorithm

Algorithm 7.19
1. Algorithm fnQ_Using_Stack_Delete(arrStack1[], arrStack2[])
2. // Purpose : This algorithm inserts an item in a queue implemented using two stacks.
3. // Input : arrStack1[] and arrStack2[] are the arrays to implement Stack1 and Stack2 respectively.
4. // Output : The data item iData that has been deleted.
5. {
6. if ( top1 == -1)
7. Queue is empty;
8. else
9. {
10. while(top1 != -1)
11. fnPush(arrStack2[], fnPop(arrStack1[]));
12. iData = fnPop(arrStack2[]);
13. while(top2 != -1)
14. fnPush(arrStack1[], fnPop(arrStack2[]));
15. return iData;
16. }
17. }// End of Algorithm

7.9 Application of Queue


1) Job queue and process queue are implemented using queue for process scheduling.
2) Printer server routine is implemented using queue.
3) All types of customer service software (like Railway ticket reservation) are designed using queue for
proper service to customer.

/* Program to implement a linear queue using array */


/* QArray.c*/
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50
#define TRUE 1
#define FALSE 0
int front=0,rear=0;

//Prototype Declaration
void fnQInsertion(int[], int);
int fnQDeletion(int[]);
void fnQDisplay (int []);
int fnQFull();
int fnQEmpty();

void fnQInsertion (int arrQueue[], int iData)


// Purpose : This function inserts an item in a linear queue.
// Input: arrQueue is the array to implement the queue. iData is the item to be inserted into the queue.
// Output : None.
// Comments : rear and front both are initialized from 0.
{
if(fnQFull() == TRUE) // Check for queue full condition.
printf("\nQueue is full\n");
else
{
arrQueue[rear] = iData; //Insert the item iData onto queue.
rear = rear+1; //Extend the length of queue.
}
}// End of Function

int fnQDeletion (int arrQueue[])


// Purpose : This function deletes an item from the linear queue.
// Input : arrQueue is the array to implement the queue.
// Output : The item iData that has been deleted.
// Comments : rear and front both are initialized from 0.
{
int iData;
if(fnQEmpty() == TRUE) // Check for queue empty condition.
printf("\nQueue is empty\n");
else
{
iData = arrQueue[front]; // item deleted.
front = front+1; // Shorten the length of queue.
return iData; //Return the data.
}
}// End of Function

void fnQDisplay (int arrQueue[])


// Purpose : This function displays the elements of a linear queue.
// Input: arrQueue is the array to implement the queue.
//Output : None.
{
int iCounter;
if (fnQEmpty() == TRUE) // Check for queue empty condition.
printf("\nNothing to display\n");
else
{
for(iCounter = rear-1;iCounter>=front;iCounter--)
printf("%d\n",arrQueue[iCounter]);
}
}// End of function

int fnQFull()
// Purpose : This function checks whether the queue is full or not.
// Input: None.
// Output : This function returns TRUE if queue is full and returns FALSE if queue is not full.
{
if (rear == MAXSIZE)
return TRUE;
else
return FALSE;
}// End of function

int fnQEmpty()
// Purpose : This function checks whether the queue is empty or not.
// Input: None.
// Output : This function returns TRUE if queue is empty and returns FALSE if queue is not empty.
{
if (front == rear)
return TRUE;
else
return FALSE;
}// End of function

void main()
{
int arrQUEUE[50],iChoice,iData;
clrscr();
do
{
printf("1. Insertion\n");
printf("2. Deletion\n");
printf("3. Display\n");
printf("4. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element");
scanf("%d",&iData);
fnQInsertion(arrQUEUE,iData);
break;
case 2:
if(fnQEmpty()==TRUE)
printf("Queue is empty\n");
else
printf("Item deleted = %d\n",fnQDeletion(arrQUEUE));
break;
case 3:
printf("\n******Queue Content******\n");
fnQDisplay(arrQUEUE);
printf("\n*************************\n");
break;
case 4:
exit(1);
default:
printf("\nWrong Choice\n");
}
}while(1);
}
/* Program to implement a linear queue using linked list */
/* QLinked.c*/
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
#define MAXSIZE 50;
#define TRUE 1
#define FALSE 0
struct Node
{
int iData;
struct Node *ptrNext;
};
typedef struct Node QueueNode;

QueueNode *front=NULL,*rear=NULL;
//front = NULL;
//rear = NULL;

void fnQInsertion(int iData)


// Purpose : This function inserts an element into linear queue using dynamic memory allocation.
// Input: Value of the item iData that is to be deleted.
// Output : None.
// Comments : Initial value of both rear and front is NULL.
{
QueueNode *ptrNewNode; //Assign a pointer to new node.
ptrNewNode = (QueueNode*) malloc(sizeof(QueueNode)); //Create the new node.
ptrNewNode->iData = iData; //Load the data field of the newly created node.
ptrNewNode->ptrNext = NULL; // Now new node is the last node in the list.
if(fnQEmpty() == TRUE) // If list is empty.
front = rear = ptrNewNode; //Set both the value of front and rear.
else
{
rear->ptrNext = ptrNewNode; //Now current rear points to the new node.
rear = ptrNewNode; //Set the rear to new node..
}
}// End of Function

int fnQDelete()
// Purpose : This function deletes an item from a linear queue.
// Input: None
// Output : The data item iData that has been deleted.
// Comments : Initial value of both rear and front is NULL.
{
int iData;
QueueNode *ptrDeleteNode;
if(fnQEmpty() == TRUE) //Check the queue empty condition.
{
printf("\nQueue is Empty\n");
return 0;
}
else
{
iData = front->iData; //The data item to be deleted.
ptrDeleteNode = front; //Set the pointer to the node to be deleted.
front = front->ptrNext; //Set the new value of top.
free(ptrDeleteNode); //Deallocate memory for the node that has been deleted.
return(iData); //Return the data of the deleted node.
}
}// End of Function

void fnQDisplay()
// Purpose : This function displays the elements of a linear queue using dynamic memory allocation.
// Input: None.
// Output : None.
{
QueueNode *ptrDisplayNode;
if(fnQEmpty() == TRUE) // If queue is empty.
printf("\nNothing to display\n");
else
{
ptrDisplayNode = front; //Initialize the pointer to front node.
while(ptrDisplayNode != NULL) //Until list is empty.
{
printf("%d\n",ptrDisplayNode->iData); //Print the data.
ptrDisplayNode = ptrDisplayNode -> ptrNext; //Go to next node.
}
}
}// End of Function

int fnQEmpty()
// Purpose : This function checks whether the queue is empty or not.
// Input: None.
// Output : This function returns TRUE if queue is empty and returns FALSE if queue is not empty.
{
if (front == NULL || rear == NULL)
return TRUE;
else
return FALSE;
}// End of Function

void main()
{
int iChoice,iData;
clrscr();
do
{
printf("1. Insertion\n");
printf("2. Deletion\n");
printf("3. Display\n");
printf("4. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element");
scanf("%d",&iData);
fnQInsertion(iData);
break;
case 2:
if(fnQEmpty()==TRUE)
printf("Queue is empty\n");
else
printf("Item deleted = %d\n",fnQDelete());
break;
case 3:
printf("\n******Queue Content******\n");
fnQDisplay();
printf("\n*************************\n");
break;
case 4:
exit(1);
default:
printf("\nWrong Choice\n");
}
}while(1);
}
/* Program to implement a Priority Queue*/
/* File Name: PriorityQ.c */
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50
#define TRUE 1
#define FALSE 0
int front=0,rear=0;

//Prototype Declaration
void fnAPQInsert(int [], int [], int, int);
int fnAPQDelete(int[], int[]);
int fnFind_Position(int[], int);
void fnInsertion_into_Array(int[], int, int, int);
void fnQDisplay (int[],int[]);
int fnQFull();
int fnQEmpty();

void fnAPQInsert(int arrQueue[], int arrPQueue[], int iData, int iPriority)


// Purpose : This function inserts an element in an ascending priority queue.
// Input: arrQueue[] is the array to store the data value and arrPQueue[] is the array to store the //corresponding
priorities. iData and iPriority are the data item and its priority respectively.
// Output : None.
// Comments : Value of both front and rear are initialized from 0.
{
int iPosition;
if(fnQFull()==TRUE)
printf("\nQueue is full\n");
else
{
if(fnQEmpty()==TRUE)
iPosition = front;
else
iPosition = fnFind_Position(arrPQueue, iPriority);
fnInsertion_into_Array(arrQueue, rear, iPosition, iData);
fnInsertion_into_Array(arrPQueue, rear, iPosition, iPriority);
rear = rear + 1;
}
}// End of Function

int fnFind_Position(int arrPQueue[], int iPriority)


// Purpose : This function finds the position of the element according to priority.
// Input: arrPQueue[] is the array to store the priorities and iPriority is the priority of the new element.
// Output : Returns the position.
// Comments : Value of both front and rear are initialized from 0.
{
int iCounter;
for(iCounter = front;iCounter<rear;iCounter++)
if(arrPQueue[iCounter]>iPriority)
return iCounter;
return iCounter;
}// End of Function

void fnInsertion_into_Array(int arrData[], int n, int k, int item)


// Purpose : This function inserts an element into one dimensional array.
// Input: arrData[] is an one dimensional array with n number of elements. Element item is to be inserted into
the kth position in the array.
// Output : None.
{
int i;
for(i=n-1;i>=k;i--)
arrData [i+1]= arrData [i];
arrData[k]=item; // Insert the item.
}// End of Function

int fnAPQDelete(int arrQueue[], int arrPQueue[])


// Purpose : This function inserts an element in an ascending priority queue.
// Input: arrQueue[] is the array to store the data value and arrPQueue[] is the array to store the //corresponding
priorities.
// Output : The data item iData that has been deleted.
// Comments : Value of both front and rear are initialized from 0.
{
int iData,iPriority;
if(fnQEmpty()==TRUE)
printf("\nQueue is empty\n");
else
{
iData = arrQueue[front];
iPriority = arrPQueue[front];
front = front + 1;
return iData;
}
}// End of Function

void fnQDisplay (int arrQueue[],int arrPQueue[])


// Purpose : This function displays the elements of a linear queue.
// Input: arrQueue is the array to implement the queue.
//Output : None.
{
int iCounter;
if (fnQEmpty() == TRUE) // Check for queue empty condition.
printf("\nNothing to display\n");
else
{
for(iCounter = rear-1;iCounter>=front;iCounter--)
printf("%d\t%d\n",arrQueue[iCounter],arrPQueue[iCounter]);
}
}// End of function

int fnQFull()
// Purpose : This function checks whether the queue is full or not.
// Input: None.
// Output : This function returns TRUE if queue is full and returns FALSE if queue is not full.
{
if (rear == MAXSIZE)
return TRUE;
else
return FALSE;
}// End of function

int fnQEmpty()
// Purpose : This function checks whether the queue is empty or not.
// Input: None.
// Output : This function returns TRUE if queue is empty and returns FALSE if queue is not empty.
{
if (front == rear)
return TRUE;
else
return FALSE;
}// End of function

void main()
{
int arrQUEUE[50],arrPQueue[50],iChoice,iData,iPriority;
clrscr();
do
{
printf("****PRIORITY QUEUE****\n");
printf("1. Insertion\n");
printf("2. Deletion\n");
printf("3. Display\n");
printf("4. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element and its priority");
scanf("%d%d",&iData,&iPriority);
fnAPQInsert(arrQUEUE,arrPQueue,iData,iPriority);
break;
case 2:
if(fnQEmpty()==TRUE)
printf("Queue is empty\n");
else
printf("Item deleted = %d\n",fnAPQDelete(arrQUEUE,arrPQueue));
break;
case 3:
printf("\n******Queue Content******\n");
fnQDisplay(arrQUEUE,arrPQueue);
printf("\n*************************\n");
break;
case 4:
exit(1);
default:
printf("\nWrong Choice\n");
}
}while(1);
}
/* Program to implement a Deque*/
/* File Name: Deque.c */
/* Program to implement a deque */
/* DEQUE.c */
#include<stdio.h>
#include<conio.h>
#define MAXSIZE 50
#define TRUE 1
#define FALSE 0
int front=0,rear=0;

//Prototype Declaration
void fnDQInsert_At_Rear(int[], int);
void fnDQInsert_At_Front(int[], int);
int fnDQDelete_From_Rear(int[]);
int fnDQDelete_From_Front(int[]);
void fnQDisplay (int []);
int fnQFull();
int fnQEmpty();

void fnDQInsert_At_Rear(int arrDeque[], int iData)


// Purpose : This function inserts an element at the rear of a queue.
// Input: arrDeque[] is the array to implement the queue and iData is the data item to be inserted.
// Output : None.
// Comments : Value of both front and rear are initialized from 0.
{
if (fnQFull() == TRUE)
printf("\nQueue is full");
else
{
arrDeque[rear] = iData;
rear = rear + 1;
}
}//End of function

void fnDQInsert_At_Front(int arrDeque[], int iData)


// Purpose : This function inserts an element at the front of a queue.
// Input: arrDeque[] is the array to implement the queue and iData is the data item to be inserted.
// Output : None.
// Comments : Value of both front and rear are initialized from 0.
{
if (front == 0 )
printf("\nQueue is full\n");
else
{
front = front - 1;
arrDeque[front] = iData;
}
}//End of function

int fnDQDelete_From_Rear(int arrDeque[])


// Purpose : This function deletes an element from the rear end of a queue.
// Input: arrDeque[] is the array to implement the queue.
// Output : The data item iData that has been deleted.
// Comments : Value of both front and rear are initialized from 0.
{
int iData;
if (fnQEmpty() == TRUE)
printf("\nQueue is empty\n");
else
{
rear = rear - 1;
iData = arrDeque[rear];
return iData;
}
}//End of function

int fnDQDelete_From_Front(int arrDeque[])


// Purpose : This function deletes an element from the front end of a queue.
// Input: arrDeque[] is the array to implement the queue.
// Output : The data item iData that has been deleted.
// Comments : Value of both front and rear are initialized from 0.
{
int iData;
if (fnQEmpty() == TRUE)
printf("\nQueue is empty\n");
else
{
iData = arrDeque[front];
front = front + 1;
return iData;
}
}//End of function

void fnQDisplay (int arrQueue[])


// Purpose : This function displays the elements of a linear queue.
// Input: arrQueue is the array to implement the queue.
//Output : None.
{
int iCounter;
if (fnQEmpty() == TRUE) // Check for queue empty condition.
printf("\nNothing to display\n");
else
{
for(iCounter = rear-1;iCounter>=front;iCounter--)
printf("%d\n",arrQueue[iCounter]);
}
}// End of function

int fnQFull()
// Purpose : This function checks whether the queue is full or not.
// Input: None.
// Output : This function returns TRUE if queue is full and returns FALSE if queue is not full.
{
if (rear == MAXSIZE)
return TRUE;
else
return FALSE;
}// End of function

int fnQEmpty()
// Purpose : This function checks whether the queue is empty or not.
// Input: None.
// Output : This function returns TRUE if queue is empty and returns FALSE if queue is not empty.
{
if (front == rear)
return TRUE;
else
return FALSE;
}// End of function

void main()
{
int arrDQUEUE[50],iChoice,iData;
clrscr();
do
{
printf("1. Insert At Front\n");
printf("2. Insert At Rear\n");
printf("3. Delete From Front\n");
printf("4. Delete From Rear\n");
printf("5. Display\n");
printf("6. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the element");
scanf("%d",&iData);
fnDQInsert_At_Front(arrDQUEUE,iData);
break;
case 2:
printf("Enter the element");
scanf("%d",&iData);
fnDQInsert_At_Rear(arrDQUEUE,iData);
break;
case 3:
if(fnQEmpty()==TRUE)
printf("Queue is empty\n");
else
printf("Item deleted = %d\n",fnDQDelete_From_Front(arrDQUEUE));
break;
case 4:
if(fnQEmpty()==TRUE)
printf("Queue is empty\n");
else
printf("Item deleted = %d\n",fnDQDelete_From_Rear(arrDQUEUE));
break;
case 5:
printf("\n******Queue Content******\n");
fnQDisplay(arrDQUEUE);
printf("\n*************************\n");
break;
case 6:
exit(1);
default:
printf("\nWrong Choice\n");
}
}while(1);
}

7.10 MCQ Chapter - 7

Questions No. 1 and No. 2 are based on the following straight queue which can be allocated eight
integers and five operations.
front = 3 rear= 5
Queue = - , - , 2 , 4 ,5, - , - , -
(for notational convenience “ – “ used to denote an empty cell)
The following operations have to be performed.
(i) 6 is added to the queue.
(ii) Two elements are deleted from the queue.
(iii) 7 and 8 are added to the queue.
(iv) Two elements are deleted from the queue.
(v) 2 and 3 are added to the queue.

1. What are the final front and rear values when the above operations are performed into a straight queue?
(a) front = 7 rear=2 (b) front = 2 rear=7 (c) front = 7 rear=8 (d) front = 5 rear=8

2. What are the final front and rear values when the above operations are performed into a circular queue?
(a) front = 7 rear=3 (b) front = 2 rear=7 (c) front = 2 rear=8 (d) front = 5 rear=8

3. The initial configuration of a queue is a, b, c, d, and a is in the front end. To get the configuration d, c, b, a,
one needs a minimum of
(a) 2 deletions and 3 additions (b) 3 deletions and 2 additions
(c) 3 deletions and 3 additions (d) 3 deletions and 4 additions

4. Queue can be used to implement


(a) radix sort (b) quick sort (c) recursion (d) depth first search

5. The process of accessing data stored in a tape is similar to manipulating data on a


(a) stack (b) queue (c) list (d) heap

6. Which of the following can be describe as a pointer?


(a) Queue
(b) Queue.Rear
(c) Queue.Rear^
(d) Queue.Front^.Info.

7. Which of the following can be describe as a integer?


(a) Queue
(b) Queue.Rear
(c) Queue.Front
(d) Queue.Front^.Info.

8. Which of the following can be describe as a record?


(a) Queue.Front^.Next
(b) Queue.Rear
(c) Queue.Front^.Next
(d) Queue.Front^.Info.

9. In a queue (where q.rear and q.ront are pointers to the ends of a queue)
(a) the number of the total elements is fixed.
(b) If q.rear>q.front then queue is empty
(c) the number of elements at any time is (q.rear – q.front – 1)
(d) None of these

10. “FRONT = REAR” refers that a queue is


(a) Full
(b) Empty
(c) Circular
(d) None of these
11. N elements of a queue are to be reversed using another queue. The number of “ADD” and “REMOVE”
operations required to do so is
(a) 2*N (b) 4*N (c) N (d) the task can’t be performed

12. Queue
(a) Can be created by setting up an ordinary contigous array to hold the items
(b) can take care of delete operation automatically
(c) need a pointer to add or delete an item
(d) None of these

Solutions:
1. c 2. a 3. a 4. d 5. b 6. b 7. d 8. a 9. d 10. b 11. d 12. a

7.11 Exercise Chapter - 7

1. What is a queue? Give one practical example and show why we will call it a queue.

2. Write a C program to implement a queue using (i) Array, (ii) Linked List

3. What is Dynamic Memory allocation related to queue? write a C function using Dynamic Memory
allocation by which you can implement a Queue.

4. What is a Circular queue? why we use circular queue instead of linear queue?

5. Give short note on: (i) Dequeue


(ii) Pririty Queue

6. How can you implement a queue using stack? write a C program to implement that.

7. State one application of queue and implement it using a C Program

Chapter - 8
LINKED LIST
8.1 Introduction
In the third chapter we have discussed about array. An array is a static data structure, i.e. the total number of
spaces to keep data elements in an array whenever define in a source program remains constant during
execution time of the program. So, during execution time we can’t change the size of an array. Hence the
spaces defined for an array may be wasted during run time or more spaces may be required in execution time.
Now if we allocate memory spaces during execution time depending upon the need of user, then it will be more
flexible. From this aspect the concept of linked list is derived. Linked list is dynamic data structure.

8.2 Definition
A linked list is a dynamic linear data structure which contains a collection of data elements, called nodes, where
the linearity is maintained by means of pointer.

8.3 Advantages
1) Number of nodes in a linked list can be increased or decreased during run time of a program. That is,
linked list is dynamic in nature.
2) In this technique memory can be allocated or de-allocated whenever required. That is, memory can be
utilized efficiently.
3) Insertion and deletion operations are easier and efficient.
4) Many complex applications (like polynomial addition or multiplication) can be done more efficiently.

8.4 Disadvantages
1) Random access is not possible.
2) In each node extra memory spaces are needed to store the address of the other node.

8.5 Operations on Linked List


There are ……… basic type of operations associated with a list data structure.
1. Counting total number of nodes.
2. Insertion of a new node in the list.
3. Deletion of an existing node from the list.
4. Searching a specified node in the list.
5. Display all nodes of the list.

8.6 Types of Linked List


There are two types of linked list depending on the number of links:
1. Singly Linked list: In this list each node has only one address field to contain the address of the next
node in the list.
Node A Node B Node C Node D

12 B 15 C 18 D 20 NULL

Figure 8.1: An example of a singly linked list with four nodes

In figure 8.1 each node has two fields. First one is data field to store the information and the second field is link
field which contains the address of the next node in the list. So from the concept of self referential structure, the
structure definition for each node is:
struct Node
{
int iData; //Data value of a node.
struct node *ptrNext; //Pointer to the next node.
};
typedef struct Node SLLNode; //Rename the data type of each node as SLLNode.
2. Doubly Linked List: Here each node contains two pointer fields, one contains the address of the next
node and the other link keeps the track of the previous node in the list. Figure 8.2 shows an example of
a doubly linked list. So the structure definition of such a node is:
struct Node
{
struct Node * ptrLeft; //Pointer to the previous node.
int iData; //Data value or information.
struct Node * ptrRight; //Pointer to the next node.
};
typedef struct Node DLLNode; //Rename the data type of each node as DLLNode.

Node A Node B Node D


NULL 10 B A 20 D B 30 NULL

Figure 8.2: An example of a doubly linked list with three nodes

8.7 Singly linked list

8.7.1 Counting total number of nodes


Algorithm 8.1
1. Algorithm fnSLL_Count_Total_Nodes(ptrStart)
2. // Purpose : This algorithm counts the total number of nodes in a singly linked list.
3. // Input : ptrStart is the address of the first node in the list.
4. // Output : This algorithm returns total number of nodes iCount in the list.
5. {
6. SLLNode *ptrTemp; //Create a new pointer ptrTemp of SLLNode type.
7. ptrTemp = ptrStart; //Initialize ptrTemp.
8. iCount = 0; //Initialize counter.
9. while(ptrTemp != NULL) //Repeat steps 11 and 12 until ptrTemp reaches NULL.
10. {
11. iCount = iCount + 1; //Increment value of iCount.
12. ptrTemp = ptrTemp->ptrNext; //Advance ptrTemp to next node.
13. }
14. return(iCount); //Return total number of nodes.
15. }//End of Algorithm

How the algorithm 8.1 works:


Consider the following list:

10 20 40 NULL
ptrStart

Step 1: (Line 7) ptrTemp = ptrStart

10 20 40 NULL
ptrTemp
ptrStart

Step 2: (Line 9 to 13) ptrTemp != NULL is true. So iCount = 1 and ptrTemp is advanced by one node.

10 20 40 NULL

ptrStart ptrTemp

Step 3: (Line 9 to 13) Again ptrTemp != NULL is true. So iCount = 2 and ptrTemp is advanced by one node.

10 20 40 NULL

ptrStart ptrTemp

Step 4: (Line 9 to 13) Again ptrTemp != NULL is true. So iCount = 3 and ptrTemp is advanced by one node.

10 20 40 NULL

ptrStart ptrTemp =
NULL

Step 5: Now ptrTemp = NULL. So control goes to outside the while loop in Line 14 which returns the value of
iCount (total number of nodes).

8.7.2 Find the address of the node at specific position


Algorithm 8.2
1. Algorithm fnSLL_Find_Position(ptrStart, iPosition)
2. // Purpose : This algorithm finds the address of a specific node.
3. // Input : ptrStart is address of the first node and address of the node at iPosition in the list is required.
4. // Output : This algorithm returns the address of the node and for non existence position it returns 0.
5. {
6. SLLNode *ptrTemp; //Create a new pointer ptrTemp of SLLNode type.
7. ptrTemp = ptrStart; //Initialize ptrTemp.
8. if(iPosition<0 || iPosiotion > fnSLL_ Count_Total_Nodes(ptrStart))
9. return 0; //If position is negative or greater than total nodes then return 0.
10. for(iCounter = 1; iCounter < iPosition; iCounter++) //Go to particular node.
11. ptrTemp = ptrTemp->ptrNext;
12. return ptrTemp; //Return address.
13. }// End of Algorithm

How the algorithm 8.2 works:


Consider the following list:
10 20 40 NULL

ptrStart
Suppose we want to find the address of the third node in the list. So iPosition = 3.

Step 1: (Line 7) ptrTemp = ptrStart

10 20 40 NULL
ptrTemp
ptrStart

Step 2: (Line 8) Value of iPosition is not negative or not greater than the total nodes. So go to Line 10.

Step 3: The for loop of Line 10 executes for 2 times (1 to 2) and accordingly ptrTemp is advanced by two nodes
in Line 11.

10 20 40 NULL

ptrStart ptrTemp

Step 4: Now ptrTemp is pointing to the third node in the list. So return ptrTemp (Line 12).

8.7.3 Inserting a node


For the insertion of a node in a singly linked list, we will use a pointer variable, ptrStart to keep the track of the
first node in the list. Initially ptrStart is set to NULL to indicate that the list is empty.
Consider the singly linked list in figure 8.3. A new node ptrNewNode is to be inserted after the node ptrTemp1.
The data value of the new node is 30.

10 20 40 NULL

ptrTemp1

ptrNewNode

Figure 8.3: Insertion of a new node in a singly linked list

Algorithm 8.3
1. Algorithm fnSLL_Insert (ptrTemp1, iData)
2. //Purpose : This algorithm inserts a new node in a single linked list after a given node.
3. //Input : The new node with data value iData is to be inserted after node ptrTemp1.
4. //Output : None.
5. {
6. SLLNode *ptrNewNode; //Create a new pointer for the new node.
7. ptrNewNode = (SLLNode*) malloc(sizeof(SLLNode)); //Create the new node.
8. ptrNewNode->iData = iData; // Set the data value into new node.
9. ptrNewNode->ptrNext = ptrTemp1->ptrNext; //New node now points to next node of given node.
10. ptrTemp1->ptrNext = ptrNewNode; //Given node now points to new node.
11. }// End of algorithm.

How the algorithm 8.3 works:


Consider the list of figure 8.3.
Step 1: ptrNewNode = (SLLNode*) malloc(sizeof(SLLNode));

10 20 40 NULL
ptrTemp1

ptrNewNode

Step 2: ptrNewNode->iData = 30

10 20 40 NULL
ptrTemp1

30

ptrNewNode
Step 3: ptrNewNode->ptrNext = ptrTemp1->ptrNext;

10 20 40 NULL

ptrTemp1

30

ptrNewNode

Step 4: ptrTemp1->ptrNext = ptrNewNode;


ptrTemp1
10 20 40 NULL

30

ptrNewNode

8.7.3.1 Insert at the beginning


Algorithm 8.4
1. Algorithm fnSLL_Insert_At_Beginning (ptrStart, iData)
2. //Purpose : Insert a new node at the beginning of the list.
3. //Input : ptrStart is the address of the first node and iData is the data value of new node.
4. //Output : This algorithm returns the address of the first node (ptrStart).
5. {
6. SLLNode *ptrNewNode;
7. ptrNewNode = (SLLNode*) malloc(sizeof(SLLNode)); //Create the new node.
8. ptrNewNode->iData = iData; //Copies the data value into new node.
9. ptrNewNode->ptrNext = ptrStart; //New node now points to currently first node
10. ptrStart = ptrNewNode; //Set new node as the first node in the list.
11. return ptrStart; //Return the updated address of the first node.
12. }// End of Algorithm

How the algorithm 8.4 works.


Consider the following list:
ptrStart
20 Node2 30 NULL
Node 1 Node 2
Figure 8.4: A singly linked list of two nodes

Suppose a node with the data value 10 is to be inserted at the beginning of the list in figure 8.4. So the function
call is:
fnSLLInsert_At_Beginning(ptrStart,10)
Step 1: Create the new node.
ptrNewNode = (SLLNode *) malloc(sizeof(SLLNode));

ptrNewNode
Step 2: Copies the data value into new node.
ptrNewNode->iData = 10

10

ptrNewNode
Step 3: Link the new node to the first node of the linked list.
ptrNewNode->ptrNext = ptrStart

10 20 30 NULL

ptrNewNode ptrStart

Step 4: Set new node as the first node in the list.


ptrStart = ptrNewNode

10 20 30 NULL

ptrStart=ptrNewNode

8.7.3.2 Insert at the last of the list


Algorithm 8.5
1. Algorithm fnSLL_ Insert_At_End (ptrStart, iData)
2. // Purpose : This algorithm inserts a node at the end of the list
3. // Input : ptrStart is the address of the first node and iData is the data value.
4. // Output : None.
5. {
6. SLLNode *ptrPosition;
7. iTotalNodes = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
8. if(iTotalNodes != 0) //If list is not empty.
9. {
10. ptrPosition = fnSLL_Find_Position(ptrStart, iTotalNodes); //Get the address of the last node.
11. fnSLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
12. }
13. }//End of Algorithm

How the algorithm 8.5 works:


Consider the singly linked list of figure 8.4. Suppose a new node with data value 40 is to be inserted at the last
of the list in figure 8.4. So the function call is:
fnSLLInsert_At_End(ptrStart, 40)
Step 1: (Line 7) iTotalNodes = 2;

Step 2: (Line 10) ptrPosition = Node2

Step 3: (Line 11) fnSLL_Insert(ptrPosition, 40)

20 30 40 NULL

Node1 Node2 Node3

8.7.3.3 Insert at the specific position


Suppose we want to insert the new node at fifth position in the list. That means the new node is to be inserted
after fourth node. So first we have to find the address of the fourth node after which we can insert the new node.
Again if the list is empty or the position of the new node is 1, then the new node is to be inserted as first node.
Algorithm 8.6 describes the total procedure.
Algorithm 8.6
1. Algorithm fnSLL_Insert_At_Specific_Position (ptrStart, iData, iPosition)
2. // Purpose : This algorithm inserts a new node at a specific position of the list.
3. // Input : ptrStart = the address of the first node. iData = the data value. iPosition = the position where the
new node is to be inserted.
4. // Output : This algorithm returns the address of the first node (ptrStart).
5. {
6. SLLNode *ptrNewNode, *ptrPosition;
7. ptrPosition = fnSLL_Find_Position(ptrStart, iPosition -1); //Get the address of the node at iPosition -1.
8. iTotal = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
9. if(iPosition <= iTotal+1 && iPosition >= 0) //If position is valid.
10. {
11. if(iPosition == 1 || ptrStart == NULL) //If position is 1 or list is empty.
12. ptrStart = fnSLL_Insert_At_Beginning (ptrStart, iData); //Insert node at beginning.
13. else
14. fnSLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
15. }
16. else
17. Not a valid position;
18. return ptrStart;
19. }//End of Algorithm
8.7.4 Deleting a node
Consider the following linked list with three nodes P, Q and R. Suppose we have to delete node Q which is after
node P. Now after deletion of node Q, node R will be the next node of P. Therefore we have to store the address
of node R in the address field of node P. Algorithm 8.7 describes the deletion process.

ptrNodeP ptrNodeQ

NULL

Node P Node Q Node R

Algorithm 8.7
1. Algorithm fnSLL_Delete (ptrNodeP, ptrNodeQ)
2. //Purpose : This algorithm deletes an existing node from a single linked list.
3. //Input : Node ptrNodeQ is to be deleted which is after node ptrNodeP.
4. //Output : The data value of the node that has been deleted.
5. {
6. ptrNodeP->ptrNext = ptrNodeQ->ptrNext; // Now node P points to the node next to node Q.
7. ptrNodeQ->ptrNext = NULL //Set address field of node Q to NULL.
8. iData = ptrNodeQ->iData; //Stores the data value of the deleted node.
9. free(ptrNodeQ); //Returns the address of node Q to heap.
10. return(iData); //Return the data that has been deleted.
11. }// End of algorithm.

How the algorithm 8.7 works:


Consider the following list with three nodes:
Node P Node Q Node R
10 Q 20 R 40 NULL
ptrNodeP ptrNodeQ

Step 1: (Line 6) ptrNodeP->ptrNext = ptrNodeQ->ptrNext

Node P Node Q Node R


10 R 20 R 40 NULL
ptrNodeP ptrNodeQ

Step 2: (Line 7) ptrNodeQ->ptrNext = NULL

Node P Node Q Node R


10 R 20 NULL 40 NULL
ptrNodeP ptrNodeQ

Step 3: (Line 8) iData = 20

Step 4: (Line 9) free(ptrNodeQ)


Node P Node R
10 R 40 NULL
ptrNodeP

Step 5: (Line 10) Return 20(data value of deleted node).

8.7.4.1 Delete from the beginning


Algorithm 8.8
1. Algorithm fnSLL_Delete_From_Beginning (ptrStart)
2. // Purpose : This algorithm deletes the first node from the list.
3. // Input : ptrStart is the address of the first node in the list.
4. //Output : Returns the address of the updated first node in the list.
5. {
6. SLLNode *ptrDeletedNode;
7. if(ptrStart == NULL) //If the list is empty.
8. {
9. There is nothing to delete.
10. return;
11. }
12. ptrDeletedNode = ptrStart; //Set the pointer to first node.
13. iData = ptrDeletedNode->iData; //Stores the data value of the deleted node.
14. ptrStart = ptrStart->ptrNext; //Now the second node is the first node of the list.
15. free(ptrDeletedNode); //Return deleted node to heap.
16. return(ptrStart);
17. }//End of Algorithm

How the algorithm 8.8 works:


Consider the following list with three nodes:
Node P Node Q Node R
10 Q 20 R 40 NULL
ptrStart

Step 1: (Line 12) ptrDeletedNode = ptrStart

Node P Node Q Node R


10 Q 20 R 40 NULL
ptrDeletedNode
ptrStart

Step 2: (Line 13) iData = 10

Step 3: (Line 14) ptrStart = ptrStart->ptrNext

Node P Node Q Node R


10 Q 20 R 40 NULL
ptrDeletedNode ptrStart

Step 4: (Line 15) free(ptrDeletedNode)


Node Q Node R
20 R 40 NULL

ptrStart

Step 5: (Line 16) Return new value of ptrStart.

8.7.4.2 Delete from the last


Algorithm 8.9
1. Algorithm fnSLL_Delete_From_End (ptrStart)
2. // Purpose : This algorithm deletes the last node from the list
3. // Input : ptrStart is the address of the first node in the list.
4. // Output : None.
5. // Comments: This algorithm works if number of nodes in the list are more than one. If the list contains only
one node, call the function to delete node from the beginning of the list.
6. {
7. SLLNode *ptrNodeP, *ptrNodeQ;
8. if(ptrStart == NULL) //If list is empty.
9. {
10. There is nothing to delete.
11. return;
12. }
13. iTotal = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
14. if(iTotal > 1) //If list contains more than one nodes.
15. {
16. ptrNodeP = fnSLL_Find_Position(ptrStart, iTotal -1); //Get the address of node P.
17. ptrNodeQ = fnSLL_Find_Position(ptrStart, iTotal); //Get the address of node Q.
18. iData = fnSLL_Delete(ptrNodeP, ptrNodeQ);
19. }
20. }//End of Algorithm

How the algorithm 8.9 works:


Consider the following list with three nodes:
Node A Node P Node Q
10 P 20 Q 40 NULL
ptrStart

Step 1: (Line 13) iTotal = 3

Step 2: (Line 16) ptrNodeP = fnSLL_Find_Position(ptrStart, 2)


Node A Node P Node Q
10 P 20 Q 40 NULL
ptrStart ptrNodeP

Step 3: (Line 17) ptrNodeQ = fnSLL_Find_Position(ptrStart, 3)


Node A Node P Node Q
10 P 20 Q 40 NULL
ptrStart ptrNodeP ptrNodeQ

Step 4: (Line 18) iData = fnSLL_Delete(ptrNodeP, ptrNodeQ) = 40

Node A Node P
10 P 20 NULL

ptrStart ptrNodeP

8.7.4.3 Delete from the specific position


Algorithm 8.10
1. Algorithm fnSLL_Delete_from_Specific_Position (ptrStart, iPosition)
2. // Purpose : This algorithm deletes the last node from the list
3. // Input : ptrStart is the address of the first node in the list. iPosition is the position from where the node
is to be deleted.
4. //Output : Returns the updated address of the first node in the list.
5. {
6. SLLNode *ptrNodeP, *ptrNodeQ;
7. iTotal = fnSLL_Count_Total_Nodes(ptrStart);
8. if(iPosition <= iTotal && iPosition > 0) //If valid position.
9. {
10. if(iPosition == 1) // If node is in the first position.
11. ptrStart = fnSLL_Delete_From_Beginning (ptrStart);
12. else
13. {
14. ptrNodeP = fnSLL_Find_Position(ptrStart, iPosition -1); //Get the address of node P.
15. ptrNodeQ = fnSLL_Find_Position(ptrStart, iPosition); //Get the address of node Q.
16. iData = fnSLL_Delete(ptrNodeP, ptrNodeQ); //Delete Q and get data value of Q.
17. }
18. }
19. else
20. Not a valid position;
21. return ptrStart;
22. }//End of Algorithm

8.7.5 Display the nodes


Algorithm 8.11
1. Algorithm fnSLL_Display_Nodes(ptrStart)
2. // Purpose : This algorithm displays all the nodes in the list.
3. // Input : ptrStart is the address of the first node in the list.
4. //Output : None.
5. {
6. SLLNode *ptrTemp;
7. ptrTemp = ptrStart;
8. while(ptrTemp!=NULL)
9. {
10. print(ptrTemp->iData);
11. ptrTemp = ptrTemp->ptrNext;
12. }
13. }//End of Algorithm

8.8 Reversing a singly linked list

Algorithm 8.12
1. Algorithm fnSLL_Reverse (ptrStart)
2. // Purpose : This algorithm reverse all the nodes in the list.
3. // Input : ptrStart, the address of the first node in the list.
4. //Output : The new value of ptrStart.
5. {
6. SLLNode *ptrTemp1,*ptrTemp2,*ptrTemp3;
7. ptrTemp3 = ptrStart;
8. ptrTemp2 = NULL;
9. while( ptrTemp3 != NULL)
10. {
11. ptrTemp1 = ptrTemp2;
12. ptrTemp2 = ptrTemp3;
13. ptrTemp3 = ptrTemp3->ptrNext;
14. ptrTemp2->ptrNext = ptrTemp1;
15. }
16. ptrStart = ptrTemp2;
17. return ptrStart;
18. }// End of Algorithm

How the algorithm 8.12 works:


Suppose a linked list with three nodes is given (figure 8.5). We have to reverse this list according to the
algorithm.

10 20 30 NULL

Figure 8.5: A linked list of three nodes

Step 1: Initially ptrTemp3=ptrStart and ptrTemp2 = NULL.

ptrTemp2 = NULL

10 20 30 NULL

ptrTemp3 = ptrStart
Step 2:
Check the condition: ptrTemp3 ≠ NULL
ptrTemp1 = ptrTemp2;
ptrTemp2 = ptrTemp3;
ptrTemp3 = ptrTemp3->ptrNext;
ptrTemp2->ptrNext = ptrTemp1;

ptrTemp1 = NULL

10 NULL 20 30 NULL

ptrTemp2 ptrTemp3
ptrStart

Step 3:
Check the condition: ptrTemp3 ≠ NULL
ptrTemp1 = ptrTemp2;
ptrTemp2 = ptrTemp3;
ptrTemp3 = ptrTemp3->ptrNext;
ptrTemp2->ptrNext = ptrTemp1;

10 NULL 20 30 NULL

ptrTemp1 ptrTemp2 ptrTemp3


ptrStart

Step 4:
Check the condition: ptrTemp3 ≠ NULL
ptrTemp1 = ptrTemp2;
ptrTemp2 = ptrTemp3;
ptrTemp3 = ptrTemp3->ptrNext;
ptrTemp2->ptrNext = ptrTemp1;
ptrTemp3 = NULL

10 NULL 20 30

ptrStart ptrTemp1 ptrTemp2

Step 5:
Now set ptrStart to ptrTemp2 as currently ptrTemp2 is the first node.
ptrStart = ptrTemp2;

10 NULL 20 30

ptrStart

8.9 Polynomial using singly linked list

8.9.1 Representation of polynomial


In representation of a polynomial, there are some terms. Each term has coefficient and exponent. Now we can
define a structure for each node and whenever required we can create such new node against each term.
Therefore the structure definition looks like:
struct Polynomial
{
int iCoeff;
int iExp;
struct Polynomial *ptrNext;
};

typedef Polynomial PolyNode;

Example:
4 3 2
5A + 7A + A + 6
The linked list representation of the above polynomial equation is: -

5 4 7 3 1 2 6 0 NULL

8.9.2 Create polynomial list


We know how to append a node at the end of a singly linked list. Now to create a polynomial using linked list,
we need to append the new node at the last of the list. Algorithm 8.13 describes how to create such a
polynomial.
Algorithm 8.13
1. Algorithm fnCreate_Polynomial(ptrPoly, iCoeff, iExp)
2. // Purpose : This algorithm creates a node in a polynomial.
3. // Input : ptrPoly is name of the polynomial. iCoeff and iExp are the coefficient and exponent of the node
respectively.
4. // Output : Returns the starting address (ptrPoly) of the polynomial.
5. {
6. PolyNode *ptrTemp,*ptrNewNode ;
7. ptrTemp = ptrPoly;
8. ptrNewNode = (PolyNode*) malloc(sizeof (PolyNode)) ;
9. if ( ptrPoly == NULL )
10. ptrPoly = ptrNewNode; //Set new node as first node if list is empty.
11. else
12. {
13. while ( ptrTemp -> ptrNext != NULL ) // traverse the entire linked list.
14. ptrTemp = ptrTemp -> ptrNext;
15. ptrTemp -> ptrNext = ptrNewNode; //Set new node as the next node of ptrTemp.
16. }
17. ptrNewNode -> iCoeff = iCoeff ; // Set coefficient in the new node.
18. ptrTemp -> iExp = iExp ; // Set exponent in the new node.
19. ptrTemp -> ptrNext = NULL ; // Set link field of new node to NULL as this is the last node.
20. return ptrPoly; //Returns the address of the starting node.
21. }/ End of Algorithm
Addition of two polynomials

Algorithm 8.14
1. Algorithm fnPolyADD(ptrPolyA, ptrPolyB, ptrPolyC)
2. //Purpose : This algorithm adds two polynomials A and B and stores the result in C.
3. //Input : ptrPolyA – pointer to the first node of polynomial A.
4. // : ptrPolyB – pointer to the first node of polynomial B.
5. // : ptrPolyC – pointer to the first node of the resultant polynomial C.
6. //Output : Returns the pointer(ptrPolyC) to polynomial C.
7. {
8. PolyNode *ptrTempA, *ptrTempB;
9. ptrTempA = ptrPolyA;
10. ptrTempB = ptrPolyB;
11. while(ptrTempA != NULL && ptrTempB != NULL)
12. {
13. if(ptrTempA->iExp == ptrTempB->iExp)
14. {
15. iX = ptrTempA->iCoeff + ptrTempB->iCoeff;
16. if(iX != 0)
17. insert iX and ptrTempA->iExp (or ptrTempB->iExp) into C;
18. ptrTempA = ptrTempA->ptrNext;
19. ptrTempB = ptrTempB->ptrNext;
20. }
21. elseif(ptrTempA->iExp < ptrTempB->iExp)
22. {
23. insert ptrTempB->iCoeff and ptrTempB->iExp into C;
24. ptrTempB = ptrTempB->ptrNext;
25. }
26. else
27. {
28. insert ptrTempA->iCoeff and ptrTempA->iExp into C;
29. ptrTempA = ptrTempA->ptrNext;
30. }
31. }
32. while(ptrTempA != NULL)
33. {
34. insert ptrTempA->iCoeff and ptrTempA->iExp into C;
35. ptrTempA = ptrTempA->ptrNext;
36. }
37. while(ptrTempB != NULL)
38. {
39. insert ptrTempB->iCoeff and ptrTempB->iExp into C;
40. ptrTempB = ptrTempB->ptrNext;
41. }
42. return ptrPolyC;
43. } // End of Algorithm
Example
How the algorithm 8.14 works.
A = 3x7 + 4x4 + 5x3
B = -3x7 + 5x5 + 2x

ptrTempA = ptrPolyA
3 7 4 4 5 3 N
ptrTempB = ptrPolyB
-3 7 5 5 2 1 N

Step 1:
ptrTempA->iExp = ptrTempB->iExp;
iX = ptrTempA->iCoeff + ptrTempB->iCoeff = 3 + (-3) = 0;
As iX is 0, so no insertion into C.
ptrTempA = ptrTempA->ptrNext;
ptrTempB = ptrTempB->ptrNext;

ptrPolyA ptrTempA
3 7 4 4 5 3 N

ptrPolyB ptrTempB
-3 7 5 5 2 1 N

Step 2:
ptrTempA->iExp < ptrTempB->iExp;
insert ptrTempB->iCoeff and ptrTempB->iExp into C;
ptrTempB = ptrTempB->ptrNext;

ptrPolyA ptrTempA
3 7 4 4 5 3 N

ptrPolyB ptrTempB
-3 7 5 5 2 1 N

C
5 5 N

Step 3:
ptrTempA->iExp > ptrTempB->iExp;
insert ptrTempA->iCoeff and ptrTempA->iExp into C;
ptrTempA = ptrTempA->ptrNext;

ptrPolyA ptrTempA
3 7 4 4 5 3 N

ptrPolyB ptrTempB
-3 7 5 5 2 1 N

C
5 5 4 4 N

Step 4:
ptrTempA->iExp > ptrTempB->iExp;
insert ptrTempA->iCoeff and ptrTempA->iExp into C;
ptrTempA = ptrTempA->ptrNext;

ptrPolyA ptrTempA=NULL
3 7 4 4 5 3 NULL

ptrPolyB ptrTempB
-3 7 5 5 2 1 NULL

C
5 5 4 4 5 3 NULL

Step 5:
As ptrTempB ≠ NULL
insert ptrTempB->iCoeff and ptrTempB->iExp into C;
ptrTempB = ptrTempB->ptrNext;

ptrPolyA ptrTempA=NULL
3 7 4 4 5 3 NULL

ptrPolyB ptrTempB=NULL
-3 7 5 5 2 1 NULL

C
5 5 4 4 5 3

2 1 NULL

8.9.4 Multiplication of two polynomials

Algorithm 8.15
1. Algorithm fnPolyMUL (ptrPolyA, ptrPolyB, ptrPolyC)
2. //Purpose : This algorithm multiplies two polynomials A and B and stores the result in C.
3. //Input : ptrPolyA – pointer to the first node of polynomial A.
4. // : ptrPolyB – pointer to the first node of polynomial B.
5. // : ptrPolyC – pointer to the first node of the resultant polynomial C.
6. //Output : Returns the pointer to polynomial C ptrPolyC.
7. {
8. PolyNode *ptrTempA, *ptrTempB;
9. ptrTempA = ptrPolyA;
10. ptrTempB = ptrPolyB;
11. if(ptrPolyA == NULL) //If polynomial A is empty, then set polynomial C as B.
12. return ptrPolyB;
13. if(ptrPolyB == NULL) //If polynomial B is empty, then set polynomial C as A.
14. return ptrPolyA;
15. while ( ptrPolyA != NULL) // for each term of the first list A
16. {
17. while (ptrPolyB != NULL)
18. // multiply each term of the second list B with a term of A.
19. {
20. coeff1 = ptrTempA->iCoeff * ptrTempB->iCoeff;
21. exp1 = ptrTempA->iExp + ptrTempB->iExp;
22. insert coeff1 and exp1 into C1;
23. ptrTempB = ptrTempB->ptrNext;
24. fnPolyADD (ptrPolyC, ptrPolyC1, ptrPolyC);
25. }
26. ptrTempB = ptrPolyB; // reposition q to the starting of B.
27. ptrTempA = ptrTempA->ptrNext; // go to the next node of A.
28. return ptrPolyC;
29. }
30. }//End of Algorithm

8.10 Advantages and disadvantages of singly linked list


Advantages:
1. Accessibility of a node in the forward direction is easier.
2. Insertion and deletion of nodes are easier.

Disadvantages
1. Accessing the preceding node of a current node is not possible as there is no backward traversal.
2. Accessing a node is time consuming.

8.11 Circular linked list


If a pointer ptrP to a node in a linear list is given, we can’t reach any nodes that precede the node to which ptrP
is pointing. This is the main drawback of the linear singly linked list. We can overcome this disadvantage by
introducing another kind of list namely circular linked list. Here the pointer field of the last node contains the
address of the first node in the list instead of NULL. Hence starting from the any node of the list, we can
traverse the entire list. Figure……………….. shows a circular linked list. So we can easily convert a linear
linked list to circular linked list by replacing the NULL pointer in the last node of the list with the address of its
first node as shown in algorithm 8.16.

Figure…: Circular Linked List


Algorithm 8.16
1. Algorithm fnCreate_CLL (ptrStart)
2. // Purpose : This algorithm creates a circular linked list from a linear list.
3. // Input : ptrStatr is the address of the first node in the list.
4. // Output : None.
5. {
6. SLLNode *ptrTemp;
7. ptrTemp = ptrStart;
8. while(ptrTemp->ptrNext != NULL) //Repeat step 9 until ptrTemp reaches to last node.
9. ptrTemp = ptrTemp ->ptrNext; //Advance the value of ptrTemp to next node.
10. ptrTemp->ptrNext = ptrStart; //Sets the pointer of last node to the first node in the list.
11. }// End of algorithm

8.12 Double linked list


Although a circular linked list has advantages over a linear list, it still has several drawbacks:
(i) One can’t traverse such a list backward.
(ii) A node can’t be deleted from a circular linked list (as well as linear linked list), given only a pointer to that
node.
In cases where these facilities are required the appropriate data structure is a double linked list. The node
structure of such a list has been discussed in the section 8.6. Like singly linked list, here also we take a variable
ptrStart which contains the address of the first node in the list. Initially value of ptrStart is NULL to indicate
that the list is empty.
DLLNode *ptrStart = NULL;

8.12.1 Counting total number of nodes


Algorithm 8.17
1. Algorithm fnDLL_Count_Total_Nodes(ptrStart)
2. // Purpose : This algorithm counts the total number of nodes in a double linked list.
3. // Input : ptrStart is the address of the first node in the list.
4. // Output : This algorithm returns total number of nodes iCount in the list.
5. {
6. DLLNode *ptrTemp; //Create a new pointer ptrTemp of DLLNode type.
7. ptrTemp = ptrStart; //Initialize pointer.
8. iCount = 0; //Initialize counter.
9. while(ptrTemp != NULL) //Repeat steps 11 and 12 until ptrTemp reaches NULL.
10. {
11. iCount = iCount + 1; //Increment value of iCount.
12. ptrTemp = ptrTemp->ptrRight; //Advance ptrTemp to next node.
13. }
14. return(iCount); //Return total number of nodes.
15. }//End of Algorithm

8.12.2 Find the address of the node at specific position

Algorithm 8.18
1. Algorithm fnDLL_Find_Position(ptrStart, iPosition)
2. // Purpose : This algorithm finds the address of a specific node.
3. // Input : ptrStart is address of the first node and address of the node at iPosition in the list is required.
4. // Output : This algorithm returns the address of the desired node.
5. {
6. DLLNode *ptrTemp;
7. ptrTemp = ptrStart;
8. for(iCounter = 1; iCounter < iPosition && ptrTemp!=NULL; iCounter++)
9. ptrTemp = ptrTemp->ptrRight;
10. return ptrTemp;
11. }// End of Algorithm

8.12.3 Inserting a node into a double linked list

Consider the double linked list in figure 8.2. Suppose a new node C is to be inserted after node B. The data
value of the new node is 25.

Algorithm 8.19
1. Algorithm fnDLL_Insert (ptrNodeB, iData)
2. //Purpose : This algorithm inserts a new node C after node B in a double linked list.
3. //Input : ptrNodeB is the address of node B. iData is data value of node C.
4. //Output : None.
5. {
6. DLLNode *ptrNodeC; //ptrNodeC is the address of node C.
7. ptrNodeC = (DLLNode*) malloc(sizeof(DLLNode)); //Create node C.
8. ptrNodeC->iData = iData; // Set the data value into new node C.
9. ptrNodeC->ptrLeft = ptrNodeB; //Set B as the left node of C.
10. ptrNodeC->ptrRight = ptrNodeB->ptrRight; //Set D as the right node of C.
11. ptrNodeB->ptrRight->ptrLeft = ptrNodeC; //Set C as the right node of D.
12. ptrNodeB->ptrRight = ptrNodeC; //Set C as the left node of B.
13. }// End of algorithm.

How the algorithm 8.19 works. Consider the list of figure 8.2. Node C is to be inserted after node B.
Step 1: ptrNodeC = (DLLNode*) malloc(sizeof(DLLNode));

ptrNodeC
Step 2: ptrNodeC ->iData = 25

25

ptrNodeC
Step 3: ptrNodeC->ptrLeft = ptrNodeB;

Node A Node B Node D


NULL 10 B A 20 D B 30 NULL

Node C

B 25
ptrNodeC

Step 4: ptrNodeC->ptrRight = ptrNodeB->ptrRight;

Node A Node B Node D


NULL 10 B A 20 D B 30 NULL
ptrNodeB
Node C

B 25 D

ptrNodeC

Step 5: ptrNodeB->ptrRight->ptrLeft = ptrNodeC;

Node A Node B Node D


NULL 10 B A 20 D C 30 NULL
ptrNodeB
Node C

B 25 D

ptrNodeC

Step 6: ptrNodeB->ptrRight = ptrNodeC;

Node A Node B Node D


NULL 10 B A 20 C C 30 NULL
ptrNodeB
Node C

B 25 D

ptrNodeC

8.12.3.1 Insert at the beginning


Algorithm 8.20
1. Algorithm fnDLL_Insert_At_Beginning (ptrStart, iData)
2. //Purpose : Insert a new node P at the beginning of the list.
3. //Input : ptrStart is the address of the first node and iData is the data value of new node P.
4. //Output : This algorithm returns the address of the first node (ptrStart).
5. {
6. DLLNode *ptrNodeP; //ptrNodeP is the address of node P.
7. ptrNodeP = (DLLNode*) malloc(sizeof(DLLNode)); //Create the new node.
8. ptrNodeP->iData = iData; //Copies the data value into new node.
9. ptrNodeP->ptrLeft = NULL; //Set NULL to left link of P.
10. ptrNodeP->ptrRight = ptrStart; //Set currently first node as the left node of P.
11. ptrStart->ptrLeft = ptrNodeP; //Set P as the left node of currently first node.
12. ptrStart = ptrNodeP; //Now P is the first node in the list.
13. return ptrStart; //Return the updated address of the first node.
14. }// End of Algorithm

How the algorithm 8.20 works. Consider the list of figure 8.2. Node P is to be inserted at the beginning in the
list. Suppose data value of P is 25.

Step 1: ptrNodeP = (DLLNode*) malloc(sizeof(DLLNode));

ptrNodeP

Step 2: ptrNodeP ->iData = 25

25

ptrNodeP

Step 3: ptrNodeP->ptrLeft = NULL;

NULL 25
ptrNodeP

Step 4: ptrNodeP->ptrRight = ptrStart;

Node A Node B Node D


NULL 10 B A 20 D B 30 NULL

Node P

NULL 25 A

ptrNodeP

Step 5: ptrStart->ptrLeft = ptrNodeP;

Node A Node B Node D


P 10 B A 20 D B 30 NULL

Node P

NULL 25 A
ptrNodeP

8.12.3.2 Insert at the last of the list


Algorithm 8.21
1. Algorithm fnDLL_ Insert_At_End (ptrStart, iData)
2. // Purpose : This algorithm inserts a node P at the end of the list.
3. // Input : ptrStart is the address of the first node and iData is the data value of P.
4. // Output : None.
5. {
6. DLLNode *ptrPosition;
7. iTotalNodes = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
8. if(iTotalNodes != 0) //If list is not empty.
9. {
10. ptrPosition = fnDLL_Find_Position(ptrStart, iTotalNodes); //Get the address of the last node.
11. fnDLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
12. }
13. }//End of Algorithm

How the algorithm 8.21 works. Consider the singly linked list of figure 8.2. Suppose a new node with data
value 40 is to be inserted after node D.

Step 1: iTotalNodes = 3;

Step 2: ptrPosition = Node D

Step 3: fnDLL_Insert(ptrPosition, 40)

Node A Node B Node D


P 10 B A 20 D B 30 P

Node P

D 25 NULL

8.12.3.3 Insert at the specific position

Algorithm 8.22
1. Algorithm fnDLL_Insert_At_Specific_Position (ptrStart, iData, iPosition)
2. // Purpose : This algorithm inserts a new node at a specific position of the list.
3. // Input : ptrStart is address of the first node. iData is data value of new node. iPosition is the position
where the new node is to be inserted.
4. // Output : This algorithm returns the address of the first node (ptrStart).
5. {
6. DLLNode *ptrNewNode, *ptrPosition;
7. ptrPosition = fnDLL_Find_Position(ptrStart, iPosition -1); //Get the address of the node at position -1.
8. iTotal = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
9. if(iPosition <= iTotal+1 && iPosition >= 0) //If position is valid.
10. {
11. if(iPosition == 1 || ptrStart == NULL) //If position is 1 or list is empty.
12. ptrStart = fnDLL_Insert_At_Beginning (ptrStart, iData); //Insert node at beginning.
13. else
14. fnDLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
15. }
16. else
17. Not a valid position;
18. return ptrStart;
19. }//End of Algorithm

8.12.4 Deleting a node form a double linked list


Consider the double linked list shown in figure 8.2. Suppose node B is to be deleted. So now node A points to
node D and node D points to node A and node B no more points to any node i.e. the links of B are NULL.

(1)

Node A Node B Node D


NULL 10 D A 20 D A 30 NULL

ptrNodeA ptrNodeB ptrNodeD

(2)

For link 1: ptrNodeD->ptrLeft = ptrNodeA.


Now address of node D, ptrNodeD, is stored in the right link field of node B i.e. ptrNodeB->ptrRight =
ptrNodeD and also ptrNodeB->ptrLeft = ptrNodeA. Now we can rewrite the statement for link 1 as-

ptrNodeB->ptrRight->ptrLeft = ptrNodeB->ptrleft

For link 2: Similarly we can write for link 2-


ptrNodeB->ptrLeft->ptrRight = ptrNodeB->ptrRight

Now set the link fields of B to NULL.


ptrNodeB->ptrLeft = ptrNodeB->ptrRight = NULL

(1)

Node A Node B Node D


NULL 10 D NULL 20 NULL A 30 NULL

ptrNodeA ptrNodeB ptrNodeD


(2)
Algorithm 8.23 describes the deletion process.

Algorithm 8.23
1. Algorithm fnDLL_Delete(ptrNodeB)
2. //Purpose : This algorithm deletes an existing node B from a double linked list.
3. //Input : ptrNodeB is the address of node B.
4. //Output : The data value of the deleted node.
5. {
6. iData = ptrNodeB->iData; //Stores the data value of the deleted node.
7. ptrNodeB->ptrRight->ptrLeft = ptrNodeB->ptrleft;
8. ptrNodeB->ptrLeft->ptrRight = ptrNodeB->ptrRight;
9. ptrNodeB->ptrLeft = ptrNodeB->ptrRight = NULL;
10. free(ptrNodeB); //Returns the address of node Q to heap.
11. return(iData);
12. }// End of algorithm

8.12.4.1 Delete from the beginning


Algorithm 8.24
1. Algorithm fnDLL_Delete_From_Beginning(ptrStart)
2. // Purpose : This algorithm deletes the first node from the list.
3. // Input : ptrStart is the address of the first node in the list.
4. //Output : Returns the address of the updated first node in the list.
5. {
6. DLLNode *ptrDeletedNode;
7. if(ptrStart == NULL) //If the list is empty.
8. {
9. There is nothing to delete.
10. return;
11. }
12. ptrDeletedNode = ptrStart; //Set the pointer to first node.
13. iData = ptrStart->iData; //Stores the data value of the deleted node.
14. ptrStart = ptrStart->ptrRight; //Now the second node is the first node of the list.
15. ptrStart->ptrLeft = NULL;
16. ptrDeletedNode->ptrRight = NULL;
17. free(ptrDeletedNode); //Return deleted node to heap.
18. return(ptrStart);
19. }//End of Algorithm

8.12.4.2 Delete from the last


Algorithm 8.25
1. Algorithm fnDLL_Delete_From_End (ptrStart)
2. // Purpose : This algorithm deletes the last node from the list
3. // Input : ptrStart is the address of the first node in the list.
4. // Output : The data value of the last node.
5. // Comments: This algorithm works if number of nodes in the list are more than one. If the list contains only
one node, call the function to delete node from the beginning of the list.
6. {
7. DLLNode *ptrLastNode;
8. if(ptrStart == NULL) //If list is empty.
9. {
10. There is nothing to delete.
11. return;
12. }
13. iTotal = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
14. if(iTotal > 1) //If list contains more than one nodes.
15. {
16. ptrLastNode = fnDLL_Find_Position(ptrStart, iTotal); //Get the address of node Q.
17. ptrLastNode->ptrLeft->ptrRight = NULL; //Set NULL to right link field of second last node.
18. ptrLastNode->ptrLeft = NULL; //Set NULL to the left link field of last node.
19. iData = ptrLastNode->iData;
20. free(ptrLastNode);
21. }
22. return iData;
23. }//End of Algorithm

8.12.4.3 Delete from the specific position


Algorithm 8.26
1. Algorithm fnDLL_Delete_from_Specific_Position (ptrStart, iPosition)
2. // Purpose : This algorithm deletes the last node of the list.
3. // Input : ptrStart is the address of the first node in the list. iPosition is the position from where the node
is to be deleted.
4. //Output : Returns the updated address of first node in the list.
5. {
6. DLLNode *ptrPosition;
7. iTotal = fnDLL_Count_Total_Nodes(ptrStart);
8. if(iPosition <= iTotal && iPosition > 0) //If valid position.
9. {
10. if(iPosition == 1) //If node is in the first position.
11. ptrStart = fnDLL_Delete_From_Beginning(ptrStart);
12. elseif(iPosition == iTotal) //If the node is the last node.
13. iData = fnDLL_Delete_From_End(ptrStart);
14. else
15. {
16. ptrPosition = fnDLL_Find_Position(ptrStart, iPosition); //Get the address of the node.
17. iData = fnDLL_Delete(ptrPosition);
18. }
19. }
20. else
21. Not a valid position;
22. return ptrStart;
23. }//End of Algorithm

8.12.5 Display the nodes


Algorithm 8.27
1. Algorithm fnDLL_Display_Nodes(ptrStart)
2. // Purpose : This algorithm displays all the nodes in the list.
3. // Input : ptrStart is the address of the first node in the list.
4. //Output : None.
5. {
6. DLLNode *ptrTemp;
7. ptrTemp = ptrStart; //Initialize pointer.
8. while(ptrTemp!=NULL) //Repeat steps 10 and 11 until ptrTemp is NULL.
9. {
10. print(ptrTemp->iData); //Print the data.
11. ptrTemp = ptrTemp->ptrRight; //Advance ptrTemp to next node.
12. }
13. }//End of Algorithm

8.12.6 Advantages and disadvantages of double linked list


Advantages:
1. Both way traversal are possible here.
2. A node can be deleted from a double linked list, given only a pointer to that node.

Disadvantages
1. Here insertion is slightly more complex.

/* C code to implement the operations on a singly linked list and reverse the list*/
/* File name : SLL.c */
#include <stdio.h>
#include <conio.h>
#include <alloc.h>

struct Node
{
int iData; //Data value of a node.
struct node *ptrNext; //Pointer to the next node.
};
typedef struct Node SLLNode; //Rename the data type of each node as SLLNode.

//Prototype Declaration
int fnSLL_Count_Total_Nodes(SLLNode *);
SLLNode* fnSLL_Find_Position(SLLNode *, int );
void fnSLL_Insert (SLLNode *, int );
SLLNode *fnSLL_Insert_At_Beginning (SLLNode *, int );
void fnSLL_Insert_At_End (SLLNode *, int );
SLLNode *fnSLL_Insert_At_Specific_Position (SLLNode *, int , int);
int fnSLL_Delete (SLLNode *, SLLNode *);
SLLNode *fnSLL_Delete_From_Beginning (SLLNode *);
void fnSLL_Delete_From_End (SLLNode *);
SLLNode *fnSLL_Delete_from_Specific_Position (SLLNode *, int);
SLLNode *fnSLL_Reverse (SLLNode *);
void fnSLL_Display_Nodes(SLLNode *);
int fnSLL_Count_Total_Nodes(SLLNode *ptrStart)
// Purpose : This Function counts the total number of nodes in a singly linked list.
// Input : ptrStart is the address of the first node in the list.
// Output : This function returns total number of nodes iCount in the list.
{
int iCount;
SLLNode *ptrTemp; //Create a new pointer ptrTemp of SLLNode type.
ptrTemp = ptrStart; //Initialize ptrTemp.
iCount = 0; //Initialize counter.
while(ptrTemp != NULL) //Repeat steps 11 and 12 until ptrTemp reaches NULL.
{
iCount = iCount + 1; //Increment value of iCount.
ptrTemp = ptrTemp->ptrNext; //Advance ptrTemp to next node.
}
return(iCount); //Return total number of nodes.
}//End of Function

SLLNode* fnSLL_Find_Position(SLLNode *ptrStart, int iPosition)


// Purpose : This Functionfinds the address of a specific node.
// Input : ptrStart is address of the first node and address of the node at iPosition in the list is required.
// Output : This Function returns the address of the node and for non existence position it returns 0.
{
int iCounter;
SLLNode *ptrTemp; //Create a new pointer ptrTemp of SLLNode type.
ptrTemp = ptrStart; //Initialize ptrTemp.
if(iPosition<0 || iPosition > fnSLL_Count_Total_Nodes(ptrStart))
return 0; //If position is negative or greater than total nodes then return 0.
for(iCounter = 1; iCounter < iPosition; iCounter++) //Go to particular node.
ptrTemp = ptrTemp->ptrNext;
return ptrTemp; //Return address.
}// End of Function

void fnSLL_Insert (SLLNode *ptrTemp1, int iData)


//Purpose : This function inserts a new node in a single linked list after a given node.
//Input : The new node with data value iData is to be inserted after node ptrTemp1.
//Output : None.
{
SLLNode *ptrNewNode; //Create a new pointer for the new node.
ptrNewNode = (SLLNode*) malloc(sizeof(SLLNode)); //Create the new node.
ptrNewNode->iData = iData; // Set the data value into new node.
ptrNewNode->ptrNext = ptrTemp1->ptrNext; //New node now points to next node of given node.
ptrTemp1->ptrNext = ptrNewNode; //Given node now points to new node.
}// End of function

SLLNode *fnSLL_Insert_At_Beginning (SLLNode *ptrStart, int iData)


//Purpose : Insert a new node at the beginning of the list.
//Input : ptrStart is the address of the first node and iData is the data value of new node.
//Output : This function returns the address of the first node (ptrStart).
{
SLLNode *ptrNewNode;
ptrNewNode = (SLLNode*) malloc(sizeof(SLLNode)); //Create the new node.
ptrNewNode->iData = iData; //Copies the data value into new node.
ptrNewNode->ptrNext = ptrStart; //New node now points to currently first node
ptrStart = ptrNewNode; //Set new node as the first node in the list.
return ptrStart; //Return the updated address of the first node.
}// End of function

void fnSLL_Insert_At_End (SLLNode *ptrStart, int iData)


// Purpose : This function inserts a node at the end of the list
// Input : ptrStart is the address of the first node and iData is the data value.
// Output : None.
{
int iTotalNodes;
SLLNode *ptrPosition;
iTotalNodes = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iTotalNodes != 0) //If list is not empty.
{
ptrPosition = fnSLL_Find_Position(ptrStart, iTotalNodes); //Get the address of the last node.
fnSLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
}
}//End of function

SLLNode *fnSLL_Insert_At_Specific_Position (SLLNode *ptrStart, int iData, int iPosition)


// Purpose : This function inserts a new node at a specific position of the list.
// Input : ptrStart = the address of the first node. iData = the data value. iPosition = the position where the
new node is to be inserted.
// Output : This function returns the address of the first node (ptrStart).
{
int iTotal;
SLLNode *ptrNewNode, *ptrPosition;
ptrPosition = fnSLL_Find_Position(ptrStart, iPosition -1); //Get the address of the node at iPosition -1.
iTotal = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iPosition <= iTotal+1 && iPosition >= 0) //If position is valid.
{
if(iPosition == 1 || ptrStart == NULL) //If position is 1 or list is empty.
ptrStart = fnSLL_Insert_At_Beginning (ptrStart, iData); //Insert node at beginning.
else
fnSLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
}
else
printf("Not a valid position\n");
return ptrStart;
}//End of function

int fnSLL_Delete (SLLNode *ptrNodeP, SLLNode *ptrNodeQ)


//Purpose : This function deletes an existing node from a single linked list.
//Input : Node ptrNodeQ is to be deleted which is after node ptrNodeP.
//Output : The data value of the node that has been deleted.
{
int iData;
ptrNodeP->ptrNext = ptrNodeQ->ptrNext; // Now node P points to the node next to node Q.
ptrNodeQ->ptrNext = NULL; //Set address field of node Q to NULL.
iData = ptrNodeQ->iData; //Stores the data value of the deleted node.
free(ptrNodeQ); //Returns the address of node Q to heap.
return(iData); //Return the data that has been deleted.
}// End of function.

SLLNode *fnSLL_Delete_From_Beginning (SLLNode *ptrStart)


// Purpose : This function deletes the first node from the list.
// Input : ptrStart is the address of the first node in the list.
//Output : Returns the address of the updated first node in the list.
{
int iData;
SLLNode *ptrDeletedNode;
if(ptrStart == NULL) //If the list is empty.
{
printf("There is nothing to delete\n");
return ptrStart;
}
ptrDeletedNode = ptrStart; //Set the pointer to first node.
ptrDeletedNode->ptrNext = NULL;
iData = ptrDeletedNode->iData; //Stores the data value of the deleted node.
ptrStart = ptrStart->ptrNext; //Now the second node is the first node of the list.
free(ptrDeletedNode); //Return deleted node to heap.
return(ptrStart);
}//End of function

void fnSLL_Delete_From_End (SLLNode *ptrStart)


// Purpose : This function deletes the last node from the list
// Input : ptrStart is the address of the first node in the list.
// Output : None.
// Comments: This function works if number of nodes in the list are more than one. If the list contains only one
node, call the function to delete node from the beginning of the list.
{
int iTotal,iData;
SLLNode *ptrNodeP, *ptrNodeQ;
if(ptrStart == NULL) //If list is empty.
{
printf("There is nothing to delete\n");
return;
}
iTotal = fnSLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iTotal > 1) //If list contains more than one nodes.
{
ptrNodeP = fnSLL_Find_Position(ptrStart, iTotal -1); //Get the address of node P.
ptrNodeQ = fnSLL_Find_Position(ptrStart, iTotal); //Get the address of node Q.
iData = fnSLL_Delete(ptrNodeP, ptrNodeQ);
}
}//End of function

SLLNode *fnSLL_Delete_from_Specific_Position (SLLNode *ptrStart, int iPosition)


// Purpose : This function deletes the last node from the list
// Input : ptrStart is the address of the first node in the list. iPosition is the position from where the node
is to be deleted.
//Output : Returns the updated address of the first node in the list.
{
SLLNode *ptrNodeP, *ptrNodeQ;
int iTotal,iData;
iTotal = fnSLL_Count_Total_Nodes(ptrStart);
if(iPosition <= iTotal && iPosition > 0) //If valid position.
{
if(iPosition == 1) // If node is in the first position.
ptrStart = fnSLL_Delete_From_Beginning (ptrStart);
else
{
ptrNodeP = fnSLL_Find_Position(ptrStart, iPosition -1); //Get the address of node P.
ptrNodeQ = fnSLL_Find_Position(ptrStart, iPosition); //Get the address of node Q.
iData = fnSLL_Delete(ptrNodeP, ptrNodeQ); //Delete Q and get data value of Q.
}
}
else
printf("Not a valid position\n");
return ptrStart;
}//End of function

SLLNode *fnSLL_Reverse (SLLNode *ptrStart)


// Purpose : This function reverse all the nodes in the list.
// Input : ptrStart, the address of the first node in the list.
// Output : The new value of ptrStart.
{
SLLNode *ptrTemp1,*ptrTemp2,*ptrTemp3;
ptrTemp3 = ptrStart;
ptrTemp2 = NULL;
while( ptrTemp3 != NULL)
{
ptrTemp1 = ptrTemp2;
ptrTemp2 = ptrTemp3;
ptrTemp3 = ptrTemp3->ptrNext;
ptrTemp2->ptrNext = ptrTemp1;
}
ptrStart = ptrTemp2;
return ptrStart;
}// End of Function

void fnSLL_Display_Nodes(SLLNode *ptrStart)


// Purpose : This function displays all the nodes in the list.
// Input : ptrStart is the address of the first node in the list.
// Output : None.
{
SLLNode *ptrTemp;
ptrTemp = ptrStart;
while(ptrTemp!=NULL)
{
printf("%d\n",ptrTemp->iData);
ptrTemp = ptrTemp->ptrNext;
}
}//End of function
void main(void)
{
SLLNode *ptrStart=NULL;
int choice,iData,iPosition;
clrscr();
do
{
printf("1. Insert element at begining \n");
printf("2. Insert element at end positon\n");
printf("3. Insert at the specific position\n");
printf("4. Display\n");
printf("5. Delete from the begining\n");
printf("6. Delete from the last\n");
printf("7. Delete the element from the specific position\n");
printf("8. Reverse the list\n");
printf("9. Exit\n");
printf("Enter your choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("Enter the item\n");
scanf("%d",&iData);
ptrStart = fnSLL_Insert_At_Beginning (ptrStart, iData);
break;

case 2:
if(ptrStart!=NULL)
{
printf("Enter the item\n");
scanf("%d",&iData);
fnSLL_Insert_At_End(ptrStart, iData);
}
else
printf("For insertion into empty list choose option 1\n");
break;

case 3:
printf("Enter the specific position and the item\n " );
scanf("%d%d", &iPosition, &iData);
ptrStart = fnSLL_Insert_At_Specific_Position (ptrStart,iData,iPosition);
break;

case 4:
printf("\nThe Elements in the list are\n");
fnSLL_Display_Nodes(ptrStart);
printf("\n*****************************\n");
break;

case 5:
ptrStart = fnSLL_Delete_From_Beginning (ptrStart);
break;
case 6:
if(fnSLL_Count_Total_Nodes(ptrStart)==1)
{
printf("There is only one node in the list. Choose either option 5 or option 7\n");
break;
}
fnSLL_Delete_From_End(ptrStart);
break;
case 7:
printf("\nEnter the position of the element to be deleted\n");
scanf("%d",&iPosition);
ptrStart = fnSLL_Delete_from_Specific_Position (ptrStart,iPosition);
break;
case 8:
ptrStart = fnSLL_Reverse(ptrStart);
break;

case 9:
exit();
default:
printf("\nWrong choice\n");
}
}while(1);

/* C code to implement the operations on a Double linked list */


/* File name : DLL.c */
#include<stdio.h>
#include<conio.h>
struct Node
{
struct Node * ptrLeft; //Pointer to the previous node.
int iData; //Data value or information.
struct Node * ptrRight; //Pointer to the next node.
};
typedef struct Node DLLNode; //Rename the data type of each node as DLLNode.

//Prototype Declaration
int fnDLL_Count_Total_Nodes(DLLNode *);
DLLNode* fnDLL_Find_Position(DLLNode *, int );
void fnDLL_Insert (DLLNode *, int );
DLLNode *fnDLL_Insert_At_Beginning (DLLNode *, int );
void fnDLL_Insert_At_End (DLLNode *, int );
DLLNode *fnDLL_Insert_At_Specific_Position (DLLNode *, int , int);
int fnDLL_Delete (DLLNode *);
DLLNode *fnDLL_Delete_From_Beginning (DLLNode *);
int fnDLL_Delete_From_End (DLLNode *);
DLLNode *fnDLL_Delete_from_Specific_Position (DLLNode *, int);
void fnDLL_Display_Nodes(DLLNode *);
int fnDLL_Count_Total_Nodes(DLLNode *ptrStart)
// Purpose : This function counts the total number of nodes in a double linked list.
// Input : ptrStart is the address of the first node in the list.
// Output : This function returns total number of nodes iCount in the list.
{
int iCount=0;
DLLNode *ptrTemp; //Create a new pointer ptrTemp of DLLNode type.
ptrTemp = ptrStart; //Initialize pointer.
iCount = 0; //Initialize counter.
while(ptrTemp != NULL) //Repeat steps 11 and 12 until ptrTemp reaches NULL.
{
iCount = iCount + 1; //Increment value of iCount.
ptrTemp = ptrTemp->ptrRight; //Advance ptrTemp to next node.
}
return(iCount); //Return total number of nodes.
}//End of Function

DLLNode *fnDLL_Find_Position(DLLNode *ptrStart, int iPosition)


// Purpose : This function finds the address of a specific node.
// Input : ptrStart is address of the first node and address of the node at iPosition in the list is required.
// Output : This function returns the address of the desired node.
{
int iCounter;
DLLNode *ptrTemp;
ptrTemp = ptrStart;
for(iCounter = 1; iCounter < iPosition && ptrTemp!=NULL; iCounter++)
ptrTemp = ptrTemp->ptrRight;
return ptrTemp;
}// End of Function

void fnDLL_Insert (DLLNode *ptrNodeB, int iData)


//Purpose : This function inserts a new node C after node B in a double linked list.
//Input : ptrNodeB is the address of node B. iData is data value of node C.
//Output : None.
{
DLLNode *ptrNodeC; //ptrNodeC is the address of node C.
ptrNodeC = (DLLNode*) malloc(sizeof(DLLNode)); //Create node C.
ptrNodeC->iData = iData; // Set the data value into new node C.
ptrNodeC->ptrLeft = ptrNodeB; //Set B as the left node of C.
ptrNodeC->ptrRight = ptrNodeB->ptrRight; //Set D as the right node of C.
ptrNodeB->ptrRight->ptrLeft = ptrNodeC; //Set C as the right node of D.
ptrNodeB->ptrRight = ptrNodeC; //Set C as the left node of B.
}// End of function.

DLLNode *fnDLL_Insert_At_Beginning (DLLNode *ptrStart, int iData)


//Purpose : Insert a new node P at the beginning of the list.
//Input : ptrStart is the address of the first node and iData is the data value of new node P.
//Output : This function returns the address of the first node (ptrStart).
{
DLLNode *ptrNodeP; //ptrNodeP is the address of node P.
ptrNodeP = (DLLNode*) malloc(sizeof(DLLNode)); //Create the new node.
ptrNodeP->iData = iData; //Copies the data value into new node.
ptrNodeP->ptrLeft = NULL; //Set NULL to left link of P.
ptrNodeP->ptrRight = ptrStart; //Set currently first node as the left node of P.
ptrStart->ptrLeft = ptrNodeP; //Set P as the left node of currently first node.
ptrStart = ptrNodeP; //Now P is the first node in the list.
return ptrStart; //Return the updated address of the first node.
}// End of Function

void fnDLL_Insert_At_End(DLLNode *ptrStart, int iData)


// Purpose : This function inserts a node P at the end of the list.
// Input : ptrStart is the address of the first node and iData is the data value of P.
// Output : None.
{
int iTotalNodes;
DLLNode *ptrPosition;
iTotalNodes = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iTotalNodes != 0) //If list is not empty.
{
ptrPosition = fnDLL_Find_Position(ptrStart, iTotalNodes); //Get the address of the last node.
fnDLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
}
}//End of Function

DLLNode *fnDLL_Insert_At_Specific_Position (DLLNode *ptrStart, int iData, int iPosition)


// Purpose : This function inserts a new node at a specific position of the list.
// Input : ptrStart is address of the first node. iData is data value of new node. iPosition is the position
where the new node is to be inserted.
// Output : This function returns the address of the first node (ptrStart).
{
int iTotal;
DLLNode *ptrNewNode, *ptrPosition;
ptrPosition = fnDLL_Find_Position(ptrStart, iPosition -1); //Get the address of the node at position -1.
iTotal = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iPosition <= iTotal+1 && iPosition >= 0) //If position is valid.
{
if(iPosition == 1 || ptrStart == NULL) //If position is 1 or list is empty.
ptrStart = fnDLL_Insert_At_Beginning (ptrStart, iData); //Insert node at beginning.
else
fnDLL_Insert(ptrPosition, iData); //Insert new node after the node ptrPosition.
}
else
printf("\nNot a valid position");
return ptrStart;
}//End of Function

int fnDLL_Delete(DLLNode *ptrNodeB)


//Purpose : This function deletes an existing node B from a double linked list.
//Input : ptrNodeB is the address of node B.
//Output : The data value of the deleted node.
{
int iData;
iData = ptrNodeB->iData; //Stores the data value of the deleted node.
ptrNodeB->ptrRight->ptrLeft = ptrNodeB->ptrLeft;
ptrNodeB->ptrLeft->ptrRight = ptrNodeB->ptrRight;
ptrNodeB->ptrLeft = ptrNodeB->ptrRight = NULL;
free(ptrNodeB); //Returns the address of node Q to heap.
return(iData);
}// End of function

DLLNode *fnDLL_Delete_From_Beginning(DLLNode *ptrStart)


// Purpose : This function deletes the first node from the list.
// Input : ptrStart is the address of the first node in the list.
//Output : Returns the address of the updated first node in the list.
{
int iData;
DLLNode *ptrDeletedNode;
if(ptrStart == NULL) //If the list is empty.
{
printf("\nThere is nothing to delete");
return;
}
ptrDeletedNode = ptrStart; //Set the pointer to first node.
iData = ptrStart->iData; //Stores the data value of the deleted node.
ptrStart = ptrStart->ptrRight; //Now the second node is the first node of the list.
ptrStart->ptrLeft = NULL;
ptrDeletedNode->ptrRight = NULL;
free(ptrDeletedNode); //Return deleted node to heap.
return(ptrStart);
}//End of Function

int fnDLL_Delete_From_End (DLLNode *ptrStart)


// Purpose : This function deletes the last node from the list
// Input : ptrStart is the address of the first node in the list.
// Output : The data value of the last node.
// Comments: This function works if number of nodes in the list are more than one. If the list contains only one
node, call the function to delete node from the beginning of the list.
{
int iTotal,iData;
DLLNode *ptrLastNode;
if(ptrStart == NULL) //If list is empty.
{
printf("\nThere is nothing to delete");
return;
}
iTotal = fnDLL_Count_Total_Nodes(ptrStart); //Count total number of nodes.
if(iTotal > 1) //If list contains more than one nodes.
{
ptrLastNode = fnDLL_Find_Position(ptrStart, iTotal); //Get the address of node Q.
ptrLastNode->ptrLeft->ptrRight = NULL; //Set NULL to right link field of second last node.
ptrLastNode->ptrLeft = NULL; //Set NULL to the left link field of last node.
iData = ptrLastNode->iData;
free(ptrLastNode);
}
return iData;
}//End of Function

DLLNode *fnDLL_Delete_from_Specific_Position (DLLNode *ptrStart, int iPosition)


// Purpose : This function deletes the last node of the list.
// Input : ptrStart is the address of the first node in the list. iPosition is the position from where the node
is to be deleted.
//Output : Returns the updated address of first node in the list.
{
int iTotal,iData;
DLLNode *ptrPosition;
iTotal = fnDLL_Count_Total_Nodes(ptrStart);
if(iPosition <= iTotal && iPosition > 0) //If valid position.
{
if(iPosition == 1) //If node is in the first position.
ptrStart = fnDLL_Delete_From_Beginning(ptrStart);
else if(iPosition == iTotal) //If the node is the last node.
iData = fnDLL_Delete_From_End(ptrStart);
else
{
ptrPosition = fnDLL_Find_Position(ptrStart, iPosition); //Get the address of the node.
iData = fnDLL_Delete(ptrPosition);
}
}
else
printf("\nNot a valid position");
return ptrStart;
}//End of Function

void fnDLL_Display_Nodes(DLLNode *ptrStart)


// Purpose : This function displays all the nodes in the list.
// Input : ptrStart is the address of the first node in the list.
//Output : None.
{
DLLNode *ptrTemp;
ptrTemp = ptrStart; //Initialize pointer.
while(ptrTemp!=NULL) //Repeat steps 10 and 11 until ptrTemp is NULL.
{
printf("%d\t",ptrTemp->iData); //Print the data.
ptrTemp = ptrTemp->ptrRight; //Advance ptrTemp to next node.
}
}//End of Function

void main(void)
{
DLLNode *ptrStart=NULL;
int choice,iData,iPosition;
clrscr();
do
{
printf("1. Insert element at begining \n");
printf("2. Insert element at end positon\n");
printf("3. Insert at the specific position\n");
printf("4. Display\n");
printf("5. Delete from the begining\n");
printf("6. Delete from the last\n");
printf("7. Delete the element from the specific position\n");
printf("8. Exit\n");
printf("Enter your choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("Enter the item\n");
scanf("%d",&iData);
ptrStart = fnDLL_Insert_At_Beginning (ptrStart, iData);
break;

case 2:
if(ptrStart!=NULL)
{
printf("Enter the item\n");
scanf("%d",&iData);
fnDLL_Insert_At_End(ptrStart, iData);
}
else
printf("For insertion into empty list choose option 1\n");
break;

case 3:
printf("Enter the specific position and the item\n " );
scanf("%d%d", &iPosition, &iData);
ptrStart = fnDLL_Insert_At_Specific_Position (ptrStart,iData,iPosition);
break;

case 4:
printf("\nThe Elements in the list are\n");
fnDLL_Display_Nodes(ptrStart);
printf("\n*****************************\n");
break;

case 5:
ptrStart = fnDLL_Delete_From_Beginning (ptrStart);
break;

case 6:
if(fnDLL_Count_Total_Nodes(ptrStart)==1)
{
printf("There is only one node in the list. Choose either option 5 or option 7\n");
break;
}
fnDLL_Delete_From_End(ptrStart);
break;
case 7:
printf("\nEnter the position of the element to be deleted\n");
scanf("%d",&iPosition);
ptrStart = fnDLL_Delete_from_Specific_Position (ptrStart,iPosition);
break;
case 8:
exit(1);
default:
printf("\nWrong choice\n");
}
}while(1);

/* Program to add two polynomials maintained as linked lists. */


/* File name : PolyAdd.c */
#include <stdio.h>
#include <conio.h>
#include <alloc.h>

/* structure representing a node of a linked list. The node can store term of a polynomial */
struct Polynomial
{
int iCoeff;
int iExp;
struct Polynomial *ptrNext;
};
typedef struct Polynomial PolyNode;

PolyNode * fnCreate_Polynomial(PolyNode *, int , int);


void fnDisplayPoly ( PolyNode *);
PolyNode * fnPolyADD(PolyNode *, PolyNode *, PolyNode *);

PolyNode *fnCreate_Polynomial(PolyNode *ptrPoly, int iCoeff, int iExp)


// Purpose : This function creates a node in a polynomial.
// Input : ptrPoly is name of the polynomial. iCoeff and iExp are the coefficient and exponent of the node
respectively.
// Output : Returns the starting address (ptrPoly) of the polynomial.
{
PolyNode *ptrTemp,*ptrNewNode ;
ptrTemp = ptrPoly;
ptrNewNode = (PolyNode*) malloc(sizeof (PolyNode)) ;
if ( ptrPoly == NULL )
ptrPoly = ptrNewNode; //Set new node as first node if list is empty.
else
{
while ( ptrTemp -> ptrNext != NULL ) // traverse the entire linked list.
ptrTemp = ptrTemp -> ptrNext;
ptrTemp -> ptrNext = ptrNewNode; //Set new node as the next node of ptrTemp.
}
ptrNewNode -> iCoeff = iCoeff ; // Set coefficient in the new node.
ptrNewNode -> iExp = iExp ; // Set exponent in the new node.
ptrNewNode -> ptrNext = NULL ; // Set link field of new node to NULL as this is the last node.
return ptrPoly;
}// End of function

void fnDisplayPoly ( PolyNode *ptrPoly )


// Purpose : This function displays a polynomial.
// Input : ptrPoly is the starting address of the list.
// Output : None.
{
if(ptrPoly==NULL)
{
printf("List is empty");
return;
}
/* traverse till the end of the linked list */
while ( ptrPoly != NULL )
{
printf ( "%dx^%d + ", ptrPoly -> iCoeff, ptrPoly -> iExp ) ;
ptrPoly = ptrPoly -> ptrNext ;
}

printf ( "\b\b\b " ) ; /* erases the last colon */


}//End of Function

PolyNode *fnPolyADD(PolyNode *ptrPolyA, PolyNode *ptrPolyB, PolyNode *ptrPolyC)


//Purpose : This function adds two polynomials A and B and stores the result in C.
//Input : ptrPolyA – pointer to the first node of polynomial A.
// : ptrPolyB – pointer to the first node of polynomial B.
// : ptrPolyC – pointer to the first node of the resultant polynomial C.
//Output : Returns None.
{
int iX;
PolyNode *ptrTempA, *ptrTempB;
ptrTempA = ptrPolyA;
ptrTempB = ptrPolyB;
while(ptrTempA != NULL && ptrTempB != NULL)
{
if(ptrTempA->iExp == ptrTempB->iExp)
{
iX = ptrTempA->iCoeff + ptrTempB->iCoeff;
if(iX != 0)
ptrPolyC = fnCreate_Polynomial(ptrPolyC, iX, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
ptrTempB = ptrTempB->ptrNext;
}
else if(ptrTempA->iExp < ptrTempB->iExp)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempB->iCoeff, ptrTempB->iExp);
ptrTempB = ptrTempB->ptrNext;
}
else
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempA->iCoeff, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
}
}
while(ptrTempA != NULL)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempA->iCoeff, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
}
while(ptrTempB != NULL)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempB->iCoeff, ptrTempB->iExp);
ptrTempB = ptrTempB->ptrNext;
}
return ptrPolyC;
} // End of function

void main( )
{
PolyNode *ptrPolyA, *ptrPolyB, *ptrPolyC;
int i = 0,iChoice,iCoeff,iExp ;
ptrPolyA = ptrPolyB = ptrPolyC = NULL ; /* empty linked lists */
clrscr();
while(1)
{
printf("\n 1. Add term in first polynomial");
printf("\n 2. Show first polynomial");
printf("\n 3. Add term in second polynomial");
printf("\n 4. Show second polynomial");
printf("\n 5. Add two polynomials");
printf("\n 6. Exit");
printf("\nEnter your choice");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("\nEnter coefficient and exponent");
scanf("%d%d",&iCoeff,&iExp);
ptrPolyA = fnCreate_Polynomial(ptrPolyA,iCoeff,iExp);
break;
case 2:
fnDisplayPoly(ptrPolyA);
break;
case 3:
printf("\nEnter coefficient and exponent");
scanf("%d%d",&iCoeff,&iExp);
ptrPolyB = fnCreate_Polynomial(ptrPolyB,iCoeff,iExp);
break;
case 4:
fnDisplayPoly(ptrPolyB);
break;
case 5:
ptrPolyC=NULL;
ptrPolyC = fnPolyADD(ptrPolyA,ptrPolyB,ptrPolyC);
fnDisplayPoly (ptrPolyC) ; /* displays the resultant polynomial */
break;
case 6:
exit(1);
default:
printf("\nWrong choice");
}

}
}
/* Program to add two polynomials maintained as linked lists. */
/* File name : PolyMul.c */
#include <stdio.h>
#include <conio.h>
#include <alloc.h>

/* structure representing a node of a linked list. The node can store term of a polynomial */
struct Polynomial
{
int iCoeff;
int iExp;
struct Polynomial *ptrNext;
};
typedef struct Polynomial PolyNode;

PolyNode * fnCreate_Polynomial(PolyNode *, int , int);


void fnDisplayPoly ( PolyNode *);
PolyNode * fnPolyADD(PolyNode *, PolyNode *, PolyNode *);

PolyNode *fnCreate_Polynomial(PolyNode *ptrPoly, int iCoeff, int iExp)


// Purpose : This function creates a node in a polynomial.
// Input : ptrPoly is name of the polynomial. iCoeff and iExp are the coefficient and exponent of the node
respectively.
// Output : Returns the starting address (ptrPoly) of the polynomial.
{
PolyNode *ptrTemp,*ptrNewNode;
ptrTemp = ptrPoly;
ptrNewNode = (PolyNode*) malloc(sizeof (PolyNode)) ;
if ( ptrPoly == NULL )
ptrPoly = ptrNewNode; //Set new node as first node if list is empty.
else
{
while ( ptrTemp -> ptrNext != NULL ) // traverse the entire linked list.
ptrTemp = ptrTemp -> ptrNext;
ptrTemp -> ptrNext = ptrNewNode; //Set new node as the next node of ptrTemp.
}
ptrNewNode -> iCoeff = iCoeff ; // Set coefficient in the new node.
ptrNewNode -> iExp = iExp ; // Set exponent in the new node.
ptrNewNode -> ptrNext = NULL ; // Set link field of new node to NULL as this is the last node.
return ptrPoly;
}// End of function

void fnDisplayPoly ( PolyNode *ptrPoly )


// Purpose : This function displays a polynomial.
// Input : ptrPoly is the starting address of the list.
// Output : None.
{
if(ptrPoly==NULL)
{
printf("List is empty");
return;
}
/* traverse till the end of the linked list */
while ( ptrPoly != NULL )
{
printf ( "%dx^%d + ", ptrPoly -> iCoeff, ptrPoly -> iExp ) ;
ptrPoly = ptrPoly -> ptrNext ;
}

printf ( "\b\b\b " ) ; /* erases the last colon */


}//End of Function

PolyNode *fnPolyADD(PolyNode *ptrPolyA, PolyNode *ptrPolyB, PolyNode *ptrPolyC)


//Purpose : This function adds two polynomials A and B and stores the result in C.
//Input : ptrPolyA – pointer to the first node of polynomial A.
// : ptrPolyB – pointer to the first node of polynomial B.
// : ptrPolyC – pointer to the first node of the resultant polynomial C.
//Output : Returns None.
{
int iX;
PolyNode *ptrTempA, *ptrTempB;
ptrTempA = ptrPolyA;
ptrTempB = ptrPolyB;

while(ptrTempA != NULL && ptrTempB != NULL)


{
if(ptrTempA->iExp == ptrTempB->iExp)
{
iX = ptrTempA->iCoeff + ptrTempB->iCoeff;
if(iX != 0)
ptrPolyC = fnCreate_Polynomial(ptrPolyC, iX, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
ptrTempB = ptrTempB->ptrNext;
}
else if(ptrTempA->iExp < ptrTempB->iExp)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempB->iCoeff, ptrTempB->iExp);
ptrTempB = ptrTempB->ptrNext;
}
else
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempA->iCoeff, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
}
}
while(ptrTempA != NULL)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempA->iCoeff, ptrTempA->iExp);
ptrTempA = ptrTempA->ptrNext;
}
while(ptrTempB != NULL)
{
ptrPolyC = fnCreate_Polynomial(ptrPolyC, ptrTempB->iCoeff, ptrTempB->iExp);
ptrTempB = ptrTempB->ptrNext;
}
return ptrPolyC;
} // End of function

PolyNode *fnPolyMUL (PolyNode *ptrPolyA, PolyNode *ptrPolyB, PolyNode *ptrPolyC)


//Purpose : This function multiplies two polynomials A and B and stores the result in C.
//Input : ptrPolyA pointer to the first node of polynomial A.
// : ptrPolyB pointer to the first node of polynomial B.
// : ptrPolyC pointer to the first node of the resultant polynomial C.
//Output : Returns the pointer to polynomial C ptrPolyC.
{
PolyNode *ptrTempA, *ptrTempB,*ptrPolyC1=NULL,*ptrPolyC2;
int coeff1,exp1;
ptrTempA = ptrPolyA;
ptrTempB = ptrPolyB;
if(ptrPolyA == NULL) //If polynomial A is empty, then set polynomial C as B.
return ptrPolyB;
if(ptrPolyB == NULL) //If polynomial B is empty, then set polynomial C as A.
return ptrPolyA;
ptrPolyC1 = (PolyNode*)malloc(sizeof(PolyNode));
ptrPolyC1->ptrNext = NULL;
while ( ptrTempA != NULL) // for each term of the first list A
{
while (ptrTempB != NULL)
// multiply each term of the second list B with a term of A.
{
coeff1 = ptrTempA->iCoeff * ptrTempB->iCoeff;
exp1 = ptrTempA->iExp + ptrTempB->iExp;
ptrPolyC1->iCoeff = coeff1;
ptrPolyC1->iExp = exp1;
ptrTempB = ptrTempB->ptrNext;
ptrPolyC2 = ptrPolyC;
ptrPolyC = NULL;
ptrPolyC = fnPolyADD (ptrPolyC2, ptrPolyC1, ptrPolyC);
}
ptrTempB = ptrPolyB; // reposition q to the starting of B.
ptrTempA = ptrTempA->ptrNext; // go to the next node of A.
}
free(ptrPolyC1);
return ptrPolyC;
}//End of function

void main( void)


{
PolyNode *ptrPolyA, *ptrPolyB, *ptrPolyC;
int iChoice,iCoeff,iExp ;
ptrPolyA = ptrPolyB = ptrPolyC = NULL ; /* empty linked lists */
clrscr();
while(1)
{
printf("\n 1. Add term in first polynomial");
printf("\n 2. Show first polynomial");
printf("\n 3. Add term in second polynomial");
printf("\n 4. Show second polynomial");
printf("\n 5. Multiply two polynomials");
printf("\n 6. Exit");
printf("\nEnter your choice");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("\nEnter coefficient and exponent");
scanf("%d%d",&iCoeff,&iExp);
ptrPolyA = fnCreate_Polynomial(ptrPolyA,iCoeff,iExp);
break;
case 2:
fnDisplayPoly(ptrPolyA);
break;
case 3:
printf("\nEnter coefficient and exponent");
scanf("%d%d",&iCoeff,&iExp);
ptrPolyB = fnCreate_Polynomial(ptrPolyB,iCoeff,iExp);
break;
case 4:
fnDisplayPoly(ptrPolyB);
break;
case 5:
ptrPolyC = NULL;
ptrPolyC = fnPolyMUL(ptrPolyA,ptrPolyB,ptrPolyC);
fnDisplayPoly (ptrPolyC) ; /* displays the resultant polynomial */
break;
case 6:
exit(1);
default:
printf("\nWrong choice");
}

}
}
8.13 MCQ Chapter 8
Questions No. 1 to No. 2 are based on the following doubly linked list and its data structure.

1. What would be the correct statement(s), when the doubly linked list is empty?
(a) Head.next=tail
(b) Tail.prev=Head
(c) Current=nill
(d) All of these

2. Which of the following correctly describe(s) the steps to delete element b from the above doubly linked list?
(a) Current.next.next.prev=Current.prev, Current.next = Current.next.next
(b) Current.next = Current.prev.next, Current.next.next.prev=Current.prev
(c)Current.next=Current.next.next, Current.next.next.prev=Current.next.prev
(d) Current.next=tail, Head.next=Current.next.prev

3. In linked list, the logical order of elements


(a) is same as their physical arrangement.
(b). is determined by their physical m:rangement.
(c) can not be determined from their physical arrangement
(d) None of these.

4. Direct or random access of element is not possible in


(a) linked list (b) array
(c) string (d) None of these.

5. Odd man out (in linked list)


(a) modify (b) replace
(c) to determine if it is empty (d) None of these

6. The nth node in singly linked list, is accessed via (where n> 1)
(a) the head node (b) the tail node
(c) ( n - 1) nodes (d) None of these
7. In linked list, the successive elements
(a) must occupy contiguous space in memory
(b) need not occupy contiguous space in memory .
(c) must not occupy contiguous space in memory
(d) None of these
8. Linear order in linked list is provided through
(a) index number
(b) the implied position of the node
(c) pointer
(d) None of these

9. Null pointer is used to tell


(a) end of linked list (b) empty pointer field of a structure
(c) the linked list is empty (d) All of the above

10. List pointer variable in linked list contains address of the


(a) following node in the list (b) current node in the list
(c) first node in the list (d) None of these.

11. Underflow condition in linked list may occur when attempting to


(a) insert a new node when there is no free space for it.
(b) delete a non-existent node in the list.
(c) delete a node in empty list
(d) None of these

12. Overflow condition in linked list may occur when attempting to


(a) create a node when free space pool is empty
(b) traverse the nodes when free space pool is empty
(c) create a node when linked list is empty
(d) None of these

13. Because of linear structure of the linked list having natural linear ordering, there is similarity between linked
list and array in
(a) insertion of a node
(b) deletion of a node
(c) traversal of elements of list
(d) none of these

14. Searching a linked list requires linked list be created


(a) in sorted order only (b) in any order
(c) without underflow condition (d) None of these

15. Copying all or part of linked list consists of


(a) taking a null string and adding appropiate elements to the list one by one.
(b) taking a null string and putting the address of the beginning of the list
(c) changing only address of the address of beginning of the list.
(d) None of these.
16. Deletion of a node in linked list involves keeping track of the address of the node(a) which immediately
follows the node that is to be deleted.
(b) which immediately precedes the node that is to be deleted
(c) that is to be deleted
(d) None of these
17. Header of a linked list is a special node at the
(a) end of the linked list (b) at the middle of the linked list
(c) beginning of the linked list (d) None of these

18. Header linked list in which last node contains the null pointer is called
(a) grounded header list (b) circular header list
(c) general header list (d) None of these

19. Header linked list in which last node points to the header node is called
(a) grounded header list (b) circular header list
(c) general header list (d) None of these

20. Polynomials in memory may be maintained through


(a) linked list with header node (b) one dimensional array
(c) stack (d) None of these

21. Having address of the node to be deleted in double linked list, the node can be deleted
(a) without traversing the list
(b) only after traversing the list from the head
(c) only traversing the list (from the tail
(d) None of these.

22. Representing the polynomial in memory using linked list requires each node having
(a) two fields (b) three fields
(c) more than three fields (d) None of these

23. The n - th node in a singly linked list is accessed via


(a) The first n - 1 nodes . (b) 1 node - only the head
(c) 1 node - only tlte tail (d) the first n + 1 nodes

24. It is required to insert a node at the end of a singly connected linked list having ",," nodes. How many nodes
are to be traversed for this insertion?
(a) 1 (b) n/2
(c) II (d) none of the above

25. There could be two representations of a circular linked list. In the first representation the list is identified by
a pointer pointing to the beginning of the list as shown below.
In the second representation, the list is identified by a pointer pointing to the end of the list as shown below.
Which of the following statements is NOT true about these two representations?
(a) In the first representation, insertion at the front (i.e. before the node X1) requires traversing the list.
(b) In the first representation, insertion at the rear (i.e. after the node X3) requires traversing the list.
(c) In the second representation, insertion only at the front (and not at the rear) does not require traversing
the list.
(d) In the second representation, insertion only at the front (and not at the rear) does not require traversing
the list.
26. Linked lists are not suitable data structures for which one of the following problem
(a) insertion sort (GATE-1994)
(b) binary search
(c) radix sort
(d) polynomial manipulation.

27. Consider the following statements. State false statement. (GATE-1996)


(a) First-in-first-out types of the computations are efficiently supported by STACKS.
(b) Implementing LISTS on linked lists is more efficient than implementing Lists on an array for almost all
the basic LIST operations.
(c) Implementing QUEUES on a circular array is more efficient than implementing QUEUES on a linear
array with two indices.
(d) Last-in-Last-out types of the computations are efficiently supported by QUEUES.

28. The concatenation of two lists is to be performed in O(1) time. Which of the following implementations of a
list should be used? (GATE-1997)
(a) singly linked list (b) doubly linked list
(c) circular doubly linked list (d) array implementation of list

29. A polynomial p(x) is such that p (0) = 5, p(1) = 4, p(2) = 9 and p(3) = 20. The minimum degree
it can have is' (GATE-1997)
(a) 1 (c) 3
(b) 2 (d) 4

30. In which of the following cases, linked list implementation of sparse matrices consumes the same memory
space as the conventional way of storing the entire array? (Assume all data-types need the same amount of
storage.)
(a) 5 x 6 matrix with 9 non-zero entries (b) 5 x 6 matrix with 8 non-zero entries
(c) 6 x 5 matrix with 8 non-zero entries (d) 6 x 5 matrix with 9 non-zero entries

31. The linked list implementation of sparse matrices is superior to the generalized dope vector method because
it is
(a) conceptually easier (b) completely dynamic
(c) efficient in accessing an entry (d) efficient if the sparse matrix is a band matrix

32. In a circularly linked list organization, insertion of a record involves the modification of
(a) no pointer (b) 1 pointer (c) 2 pointers (d) 3 pointers

33. Linked lists are not suitable for implementing


(a) insertion sort (b) binary search
(c) radix sort (d) polynomial manipulation

34. The concatenation of two lists is to be performed in 0(1) time. Which of the following implementations of a
list could be used?
(a) Singly linked list (b) Doubly linked list
(c) Circular doubly linked list (d) Array implementation of list

35. Write a statement to do the following "Make B point to the last node in the list"
(a) B: = B^.Next; (b) B:=end; (c) B : = last; (d) B : = B^.Info.Key;
36. Write a statement to do the following "Make List point to an empty list"
(a) List: = empty; (b) List: = NIL; (c) List: = Zero; (d) None of these.

37. In a linked list


(a) each link contains a pointer to the next link
(b) an array of pointers point to the links
(c) each link contains data or a pointer to data
(d) the links are stored in an array.

Solutions:
1. d 2. c 3. c 4. a 5. c 6. a 7. b 8. c 9. a 10. a 11. c 12. a 13. c 14. a 15. a
16. b 17. c 18. a 19. b 20. a 21. a 22. b 23. b 24. d 25. c 26. b 27. d 28. c 29. c 30. c
31. a,b 32. c 33. b 34. c 35. d 36. d 37. a

8.13 Exercise Chapter 8

1. What is a single Linked List? What are the advantages and disadvantages of single Linked List?

2. Consider a single Linked List contains the following elements:


roll number: integer, name: string of max 25 char, avg no: float
represent a single linked link with the elements above in C language.

3. Write down C functions to: (i) insert an element into (a) beginning of a single linked list
(b) middle position of a single linked list
(ii) delete an element from (a) beginning of a single linked list
(b) middle position of a single linked list

4. Write an algorithm and a C function to reverse a single linked list.

5. How can you represent a polynomial using single linked list. State an polynomial example and show how
can you represent the polynomial using linked list using a C program.

6. What is a Double Linked List? What are the advantages and disadvantages of Double Linked List?

7. Consider a Double Linked List contains the following elements:


roll number: integer, name: string of max 25 char, avg no: float
represent a double linked link with the elements above in C language.

8. Write down C functions to: (i) insert an element into (a) beginning of a double linked list
(b) middle position of a double linked list
(ii) delete an element from (a) beginning of a double linked list
(b) middle position of a double linked list

9. Define and differentiate circular and double linked list.

CHAPTER – 9
RECURSION
9.1 Definition
Recursion means process of solving a problem by reducing it to smaller versions of itself. More precisely,
recursion is a technique that allows us to break down a problem into one or more sub problems that are similar
in form of original problem. It is a method for solving those problems which fall into the divide and conquer
paradigm.
Suppose P is a procedure containing either a call statement to itself or a call statement to a second
procedure that may eventually result in a call statement back to the original procedure P. Then P is called a
recursive procedure. So that the program will not continue to run indefinitely, a recursive procedure must have
the following two properties:
i) There must be certain criteria called base criteria for which the procedure does not call itself.
ii) Each time the procedure does call itself (directly or indirectly) it must be closer to the base
criteria.
A recursive procedure with these two properties is said to be well-defined.
The recursive programs though are small and easy to write but are not efficient in terms of the time and
space complexity.
Recursion may be implemented by means of stack..

9.2 Types of recursion


Recursion is of two types depending on whether a function calls itself from itself or whether two functions call
one another mutually. The former is called direct recursion and latter is called indirect recursion. Thus two
types of recursions are:-
(i) Direct recursion
(ii) Indirect recursion
Both types of recursions are shown diagrammatically below: -

fn1() fn1()
{ {
−−−−−− ; −−−−−− ;
fn1(); −−−−−− ;
} fn2();
}
fn2()
{
fn1();
−−−−−− ;
}

(a) Direct recursion (b) Indirect recursion

Figure 9.1: Examples of direct and indirect recursion

Depending on the number of recursive calls recursion can be further categorized as:
(a) Linear recursion (b) Binary recursion (c) Multiple recursion

9.2.1 Linear recursion


Linear recursion can be defined as follows:
(I) It performs a single recursive call. If there are more than one recursive calls in a procedure then
only one of them should be chosen based on some tests or criteria.
(II) Define each recursive call, so that it makes progress to base case.
9.2.2 Binary recursion
Binary recursion can be defined as follows:
(I) It performs two recursive calls for each non-base case.
(II) Define each recursive call, so that it makes progress to base case.

9.2.3 Multiple recursion


Multiple recursion can be defined as follows:
(I) It performs many recursive calls.
(II) Define each recursive call, so that it makes progress to base case.

9.3 Some recursive algorithms


In this section we will go through some recursive algorithms. But at first we will see how a recursive algorithm
is written. Suppose a problem has the following recursive definition:
F(x) = c for G(x) ------------- (1)
= F(H(x)) for other values of x -------------(2)
For the above problem condition 1 is the base case and condition 2 implies recursive call. So for x=G(x) return
the value c to the calling function and for the other values of x calls the same function with the new argument
H(x).
Algorithm fnF(x)
{
if(x==G(x))
return c;
else
fnF(H(x);
}// End of Algorithm
Using the above said concept we can implement any recursive function for any recursive solution of a problem.

9.3.1 Factorial function


The recursive definition to calculate the factorial value of a number is as follows:
Fact (n) = 1 for n=0 and n=1
= n*Fact (n-1) for n>1
Algorithm 9.1
1. Algorithm fnFact (n)
2. // Purpose : This algorithm calculates the factorial of a positive number.
3. // Input : n is the given number.
4. // Output : This function returns -1 for negative value of n, otherwise return factorial of n.
5. {
6. if(n<0)
7. return -1;
8. if(n == 1 || n == 0 ) // base criteria
9. return 1;
10. else
11. return (n*fnFact(n-1)); // recursive call
12. }// End of Algorithm
In the algorithm 9.1, line 6 checks for a negative n. If n is negative then line 7 returns -1. Line 8 returns 1 if n is
0 or 1. Otherwise line 11 solves the problem recursively. Example 9.1 shows how the algorithm works.

Example 9.1: Let us calculate the value of 4! using the above recursive algorithm:
fnFact(4) = 4* fnFact(3)
fnFact(3) = 3*fnFact(2)
fnFact(2) = 2*fnFact(1)
fnFact(1) = 1
fnFact(2) = 2*1 = 2
fnFact(3) = 3*2 = 6
fnFact(4) = 4*6 = 24

9.3.2 G.C.D. of two numbers


According to Euclid’s algorithm, the recursive definition the gcd value of two positive integers x and y is as
follows:
Gcd(x,y) = Gcd(y,x) if x<y
=y if x≥y and x%y=0
= Gcd(x,x%y) if x>y

Algorithm 9.2
1. Algorithm fnGcd (x, y)
2. //Purpose : This algorithm calculates the gcd value of two numbers.
3. //Input : x and y are the two positive integers and x>y.
4. //Output : The calculated gcd value of x and y.
5. {
6. if (x>=y && x%y==0) //Base criteria
7. return y;
8. else
9. return(fnGcd(y, x%y)); //Recursive call
10. }// End of algorithm

Example 9.2: Let us show the execution of above algorithm to calculate the gcd value of 10 and 6.
fnGcd(10,6) → fnGcd(6,10%6) → fnGcd(4, 6%4)
As here 4>2 and 4%2=0, fnGcd(4,2) returns 2.
2 ← 2 ← 2

9.3.3 Exponential power


n
We can define a recursive function to compute the value of x as follows:
Power(x,n) = 1 if n = 0
=x if n = 1
= Power(x, n/2) * Power(x, n/2) if n is even and n > 1
= x * Power(x, (n-1)/2) * Power(x, (n-1)/2) if n is odd and n > 1

Algorithm 9.3
1. Algorithm fnPower(x,n)
n
2. //Purpose : This algorithm computes x .
3. //Input : x is mantissa and n is exponent.
n
4. //Output : The value of x .
5. {
6. if (n == 0) //Base criteria.
7. return 1;
8. if (n == 1) //Base criteria.
9. return x;
10. if (n>1 && n%2 == 0)
11. return(fnPower(x,n/2) * fnPower(x,n/2)); //Recursive call.
12. if (n>1 && n%2 == 1)
13. return(x * fnPower(x,(n-1)/2) * fnPower(x,(n-1)/2)); //Recursive call.
14. }// End of algorithm.
6
Example 9.3: Figure 9.2 shows the execution of algorithm 9.3 to calculate the value of 2 and the order of the
function calls are shown with each and every function call within third braces.

64 [1]
fnPower(2,6)

[2] 8 8 [5]
fnPower(2,3) * fnPower(2,3)

[3] 2 2 [4] [6] 2 [7] 2


2 * fnPower(2,1) * fnPower(2,1) 2 * fnPower(2,1) * fnPower(2,1)

return 2 return 2 return 2 return 2

6
Figure 9.2: A recursion tree to compute the value of 2 .

9.3.4 Fibonacci sequence


The celebrated Fibonacci sequence is as follows: -
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 …………
The recursive definition to calculate the nth number in the above series can be stated as follows:
Fib(n) = n for n= 0 or 1
= Fib(n-2) + Fib(n-1) for n>1

Algorithm 9.4
1. Algorithm fnFib(n)
2. // Purpose : This algorithm finds the nth number in the Fibonacci series.
3. // Input : n is an integer.
4. // Output : The value of nth number in the Fibonacci series.
5. {
6. if(n == 0 || n ==1 ) //Base criteria.
7. return n;
8. else
9. return(fnFib(n-2) + fnFib(n-1)); //Recursive call.
10. }//End of algorithm

Example 9.4: The recursion tree to calculate the value of Fib(5) using the algorithm is shown in Figure 9.3 and
the order of the function calls are shown with each and every function within third braces.
5 [1]
fnFib(5)

[2] 2 3 [7]
fnFib(3) + fnFib(4)

[3] 1 1 [4] [8] 1 2 [11]


fnFib(1) + fnFib(2) fnFib(2) + fnFib(3)

[5] 0 1 [6] 0 [9] 1 [10] [12] 1 1 [13]


return 1 fnFib(0) + fnFib(1) fnFib(0) + fnFib(1) fnFib(1) + fnFib(2)

[14] 0 [15] 1
return 0 return 1 return 0 return 1 return 1 fnFib(0) + fnFib(1)

return 0 return 1
th
Figure 9.3: A recursion tree to find the n value in Fibonacci series

9.3.5 Towers of Hanoi

The Towers of Hanoi problem is a famous recursive problem, which is based on 3 pegs and a set of discs with
different sizes. Suppose 3 pegs A, B and C are given and on peg A there are finite number of n discs in
increasing order i.e. biggest one at the bottom and smallest one at the top. The object is to move the discs from
peg A to peg C using peg B as an auxiliary peg. The rules are: -
a) Only one disc can be moved at a time.
b) Only the top disc on any peg can be moved to any other peg.
c) A larger disc can’t be placed on a smaller one.
If there is one disc on peg A then move the only disc to peg C (A → C). For more than one disc on peg A, move
top n-1 discs recursively to peg B using peg C as auxiliary according to the above stated rules. Now move
remaining one disc from peg A to peg C. Then again move n-1 discs from peg B to peg C using peg A as
auxiliary.

The solution to the Towers of Hanoi problem for n = 3 appear in the figure 9.4.
Move top disc from peg A to peg C.
Move top disc from peg A to peg B.
Move top disc from peg C to peg B.
Move top disc from peg A to peg C.
Move top disc from peg B to peg A.
Move top disc from peg B to peg C.
Move top disc from peg A to peg C.

A B C A B C
(1) Initial (2) A -> C

A B C A B C

(3) A -> B (4) C -> B

A B C A B C

(5) A -> C (6) B -> A

A B C A B C

(7) B -> C (8) A -> C

Figure 9.4: The moves to solve Tower of Hanoi problem for 3 discs.

Algorithm 9.5
1. Algorithm fnTowers_of_Hanoi(A, B, C, N)
2. //Purpose : This algorithm finds the moves to solve the Tower of Hanoi problem for N discs.
3. //Input : N is total number of discs.
4. //Output : None.
5. {
6. if(N == 1)
7. {
8. Move top disc from A to C (A → C);
9. return;
10. }
11. else
12. {
13. fnTowers_of_Hanoi(A, C, B, N-1);
14. fnTowers_of_Hanoi(A, B, C, 1);
15. fnTowers_of_Hanoi(B, A, C, N-1);
16. }
17. } // End of algorithm

Let us find the moves for N = 3 using the above recursive algorithm:
TOH(A,B,C,3)

TOH(A,C,B,2) TOH(A,B,C,1) TOH(B,A,C,2)


A -> C

TOH(A,B,C ) TOH(A,C,B,1) TOH(C,A,B,1) TOH(B,C,A,1) TOH(B,A,C,1) TOH(A,B,C,1)


A -> C A -> B C -> B B -> A B -> C A -> C
Therefore the total moves required for 3 discs are:
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
Number of moves required for N discs are: -
T(N) = 2T(N-1) + 1
= 22T(N-2) + 2 + 1
= ………………...
= …………………
= 2iT(N-i) + 2i-1 + ……+ 2 + 1
= 2N-1 + 2N-2 + ……..+ 2 + 1 [Let N-i = 1. So T(1) = 1]
= 2N – 1

9.4 Tail recursion


A recursive function is said to be tail recursive if there are no pending operations to be performed on return
from a recursive call. Consider the function fnFact() of the algorithm 9.1. Notice that there is a pending
operation, namely multiplication, to be performed on return from each recursive call. Whenever there is a
pending operation, the function is called non-tail recursive. The fnFact() function can be rewritten in a tail
recursive way as shown in algorithm 9.6.

Algorithm 9.6
1. Algorithm fnFact1(n, result)
2. // Purpose : This algorithm finds the factorial of a number in a tail-recursive way.
3. // Input : result is the calculated factorial value of n
4. // Output : This function returns the value of result.
5. {
6. if(n == 0 || n == 1)
7. return result;
8. else
9. return fnFact1(n-1, n*result);
10. }// End of Algorithm

Assume factorial value of 3 is to be calculated. Then the function fnFact1() described in Algorithm 9.6 is to be
called as fnFact1(3,1). Now the question is how does the algorithm work? First the condition of line 6 executes.
As n=3, so program control goes to line 9 of the else part. Now the function fnFact1(3,1) calls itself but with the
different parameters like fnFact1(2,3). Similarly fnFact1(2,3) calls fnFact1(1,6). Now as n=1, the function
fnFact1(1,6) returns 6 to fnFact1(2,3) and so on. Example 9.5 shows how the algorithm works. From the
example it is clear that there is no pending operation after return from any function call. Thus we can implement
a factorial function in a tail-recursive way.

Example 9.5: Factorial of 3 is to be calculated.


fnFact1(3,1)

call return 6

fnFact1(2,3)

call return 6

fnFact1(6,1)

9.5 Disadvantages of recursion


The disadvantages of recursion are as follows:
(i) It consumes more storage space because the recursive calls along with automatic variables are stored on the
stack.
(ii) If proper precautions are not taken, recursion may result in non-terminating iterations.
(iii) It also slows down execution speed, as function call require jumps, and saving the current state of program
onto stack before jump.

9.6 Recursion Vs Iteration

Serial Iteration Recursion


No.
1. It is a process of executing a statement or a Recursion is the technique of defining anything in
set of statements repeatedly, until some terms of itself.
specific condition is satisfied.
2. Iteration involves four clear cut steps There must be an exclusive if statement inside the
initialization, condition, execution, and recursive functions, specifying stopping condition
updation. which indicates the base criteria.
3. Any recursive problem can be solved Not all problems have recursive solution.
iteratively.
4. Iterative counterpart of a problem is more Recursion is generally a worse option to go for
efficient in terms of memory utilization and simple problems, or problems not recursive in
execution speed. nature.

/****C code to solve some problems using recursion****/


/****File Name: recursion.c****/
#include<stdio.h>
#include<conio.h>
int iCount_total_moves_for_toh;

int fnFact (int n)


// Purpose : This function calculates the factorial of a positive number.
// Input : n is the given number.
// Output : This function returns -1 for negative value of n, otherwise return factorial of n.
{
if(n<0)
return -1;
if(n == 1 || n == 0 ) // base criteria
return 1;
else
return (n*fnFact(n-1)); // recursive call
}// End of function

int fnGcd (int x, int y)


//Purpose : This function calculates the gcd value of two numbers.
//Input : x and y are the two positive integers and x>y.
//Output : The calculated gcd value of x and y.
{
if (x>=y && x%y==0) //Base criteria
return y;
else
return(fnGcd(y, x%y)); //Recursive call
}// End of function

int fnPower(int x,int n)


//Purpose : This function computes xn.
//Input : x is mantissa and n is exponent.
//Output : The value of xn.
{
if (n == 0) //Base criteria.
return 1;
if (n == 1) //Base criteria.
return x;
if (n>1 && n%2 == 0)
return(fnPower(x,n/2) * fnPower(x,n/2)); //Recursive call.
if (n>1 && n%2 == 1)
return(x * fnPower(x,(n-1)/2) * fnPower(x,(n-1)/2)); //Recursive call.
}// End of function.
int fnFib(int n)
// Purpose : This function finds the nth number in the Fibonacci series.
// Input: n is an integer.
// Output : The value of nth number in the Fibonacci series.
{
if(n == 0 || n ==1 ) //Base criteria.
return n;
else
return(fnFib(n-2) + fnFib(n-1)); //Recursive call.
}//End of function

void fnTowers_of_Hanoi(char A, char B, char C, int N)


//Purpose : This function finds the moves to solve the Tower of Hanoi problem for N discs.
//Input : N is total number of discs.
//Output : None.
{
if(N == 1)
{
printf("%c->%c\n",A,C);
iCount_total_moves_for_toh++;
return;
}
else
{
fnTowers_of_Hanoi(A, C, B, N-1);
fnTowers_of_Hanoi(A, B, C, 1);
fnTowers_of_Hanoi(B, A, C, N-1);
}
} // End of function

void main(void)
{
int n,iChoice,x,y;
clrscr();
do
{
printf("1. Calculate factorial recursively\n");
printf("2. Calculate GCD recursively\n");
printf("3. Calculate x^n recursively\n");
printf("4. Calculate nth term of fibonacci series recursively\n");
printf("5. Tower of Hanoi problem\n");
printf("6. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter the number to calculate the factorial");
scanf("%d",&n);
printf("The factorial of %d = %d\n",n,fnFact(n));
break;
case 2:
printf("Enter two numbers to calculate GCD value");
scanf("%d%d",&x,&y);
printf("The GCD value of %d and %d is %d\n",x,y,fnGcd(x,y));
break;
case 3:
printf("Enter the value of x and n");
scanf("%d%d",&x,&y);
printf("The GCD value of %d and %d is %d\n",x,y,fnPower(x,y));
break;
case 4:
printf("Enter n");
scanf("%d",&n);
printf("The %d term in fibonacci series is %d\n",n,fnFib(n));
break;
case 5:
printf("Enter the number of disks\n");
scanf("%d",&n);
iCount_total_moves_for_toh=0;
printf("The moves for %d disks are\n",n);
fnTowers_of_Hanoi('A','B','C',n);
printf("Total number of moves are %d\n\n",iCount_total_moves_for_toh);
break;
case 6:
exit(1);
default:
printf("Wrong Choice\n");
}
}while(1);
}

9.7 MCQ Chapter 9

1. Tail recursion
(a) occurs when the recursive call is the last statement executed in a recursive procedure or function
(b) is a path that includes a recursive call to the routine, to solve a smaller version of the original problem
(c) is a structure that keeps track of the activation records at run time, in order to preserve the values of
parameters, return addresses, registers, and so on
(d) refers to the point in the compile/execution cycle when variable names are associated with addresses in
memory.

2. Recursive case
(a) is a nonrecursive exit from the recursive routine
(b) is a path that includes a recusive call to the routine, to solve a similer version of the original problem
(c) is a structure that keeps track of the activation records at runtime, in order to preserve the values of
parameters, return address, registers and so on.
(d) refers to the point in the compile / execution cycle when variable names are associated with addresses
in memory.

3. Base case
(a) is a nonrecursive exit from the recursive routine
(b) is a path that includes a recusive call to the routine, to solve a similer version of the original problem
(c) is a structure that keeps track of the activation records at runtime, in order to preserve the values of
parameters, return address, registers and so on.
(d) refers to the point in the compile / execution cycle when variable names are associated with addresses
in memory.

4. Binding time
(a) is a nonrecursive exit from the recursive routine
(b) is a path that includes a recusive call to the routine, to solve a similer version of the original problem
(c) is a structure that keeps track of the activation records at runtime, in order to preserve the values of
parameters, return address, registers and so on.
(d) refers to the point in the compile / execution cycle when variable names are associated with addresses
in memory.

5. Run time stack


(a) is a nonrecursive exit from the recursive routine
(b) is a path that includes a recusive call to the routine, to solve a similer version of the original problem
(c) is a structure that keeps track of the activation records at runtime, in order to preserve the values of
parameters, return address, registers and so on.
(d) refers to the point in the compile / execution cycle when variable names are associated with addresses
in memory.

6. Infinite regress occurs when:


(a) a base case is ommited
(b) a base case is never reached
(c) both (a) and (b)
(d) none of these

7. Fibonacci function fib(n) = fib(n-1) + fib(n -2) is an example of


(a) Direct recursion (b) Tree recursion
(c) linear ecursion (d) both (a) and (b)

8. Any recursive function can be converted into an equivalent non recursive function
(a) Always (b) never
(c) sometimes (d) if and only if the function is tail recursive

9. Recursion is equivalent to
(a) Greedy paradigm (b) Divide and Conquer paradigm
(c) both (a) and (b) (d) None of these

10. Advantage of recursion is


(a) Coding complexity is less (b) Time complexity is less
(c) Space complexity is less (d) None of these

Solutions:
1. a 2. b 3. a 4. d 5. c 6. c 7. d 8. a 9. b 10. a

9.8 Exercise Chapter 9


1. What is Recursion? Write a C recursive algorithm to compute factorial of any given number. Check your
algorithm with n=5.

2. Discuss and differentiate different types of recursions.

3. Write a C Program to determine GCD of given numbers. Show using a stack how your algorithm works.

4. Hence, Write a C Program to determine LCM of given numbers. Show using a stack how your algorithm
works.

5. Write a C Program to determine nth element of a Fibonacci series. Show using a stack how your algorithm
works.

6. What is iteration? How does it differs from recursion? What are the disadvantages of recursion.

7. “Every recursive algorithm can be expressed as a iterative algorithm” --- Justify the statement with a
suitable example.

8. What is tail recursion? Explain with a suitable example and diagram.

Chapter 10
TREE
10.1 Definition
A tree is a finite set of vertices connected by edges such that −
(i) There is one specially designated vertex called root.
(ii) The remaining vertices are partitioned into a collection of sub-trees T1, T2 … Tn each of which is also a tree.

root

T2
T3
T1 B C D

E F G H I J

Figure 10.1: Example of a tree

10.2 Basic terminologies


Root: A node without any parent is called root node. In figure 10.1 A is the root node.
Node (or Vertex): Each data item in a tree is called a node. The tree in figure10.1 has 11 nodes (A, B, C, D, E,
F, G, H, I, J and K).
Edge: The line drawn from one node to another node is called an edge.
Internal node: A node with at least one child is called an internal node. In figure 10.1 A, B, C, D and F are the
internal nodes.
External node (or Leaf node): A node without any children is called an external node or leaf node. In figure
10.1 E, K, G, H, I and J are the leaf nodes.
Ancestor and Descendant: A node n is an ancestor of node m (and m is descendant of n) if n is either the
father of m or the father of some ancestors of m. In figure 10.1 node B is an ancestor of the nodes E, F and K.
Degree of a node: The number of sub trees of a node is called its degree. In figure 10.1 degree of node A is 3,
degree of node B is 2, degree of node E is 0, degree of node F is 1, and degree of node K is 0 and so on.
Degree of a tree: It is the maximum degree of nodes in a given tree. In figure 10.1 node A has degree 3 and
another node D is also having its degree 3. In all, this value is the maximum. So the degree of the above tree is
3.
Siblings: The nodes who share the same parent are called siblings. In figure 10.1 H, I and J are siblings.
Level: Number of ancestors of a node is called its level. Each node in a tree is assigned a level number as
follows:
1) The root of tree is at level 0.
2) Level of other node = level of its parent + 1.
In figure 10.1 node A is at level 0, nodes B, C and D are at level 1, nodes E, F, G, H, I and J are at level 2 and
node K is at level 3.
Depth (or height): The depth of a tree T can be defined as follows:
depth of T = maximum level number of T + 1
In figure 10.1 depth of the tree is 4.
Path and Path length: It is a sequence of consecutive edges from the source node to the destination node. The
number of edges in a path is path length. In figure 10.1, the path between A and H is A → D → H and the path
length is 2.
Branch: Path ending in a leaf node is called as branch. In figure 10.1 A → B → F → K is a branch.
Internal path length: Internal path length of a tree can be defined as the sum of the levels of all the internal
nodes in the tree. In figure 10.1 the internal path length of the tree is 0+1+2+1+1 = 5.
External path length: External path length of a tree is the sum of the levels of all the external nodes in the tree.
The external path length of the tree in figure 10.1 is 2+3+2+2+2+2 = 13.
Forest: A set of disjoint trees is called a forest. The following figure is a forest with three trees. Figure 10.2(d)
shows an example of a forest.

10.3 Binary tree


A binary tree is a finite set of elements that is either empty or is partitioned into three disjoint subsets.
1) The first subset contains a single element called the root of the tree.
2) The other two subsets are themselves binary trees, called the left and right sub trees of the original
tree.
3) Here each and every node can have at most two children.

(a) (b) (c) (d)

(d)

Figure 10.2: Example of some binary trees and forest

10.4 Types of binary trees

Complete binary tree: A binary tree is said to be complete if all its levels except possibly the last, have the
maximum number of possible nodes, and if all the nodes at the last level appear as far left as possible.
A

B C
D E F

Figure 10.3: Complete binary tree

Full binary tree: A binary tree is called a full binary tree if it satisfies the following two conditions:
1) Each node has either zero or two children.
2) All the leaf nodes are in the same level (and obviously this is the last level).
A

B C

D E F G

Figure 10.4: Full binary tree

Extended binary tree (or 2-tree): This is a binary tree where each node has either two or zero children. The
node with zero children is called external node and the node with two children is called internal node. External
nodes are represented by a square and internal nodes are represented by a circle.
Frequently an algorithm can be represented by a 2-tree where internal nodes represent tests and external
nodes represent actions.

1 5
0

2 3
Figure 10.5: Extended binary tree

Here, NE = NI + 1 where-
NE = number of external nodes
NI = number of internal nodes
For the above 2-tree –
External path length = LE = 2 + 2 + 3 + 3 + 3 + 3 = 16
Internal path length = LI = 0 + 1 + 1 + 2 + 2 = 6
Observe that-
LE = LI + 2NI

10.5 Properties of binary trees


The followings are the properties of a binary tree:
1. A binary tree with n nodes has exactly n-1 edges.
Proof: This property can be proved by induction.
Basis: Let n=1. That is there is only one node in the tree. So number of edge is 0. Hence the property is true for
one node.
Induction hypothesis:
n = k: Assume the property is true for n = k i.e. for k nodes there is k-1 edges.
n = k+1: Now addition of one node includes one extra edge into edge set. So, now total number of edges are =
(k-1) + 1 = k. Hence proved.

2. The maximum number of nodes on level i is 2i.


Proof: By induction:
Basis: Let i = 0. At level 0 there is only one node (root node). Hence the property is true for i = 0.
Induction hypothesis:
i = k: Assume the property is true for kth level. So at level k there is 2k nodes.
i = k + 1: In a binary tree maximum number of children for each node is 2. Now at level k there is 2 k nodes.
Thus at level k+1 the maximum number of nodes can be 2*2k = 2k+1. Hence proved.

3. The number of nodes in a full binary tree with height h is 2 h – 1 (or the maximum number of nodes in
a binary tree with height h is 2h – 1).
Proof: Total number of nodes in a full binary tree with height h is
h-1
i 0
N = ∑ 2 = 2 + 21 + ……….. + 2h-1 = 2h -1
i=0

4. If n is the total number of nodes in a complete binary tree of height h, then h = └ log2 n ┘ + 1.
Proof: From the definition of a complete binary tree of height h, it is full up to height h-1 and in the last level it
may have some shortage of nodes. Hence we can write
2h-1 < n ≤ 2h – 1 [As maximum number of nodes at height h-1 is 2h-1-1 and at height h is 2h-1]
h-1 h
2 <n<2
h-1< log2 n < h
So the value of log2 n lies between h and h-1. So if we take the floor value of log2 n then it will be h-1.
Hence h = └ log2 n ┘ + 1.

5. If n0 be the total number of leaf nodes and n 2 be the total number of nodes having two children in a
binary tree, then prove that n2 = n0 - 1.
Proof: Assume n1 is the total number of nodes with one child.
Now the total number of nodes N = n0 + n1 + n2
And total number of edges E = n0 * 0 + n1 * 1 + n2 * 2 = n1 + 2n2.
Again we know E = N – 1
Hence, n1 + 2n2 = (n0 + n1 + n2) -1 which yields n2 = n0 – 1.

10.6 Representation of binary tree

There are two general ways to represent a binary tree in memory.

1) Linked representation: The most common and easiest way to represent a binary tree is using linked
list. Each node in a binary tree has three fields. One field is used to store the data value and other two
fields are used to store the address of the two children.
struct Node
{
struct Node *ptrLeft;
left info right
char info;
struct Node *ptrRight;
};
typedef struct Node TreeNode;

B C

D E G H

Figure 10.6: Example of a binary tree

The linked representation of the binary tree in figure 10.6 is shown in figure 10.7. N represents the NULL
value.

B C

N D N E N N G N N H N

N F N

Figure 10.7: Linked representation of the binary tree in figure 10.6

Here we take a pointer variable ptrRoot to store the address of the root node in the tree.
TreeNode *ptrRoot;

The NULL value of ptrRoot indicates an empty tree. For a leaf node both the value of ptrLeft and ptrRight is
NULL.

2) Sequential representation: Another method to represent a binary tree in memory is by array. This
method is useful for a complete binary tree and is most efficient for a full binary tree. The rules to store
the data values of a binary tree in an array are:
i) The root R of tree T is stored in TREE [1].
ii) If a node N occupies TREE [k] then its left child is stored in TREE [2*k] and right child is
stored in TREE [2*k + 1].
For a binary tree with height h, 2h – 1 array spaces are required to store it.
The sequential representation of the binary tree of figure 10.7 is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
A B C D E G H N N F N N N N N

Now the question is how we can get the leaf nodes from the sequential representation. If the height of the binary
tree is h, then all the nodes at level h-1 are obviously the leaf nodes. Now in the array the range of index of the
nodes at level h-1 is from 2h-1 to 2h -1. So any node within this range in the array is a leaf node. For the other
nodes N (that are above the last level) at index K, if TREE[2*K] and TREE[2*K + 1] both is NULL then N is
leaf node.

10.7 Binary tree traversal methods


There are three standard ways of traversing a binary tree T with root R.
A) Preorder traversal:
1. Process the root R
2. Traverse the left sub tree of R in preorder
3. Traverse the right sub tree of R in preorder

B C

D E F G

Figure 10.8: Example of a binary tree


Preorder traversal of figure10.8: ABDECFG

Algorithm 10.1
1. Algorithm fnRecursivePreorder (ptrRoot)
2. // Purpose : This algorithm finds the preorder traversal of a binary tree using recursion.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. if(ptrRoot != NULL)
7. {
8. print(ptrRoot->info);
9. fnRecursivePreorder (ptrRoot->ptrLeft);
10. fnRecursivePreorder (ptrRoot->ptrRight);
11. }
12. }//End of algorithm

Figure 10.9 shows the different steps of the recursive preorder algorithm for the binary tree of Figure 10.8.
B D
print(B) print(D)
A
print(A)
fnRecurPreorder(D)
fnRecurPreorder(E) fnRecurPreorder(NULL)
fnRecurPreorder(NULL)
print(E)
print(F)
fnRecurPreorder(NULL)
E
fnRecurPreorder(B)

fnRecurPreorder(A)
fnRecurPreorder(NULL)
C F

print(C)

fnRecurPreorder(F)

fnRecurPreorder(C)
G
print(G)

fnRecurPreorder(G)

fnRecurPreorder(NULL)

Figure 10.9: Tree of recursive calls for preorder of the tree in figure 10.8

Algorithm 10.2
1. Algorithm fnNon_Recursive_Preorder(ptrRoot)
2. // Purpose : This algorithm finds the preorder traversal of a binary tree in a non-recursive way.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. TreeNode *N;
7. Initialize stack S; // initialize a stack s to be empty
8. fnPush(ptrRoot, S) // push root node onto the stack
9. while(!empty(S)) // while the stack is not empty
10. {
11. N = fnPop(S); // pop a node from the stack
12. if(N != NULL) // if the node is not NULL
13. {
14. print N->info; // print the contents of the node
15. fnPush(N->ptrRight, S); // push the root of the right sub tree
16. fnPush(N->ptrLeft, S); // push the root of the left sub tree
17. }
18. }
19. }//End of algorithm

B) Inorder traversal:
1. Traverse the left sub tree of the root R in inorder.
2. Process the root R.
3. Traverse the right sub tree of the root R in inorder.

Inorder traversal of figure 10.8 is DBEAFCG

Algorithm 10.3
1. Algorithm fnRecursiveInorder(ptrRoot)
2. // Purpose : This algorithm finds the in-order traversal of a binary tree using recursion.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. if(ptrRoot != NULL)
7. {
8. fnRecursiveInorder (ptrRoot->ptrLeft);
9. print(ptrRoot->info);
10. fnRecursiveInorder (ptrRoot->ptrRight);
11. }
12. }//End of algorithm

Algorithm 10.4
1. Algorithm fnNon_Recursive_Inorder(ptrRoot)
2. // Purpose : This algorithm finds the in-order traversal of a binary tree in a non-recursive way.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. TreeNode *P;
7. P = ptrRoot; //Assign the address of the root node to P.
8. Initialize stack S; // initialize a stack s to be empty.
9. while( !empty(S) || P != NULL) // while the stack is not empty or P is not NULL.
10. {
11. while(P != NULL) //While P is not NULL.
12. {
13. fnPush(S, P); //Push P to stack.
14. P = P->ptrLeft; //Traverse the left child of P.
15. }
16. if( !empty(S)) //If Stack is not empty.
17. {
18. P= fnPop(S); // Pop from stack and assign the address to P.
19. Print P->info; // Print the information field of P.
20. P = P->ptrRight; //Traverse the right child of P.
21. }
22. }
23. }//End of algorithm

C) Postorder traversal:
1. Traverse the left sub tree of R in postorder
2. Traverse the right sub tree of R in postorder
3. Process the root R

Postorder traversal of figure 10.8 is: DEBFGCA

Algorithm 10.5
1. Algorithm fnRecursivePostorder(ptrRoot)
2. // Purpose : This algorithm finds the post-order traversal of a binary tree using recursion.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. if(ptrRoot != NULL)
7. {
8. fnRecursivePostorder(ptrRoot->ptrLeft);
9. fnRecursivePostorder(ptrRoot->ptrRight);
10. print (ptrRoot->info);
11. }
12. }//End of algorithm

Algorithm 10.6
1. Algorithm fnNon_Recursive_Postorder(ptrRoot)
2. // Purpose : This algorithm finds the post-order traversal of a binary tree in a non-recursive way.
3. // Input : ptrRoot is the address of the root node of the tree.
4. //Output : None.
5. {
6. TreeNode *P, *N;
7. P = ptrRoot; //Assign the address of root node to P.
8. while( !empty(S)) //While stack is not empty.
9. {
10. while(P != NULL) //While P is not NULL.
11. {
12. fnPush(S, P); //Push P onto stack.
13. if(P->ptrRight != NULL);
14. fnPush(S, NULL); //Push NULL if right child of P exists.
15. P = P->ptrLeft; //Traverse to the left child of P.
16. }
17. N = fnPop(S); //Pop one node from stack assign it to N.
18. if(N != NULL)
19. print N->info; // If N is not null then print it’s information field.
20. else
21. {
22. N = fnPop(S); // Pop one node from stack assign it to N.
23. P = N->ptrRight; //Assign the address of the right child of N to P.
24. fnPush(S, N); //Push N onto stack.
25. }
26. }
27. }//End of algorithm

10.8 Creation of binary tree from preorder and inorder traversal

Consider the following preorder and inorder traversals of a binary tree.


Preorder :ABDGHEICFJK
Inorder :GDHBEIACJFK
Now the different steps to create the binary tree are as follows:

STEP 1:
In preorder traversal, first we print the data and then move to left child and right child respectively. Therefore
the first node in preorder traversal is obviously the root node and then the left sub tree and right sub tree of the
root can be obtained from the inorder traversal.
Preorder :ABDGHEICFJK
Inorder :GDHBEIACJFK

GDHBEI CJFK

STEP 2:
Among the nodes of the left sub tree of A, node B comes first in the preorder traversal. So B is the immediate
left child of A. Similarly C is the immediate right child of A.
Preorder :ABDGHEICFJK
Inorder :GDHBEIACJFK

B C

GDH EI JFK

STEP 3:
Preorder :ABDGHEICFJK
Inorder :GDHBEIACJFK

G D H B E I C J F K
10.9 Creation of binary tree from postorder and inorder traversal

Consider the following post-order and in-order traversals of a binary tree.


Postorder :HDIEBJFKLGCA
Inorder :HDBIEAFJCKGL
The steps to build the binary tree from two traversals are as follows:

STEP 1:
In post-order traversal, first we move to left child and right child respectively and then print the data. Therefore
the last node in post-order traversal is obviously the root node and then the left sub tree and right sub tree of the
root can be obtained from the in-order traversal.
Postorder :HDIEBJFKLGCA
Inorder :HDBIEAFJCKGL

HDBIE FJCKGL

STEP 2:
Now among the nodes of the left sub tree of root A, node B is traversed last in post-order traversal. Therefore
node B is immediate left child of A. Similarly we get node C as the immediate right child of A.
Postorder :HDIEBJFKLGCA
Inorder :HDBIEAFJCKGL

B C
DH IE IE KGL

STEP 3:
Postorder :HDIEBJFKLGCA
Inorder :HDBIEAFJCKGL

B C

D E F G

H I L K L

10.10 Expression tree


Expressions tree generally a 2-tree in which external nodes contain operands and internal nodes contain
operators. But due to presence of unary operators into expressions it may not be a 2-tree. An expression tree
does not contain parenthesis, the reason for this is that for evaluating an expression using expression tree,
structure of tree itself decides the order of operations. An expression tree may not unique for an expression.
But the result they produce must be correct. The preorder and post-order traversal of an expression tree gives
the prefix and postfix notation of the arithmetic expression respectively.

Example 1: Create expression tree for the arithmetic expression A+B-C.

- +

+ C A -

A B B C

Two expression trees for A+B-C


Example 2: Create an expression tree for A + B * C – D + logE / F.

Step 1:
+

A B*C-
D+logE/F
Step 2:
+

A -

B*C D+logE/F

Step 3:
+

A -

* +

B C D logE/F

Step 4:
+

A -

* +

B C D /

F
logE

Step 5:
A + -

* +

B C D /

F
lo
g

10.11 Binary search tree


A binary search tree is a binary tree in which each node has value greater than every node of its left sub tree and
less than every node of its right sub tree. This type of tree does not contain duplicate value. The inorder
traversal of a binary search tree gives a list in ascending order. An example of a binary search tree is shown in
figure 10.10.

B G

A C F H

D I J

Figure 10.10: An example of a binary search tree

Insertion into binary search tree:


Consider the following items:
ITEMS: M F P B I Q
We have to create a binary search tree using the items.
Solutions:
Step 1: The first item in the list is M. As the tree is empty, insert M as the root node.
insert M

Step 2: The second item F is less than the root M. SoMinsert F as the left child of M.
insert F
M

F
Step 3: Next item P is greater than M, so insert P as the right child of M.
insert P
M

F P

Step 4: Next item in the list is B. B is less than the root M. So next comparison takes place with F (the left child
of M). Now B is less than F and left child of F is NULL. So insert B as the left child of F.
insert B
M

F P

Step 5: Next item I is less than the root M and greater than F (the left child of M). As the right child of F is
NULL, insert I as the right child of F.
insert I
M

F P

B I

Step 6: The last item Q is greater than the root M and its child P. So insert Q as the right child of P.
insert Q

F P

B I Q

Algorithm for insertion into binary search tree:


Algorithm 10.7
1. Algorithm fnInsert_BST(cData)
2. //Purpose : This algorithm inserts a new node into a binary search tree.
3. //Input : The item cData.
4. //Output : This algorithm returns 0 for a duplicate node and returns 1 for a successful insertion.
5. //Comments: Root of the tree is ptrRoot which is a global variable.
6. {
7. TreeNode *ptrNewNode, *ptrParent, *ptrChild;
8. ptrNewNode=(TreeNode *)malloc(sizeof(TreeNode));
9. ptrNewNode->ptrLeft=ptrNewNode->ptrRight=NULL;
10. ptrNewNode->info=cData;
11. if(ptrRoot == NULL)
12. ptrRoot = ptrNewNode;
13. else
14. {
15. ptrChild = ptrRoot;
16. while(ptrChild != NULL)
17. {
18. ptrParent = ptrChild;
19. if(ptrChild->info > cData) ptrChild = ptrChild->ptrLeft;
20. else if(ptrChild->info < cData) ptrChild = ptrChild->ptrRight;
21. else return 0;
22. }
23. if(ptrParent->info > cData) ptrParent->ptrLeft = ptrNewNode;
24. else ptrParent->ptrRight = ptrNewNode;
25. }
26. return 1;
27. }//End of Algorithm

Recursive algorithm for insertion into binary search tree:


Algorithm 10.8
1. Algorithm TreeNode *fnInsert_BST( ptrRoot, iData)
2. //Purpose : This algorithm inserts a new node into a binary search tree in recursive way.
3. //Input : Root of the tree is ptrRoot and the item to be inserted is iData.
4. //Output : This algorithm returns address of a node.
5. {
6. if (ptrRoot == NULL) // position found.
7. {
8. ptrRoot = (TreeNode *) malloc(sizeof(TreeNode)); //Create new node.
9. ptrRoot ->ptrLeft = NULL; // Set its left child to NULL.
10. ptrRoot ->ptrRight = NULL; // Set its right child to NULL.
11. ptrRoot ->info = iData; //Insert the data value into the new node.
12. }
13. elseif ( iData < ptrRoot->info) // traverse to the left of p
14. ptrRoot->ptrLeft = fnInsert_BST(ptrRoot->ptrLeft, iData);
15. elseif( iData > ptrRoot->info) // traverse to the right of p
16. ptrRoot->ptrRight = fnInsert_BST(ptrRoot->ptrRight, iData);
17. else
18. print (Duplicate node);
19. return(ptrRoot);
20. }//End of algorithm

Deletion from binary search tree:


Rules:
1. If the node has no child then simply remove it.

M
F P

B Q

Node I is deleted

2. If the node has one child then moves up the only child.

F Q

B I

Node P is deleted

3. If the node has two children then replace its position by its inorder successor (or predecessor).

M P

F P F Q

B I Q B I

Inorder : B F I M P Q
M is to be deleted. As M has two children replace node M by its inorder successor P

Node M is deleted
Algorithm to delete a node from a BST:
Algorithm 10.9
1. Algorithm fnDelete_BST(ptrDeleteNode)
2. //Purpose : This algorithm deletes a node from a binary search tree.
3. //Input : The node with the address ptrDeleteNode is to be deleted.
4. //Output : None.
5. {
6. if(ptrDeleteNode->ptrLeft == NULL && ptrDeleteNode->ptrRight == NULL)
7. {
8. free(ptrDeleteNode);
9. return;
10. }
11. else if(ptrDeleteNode->ptrLeft != NULL && ptrDeleteNode->ptrRight != NULL)
12. {
13. ptrSuccessor = Get_Inorder_Successor(ptrDeleteNode);
14. ptrSuccessor->ptrLeft = ptrDeleteNode->ptrLeft;
15. ptrSuccessor->ptrRight = ptrDeleteNode->ptrRight;
16. free(ptrDeletenode);
17. return;
18. }
19. else if(ptrDeleteNode->ptrLeft == NULL && ptrDeleteNode->ptrRight !=NULL)
20. {
21. ptrParent = Get_Parent(ptrDeleteNode);
22. if(ptrParent->ptrLeft == ptrDeleteNode)
23. ptrParent->ptrLeft = ptrDeleteNode->ptrRight;
24. else
25. ptrParent->ptrRight = ptrDeleteNode->ptrRight;
26. free(ptrDeleteNode);
27. return;
28. }
29. else
30. {
31. ptrParent = Get_Parent(ptrDeleteNode);
32. if(ptrParent->ptrLeft == ptrDeleteNode)
33. ptrParent->ptrLeft = ptrDeleteNode->ptrLeft;
34. else
35. ptrParent->ptrRight = ptrDeleteNode->ptrLeft;
36. free(ptrDeleteNode);
37. return;
38. }
39. }//End of algorithm

10.12 Heap
A heap is a complete binary tree and is implemented in array as sequential representation rather than linked
representation. A heap is called max heap or descending heap if every node of heap has a value greater than or
equal to the value of every child of that node. Similarly a heap is called min heap or ascending heap if every
node of heap has a value less than or equal to the value of every child of that node.

Insertion into max heap:


M F P B I Q

Step 1: insert node M

Step 2: insert node F


M M

F F

Step 3: insert node P

M P
Reheap

F P F M

Step 4: insert node B

P P

F M F M

B B

Step 5: insert node I

P P
Reheap

F M I M

B I B F

Step 6: insert node Q

P Q
Reheap
I M I P

B F Q B F M

Deletion from heap:


1. Assign the node to some variable ITEM
2. Replace ITEM by the last node L of the tree
3. Reheap

I P Q I P B F M

1 2 3 4 5 6
B F M

Suppose ITEM = Q. Then replacing node Q by the last node (L=) M we get

M P
Reheap

I P I M

B F B F

10.13 Weight balanced binary tree

Huffman tree: Consider the extended binary tree shown in the figure 10.10.
Figure 10.10

Suppose every external node has some weight W, then the weighted path length for the external node will be –

P = W1P1 + W2P2 + …………….. + WnPn where W denotes the weight and P denotes the
path length of an external node.
Let us take the weights 4, 7, 8, 12 and create three different 2-trees.

4 7 8 12 8

7 12

Figure 10.11 Figure 10.12

12
8

4 7

Figure 10.13

Pa = 4 X 2 + 7 X 2 + 8 X 2 + 12 X 2 = 62
Pb = 4 X 1 + 7 X 3 + 12 X 3 + 8 X 2 = 77
Pc = 12 X 1 + 4 X 3 + 7 X 3 + 8 X 2 = 61

Now we can see that three different trees have different path lengths. So problem arises for obtaining a unique
tree which has minimum weighted path length. This extended binary tree can be obtained by Huffman algorithm

Huffman algorithm:
1. Let us take there are n weights W1, W2, ………. , Wn.
2. Take two minimum weights and create a sub tree. Suppose W1 and W2 are first two minimum weights then
sub tree will be-

W1 W2

3. Now the remaining weights will be W1 + W2, W3, W4, ……………. , Wn.
4. Create all sub trees at the last weight.

Example:
W1 W2 W3 W4 W5 W6 W7

16 11 7 20 25 5 16
STEP 1:

5 7

STEP 2: W1 W2 W3+W6 W4 W5 W7

16 11 12 20 25 16
11

5 7

STEP 3: W1 W2 + W3+W6 W4 W5 W7

16 23 20 25 16

11
16 16

5 7

STEP 4: W1 + W7 W2 + W3+W6 W4 W5

32 23 20 25

16 16 20

11

5 7

Step 5: W1 + W7 W2 + W3+W6 + W4 W5

32 43 25
25 20

16 16 11

5 7

Step 6: W1 + W7 + W5 W2 + W3+W6 + W4

57 43

25 20

16 16 11

5 7

10.14 AVL Search tree / Height balanced binary tree

An AVL tree is a binary search tree where the balance factor of any node can have only three values -1, 0 or 1.
The name AVL comes from the Russian mathematics who developed this tree in 1962 : G. M. Adel’son –
Velskii and E. M. Landis.

Balance factor = height of left sub tree (hL) – height of right sub tree (hR)
0 0

M M
0 -1 1 -1

F P F P
0 0 0 0 0
B I Q B Q

Figure 10.14: Examples of AVL tree


Insertion into AVL tree:
First insert an item into AVL tree according to the rules of insertion into binary search tree. Then check whether
the tree is balanced or not. If it is unbalanced, then find the nearest ancestor node (P) to the inserted node with
balance factor +2 or -2 on the path from inserted node to root node. P is called the pivot node. Then perform one
of the following four rotations on the unbalanced tree to make it balanced depending on some criteria.
1. Left to left rotation: When the pivot node is left heavy and the new node is inserted in left sub tree of the
left child of pivot node then the rotation performed is left to left rotation.
+1 +2 0

P P A

0 P 1 P 0
A A P
R
Insert into AL
R
LL rotation A
h h L h+1

A A A A A P
L R L R R R
h h h+1 h h h

Balanced Unbalanced Balanced


2. Right to Right rotation: When the pivot node is right heavy and the new node is inserted in right sub tree
of the right child of pivot node then the rotation performed is right to right rotation.
-1 -2 0
A
P P
0 -1 0
P A
A Insert into AR P A RR rotation R
P
L L
h h
A A A A P A
L R L R L L
h h h h+1 h h h+1

Balanced Unbalanced Balanced


3. Left to Right rotation: When the pivot node is left heavy and the new node is inserted in right sub tree of
the left child of pivot node then the rotation performed is left to right rotation. Here we insert a new node
into BL (left sub tree of node B). But the new node can also be inserted into BR (i.e. the right sub tree of the
node B). Actually left to right rotation is a kind of double rotation. First rotate the unbalanced tree right to
right taking node A as pivot node and then perform left to left rotation on the resultant tree taking node P as
pivot node.

0 +2 +0

P B
P
0 -1 0 -1
P Insert into BL LR rotation A P
A h R A h P
0 +1 R
A A B B P
h L h A h L h L R h R
B B
L
h
B B B B h-1
h-1 L R h-1 L R h-1
h

Balanced Unbalanced Balanced

4. Right to Left rotation: When the pivot node is right heavy and the new node is inserted in left sub tree of
the right child of pivot node then the rotation performed is right to left rotation. Here we insert a new node
into BL (left sub tree of node B). Insertion can also take place into BR. Right to left rotation is also a double
rotation. Here first we perform a left to left rotation taking the node A as pivot node and then a right to right
rotation taking the node P as pivot node. The final balanced tree is shown in figure.

-1 -2
0
P P
B
0 1
P A Insert into BL P A RL rotation
L L P
0 -1 A
h h
B 0 A B 1 A
R R
P B B A
h h h h
B B B B L L R R
h
L R L R
h-1 h-1 h h-1 h-1

Balanced Unbalanced Balanced

Example: Construct an AVL search tree with the following elements in the order of their occurrence:
G, B, A, K, M, C

Insert G, A:
G

Insert B:
G

LL Rotation B
B
A G
A

Insert K, M: B B

RR Rotation
A G A K

K G M

Insert C: G
B

B K
A K RL Rotation

A C M
G M

Deletion from AVL tree:


If deletion of a node from an AVL search tree makes the tree unbalanced, then first find the pivot node in the
tree. Now perform the following rotations on the unbalanced tree to make it balance.

1. R0 rotation: If deletion takes place from the right sub tree of P and the balance factor of the left child (A)
of P is 0, then perform R0 rotation.
+1 +2 -1

P A
Delete from PR P R0 rotation

0 0 1
A P A P A P
R h R h-1 L h
A A A A A P h-1
h L R h h L R h h R R

Balanced Unbalanced Balanced

2. R1 rotation: If deletion takes place from the right sub tree of P and the balance factor of the left child (A)
of P is 1, then perform R1 rotation.

+1 +2 0

P Delete from PR P R1 rotation A

+1 +1 0
A P A P A P
R h R h-1 L h
A A A A h-1 A P h-1
h L R h-1 h L R h-1 R R

Balanced Unbalanced Balanced

3. R-1 rotation: If deletion takes place from the right sub tree of P and the balance factor of the left child (A)
of P is -1, then perform R2 rotation.

+1 +2 0

P P B

-1 -1 0 0
A P Delete from PR A P R-1 rotation A P
h R h-1 R
0 0
A B A B A B B P
h-1 L h-1 L L L R R
h
B B B B h-1 h-1 h-1 h-1
L R L R
h-1 h-1 h-1 h-1

Balanced Unbalanced Balanced

4. L0 rotation: If deletion takes place from the left sub tree of P and balance factor of the right child (A) of P
is 0, then perform L0 rotation.
5. L1 rotation: If deletion takes place from the left sub tree of P and the balance factor of the right child (A)
of P is 1, then perform L1 rotation.
6. L-1 rotation: If deletion takes place from the left sub tree of P and the balance factor of the right child (A)
of P is -1, then perform R2 rotation.

10.15 Threaded binary tree


Consider the binary tree in figure ………10.6 and its linked presentation in figure ……….10.17. In figure……
10.17, the total number of links is 16 and the total number of NULL links is 9 where as the total number of
nodes in the binary tree is 8. In fact if a binary tree has n number of nodes, then total number of links in the tree
2n and total number of NULL links is n+1. A.J. Perlis and C. Thornton jointly proposed an idea to make
effective use of these NULL pointers. The basic idea is to replace the NULL pointers by some appropriate
pointer values called thread. The general rule says to replace the NULL left pointer of a node with the address
of the immediate predecessor of this node and replace the NULL right pointer of a node by the address of the
immediate successor of this node. The threaded binary tree corresponding to the tree of figure 10.6 is shown in
the figure 10.15. In the threaded binary tree still the left pointer of node D and right pointer of node H is NULL.
To eliminate these two NULL values we take a header node. Left pointer of node D and right pointer of node H
both contain the address of the header node. Now the question comes how we can differentiate between a thread
pointer and normal pointer from memory representation of the tree. For this purpose we can include two more
fields with each node to decide whether this is a thread pointer or normal pointer. Then the structure definition
for a node is:
struct Node
{
struct Node *ptrLeft;
left lThread info rThread right int lThread;
char info;
struct Node *ptrRight;
int rThread;
};
typedef struct Node ThreadedTreeNode;

The value 0 of lThread or rThread indicates that it is a normal pointer, otherwise a thread pointer. For any node
p in a threaded binary tree:
lThread(p) = 0 indicates left child of p is a normal pointer.
lThread(p) = 1 indicates left child of p is a thread pointer.
rThread(p) = 0 indicates right child of p is a normal pointer.
rThread(p) = 1 indicates right child of p is a thread pointer.
Now the algorithm to find the in-order successor of a node x in the tree is:

Algorithm 10.10
1. Algorithm fnFind_Inorder_Successor(x)
2. // Purpose : This algorithm finds the successor node of x in in-order traversal of a threaded binary tree.
3. // Input : The address of the node for which the in-order successor node is required to find.
4. // Output : The address of the in-order successor node of x.
5. {
6. ThreadedTreeNode *p;
7. p = x->ptrRight;
8. if(x->rThread == 0) //if q has right child
9. while(p->lThread==0)
10. p = p->ptrLeft;
11. return p;
12. }//End of algorithm
Now the algorithm to find the in-order predecessor of a node x in the tree is:
Algorithm 10.11
1. Algorithm fnFind_Inorder_Predecessor(x)
2. // Purpose : This algorithm finds the predecessor node of x in inorder traversal of a threaded binary tree.
3. // Input : The address of the node for which the inorder predecessor node is required to find.
4. // Output : The address of the inorder predecessor node of x.
5. {
6. ThreadedTreeNode *p;
7. p = x->ptrLeft;
8. if(x->lThread == 0) //if q has left child
9. while(p->rThread==0)
10. p = p->ptrRight;
11. return p;
12. }//End of algorithm

Finally the algorithm for performing in-order traversal on the threaded binary tree is:
Algorithm 10.12
1. Algorithm fnInorder(H)
2. // Purpose : This algorithm finds the inorder traversal of a threaded binary tree.
3. // Input : H is the address of the header node of the tree.
4. // Output : None.
5. {
6. ThreadedTreeNode *Header;
7. Header = H; // stores the address of the header node into the variable Header.
8. while(1)
9. {
10. H = fnFind_Inorder_Successor(H);
11. if(H==Header) // Traversing is finished.
12. return;
13. else
14. print(H->info);
15. }
16. }//End of Algorithm

B C

N D E G H N

Figure 10.15: Threaded binary tree corresponding to figure 10.6


Header node
1 - 1

0 A 0

0 B 0 0 C 0

1 D 1 0 E 1 1 G 1 1 H 1

1 F 1

Figure 10.16: Two way in-order threading using lThread and rThread fields with header node

10.16 M-way search tree


If we relax the restriction that each node can have only one key, we can reduce the height of the tree. An M-way
search tree means a search tree with degree M. An M-way search tree T may be an empty tree. If T is non-
empty, then it satisfies the following properties:
1. Each and every node of T has at least one key value and at most M-1 key values.
2. If in a node there are n keys k0, k1……kn-1, then k0 < k1 < ……… < kn-1.
3. All keys in left sub tree of a key are less than it and all keys in right sub tree of a key are greater than it.
4. Each of the sub trees of a node are also M-way search tree.
The figure 10.17 shows an M-way search tree of order 3. The value N in the link part of every node denotes a
NULL link.

G M

N A D N N J N L N N X N Z N

N B N C N

Figure 10.17: An M-way search tree with M=3

The height of an M-way search tree with n keys is logM(n + 1). The maximum number of keys in an M-way
search tree of height h is Mh -1.
Rules for insertion into M-way search tree: First find the position of the node for the new key in the tree. If
the node is not full then insert the key into appropriate position in the node. For a full node create a new node in
proper place.
Example: Insert the following keys into an M-way search tree of order 3.
W, A, X, P, M, K, J
Insert W, A:
N A N W N

Insert X:
N A N W

N X N - N

Insert P, M:
N A W

N M N P N N X N - N

Insert K:
N A W

M N P N N X N - N

N K N - N

Insert J:
N A W

M N P N N X N - N

N J N K N
Rules for deletion from an M-way search tree: Suppose key Ki is to be deleted from a node of an M-way
search tree.

….. Li-1 Ki Li …..

Li-1 and Li are the addresses of the left child node and the right child node of K i respectively. Now the following
four conditions may come into consideration.
1. Li-1 and Li both are NULL. Delete Ki simply.

N A J Delete key B N A J

N B N E N N N N P N N E N - N N N N P N
Before Deletion After Deletion

2. Li-1 is not NULL and L i is NULL. Pick the largest key value (K) from the left child node of K i, delete key K
and replace Ki with K.

N A J Delete key N N A J

N B N E N N N P N N B N E N M N P N

N L N M N N L N - N

Before Deletion After Deletion

3. Li-1 is NULL and Li is not NULL. Pick the smallest key value (K) from the right child node of K i, delete key
K and replace Ki with K.

N A J Delete key A N B J

N B N E N N N N P N N E N - N N N N P N
Before Deletion After Deletion

4. Li-1 and Li both are NULL. Choose either the largest key value from the left child node of K i or the smallest
key value from the right child node of Ki, delete the key and replace Ki with the chosen key.
N A J Delete key J N A E

N B N E N N N N P N N B N - N N N N P N
Before Deletion After Deletion
OR
N A J Delete key J N A N

N B N E N N N N P N N B N E N N P N - N
Before Deletion After Deletion
10.17 B-Tree/Balanced M-way search tree
M-way search tree minimizes the time of file access due to its reduced height. But it is required to keep the
height of the tree as low as possible and for this purpose we need to balance the height of the tree. This balanced
M-way search tree is called B-Tree.
Definition: A B-tree of order M is an M-way search tree in which
1. Root node has at least 1 key value and at most M-1 key values.
2. The other nodes except the root contain at least ┌M/2┐ -1 key values and M-1 key values.
3. All leaf nodes are on the same level.
4. Keys are arranged in a defined order within the node.
Rules for insertion a new key into a B-tree:
1. First search the tree to check the existence of new key value in the tree. If no duplicate key is found, then get
the leaf node N where the new key is to be inserted.
2. If N is not full (a node is called full when it contains at most keys), then insert the new key in N and exit.
3. If N is full, then split the node into two new nodes at its median value and move the median key value to its
parent node.
Repeat step 3 until the tree is balanced.
Rules for deletion a key from a B-tree: In B-tree deletion is done from the leaf node only. If we need to delete
a key from an internal node then replace the node with its immediate successor (or predecessor) until a key from
any leaf node is promoted to its upper level. Then balance the tree according to the given rules:
1. If the leaf node has enough key, then simply remove it.

J - N - N - N

N A N C N F N I N N L N O N R N V N

Delete I

J - N - N - N

N A N C N F N - N N L N O N R N V N
2. If the leaf node contains less than minimum number of keys, then check its immediate left sibling node and
immediate right sibling node.
i) If the left sibling node has more than the minimum number of key, then move the largest key value of
this node to its parent and move down the intervening entry from the parent node to its right child
node.

J - N - N - N

N A N C N F N I N N L N O N - N - N

Delete O

I - N - N - N

N A N C N F N - N N J N L N - N - N

ii) If the right sibling node has more than the minimum number of key, then move the smallest key value
of this node to its parent and move down the intervening entry from the parent node to its left child
node.

J - N - N - N

N A N C N - N - N N L N O N R N V N

Delete A

L - N - N - N

N C N J N - N - N N O N R N V N - N

iii) If both the sibling nodes have minimum number of keys, then create a new node merging the two leaf
nodes and the intervening key value of their parent node.
J P - N - N

N A N C N - N - N N Q N S N - N - N

N L N M N - N - N

Delete J

P - N - N - N

N A N C N L N M N N Q N S N V N - N

Example: Insert the following elements in a B-tree of order 3.


W, A, X, P, M, Z, K, J, T, L
Insert W, A:
N A N W N

Insert X, P, M, Z:
N A W

N M N P N N X N Z N

Insert K:
M - N

N A - N W - N

K N - N N P N - N N X N Z N
Insert J, T:
M - N

N A - N W - N

J N K N N P N T N N X N Z N

10.18 Red/Black tree:


A Red-Black tree is a colored binary search tree which has the following properties:
1. Every node is RED or BLACK.
2. Every leaf is a NULL node and colored BLACK.
3. If a node is RED, then both children are BLACK.
4. Every path from a node to a leaf contains the same number of BLACK nodes.

Black Nodes Red Nodes

D K

C E J M

N N N N N N N N

Figure 10.18: An example of a Red-Black tree


/* Program to implement a Binary Search Tree */
/* File Name: BST.C */
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
struct Node
{
struct Node *ptrLeft;
int iData;
struct Node *ptrRight;
};
typedef struct Node TreeNode;

//Prototype Declaration
void fnRecursivePreorder (TreeNode *);
void fnRecursiveInorder(TreeNode *);
void fnRecursivePostorder(TreeNode *);
TreeNode *fnInsert_BST( TreeNode *, int);

void fnRecursivePreorder (TreeNode *ptrRoot)


// Purpose : This function finds the preorder traversal of a binary tree using recursion.
// Input: ptrRoot is the address of the root node of the tree.
//Output : None.
{
if(ptrRoot != NULL)
{
printf("%d\t",ptrRoot->iData);
fnRecursivePreorder (ptrRoot->ptrLeft);
fnRecursivePreorder (ptrRoot->ptrRight);
}
}//End of function

void fnRecursiveInorder(TreeNode *ptrRoot)


// Purpose : This function finds the in-order traversal of a binary tree using recursion.
// Input: ptrRoot is the address of the root node of the tree.
//Output : None.
{
if(ptrRoot != NULL)
{
fnRecursiveInorder (ptrRoot->ptrLeft);
printf("%d\t",ptrRoot->iData);
fnRecursiveInorder (ptrRoot->ptrRight);
}
}//End of function

void fnRecursivePostorder(TreeNode *ptrRoot)


// Purpose : This function finds the post-order traversal of a binary tree using recursion.
// Input: ptrRoot is the address of the root node of the tree.
//Output : None.
{
if(ptrRoot != NULL)
{
fnRecursivePostorder(ptrRoot->ptrLeft);
fnRecursivePostorder(ptrRoot->ptrRight);
printf("%d\t",ptrRoot->iData);
}
}//End of function

TreeNode *fnInsert_BST( TreeNode *ptrRoot, int iData)


//Purpose : This function inserts a new node into a binary search tree in recursive way.
//Input : Root of the tree is ptrRoot and the item to be inserted is iData.
//Output : This function returns address of a node.
{
if (ptrRoot == NULL) // position found.
{
ptrRoot = (TreeNode *) malloc(sizeof(TreeNode)); //Create new node.
ptrRoot ->ptrLeft = NULL; // Set its left child to NULL.
ptrRoot ->ptrRight = NULL; // Set its right child to NULL.
ptrRoot ->iData = iData; //Insert the data value into the new node.
}
else if ( iData < ptrRoot->iData) // traverse to the left of p
ptrRoot->ptrLeft = fnInsert_BST(ptrRoot->ptrLeft, iData);
else if( iData > ptrRoot->iData) // traverse to the right of p
ptrRoot->ptrRight = fnInsert_BST(ptrRoot->ptrRight, iData);
else
printf("\nDuplicate node");
return(ptrRoot);
}//End of function

void main(void)
{
TreeNode *ptrRoot;
int iChoice;
int iData;
clrscr();
ptrRoot = NULL;
while(1)
{
printf("\n1. Insert a node");
printf("\n2. Delete a node");
printf("\n3. Inorder Traversal");
printf("\n4. Postorder Traversal");
printf("\n5. Preorder Traversal");
printf("\n6. Exit");
printf("\n4. Enter your choice");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("\nEnter Node value");
scanf("%d",&iData);
ptrRoot = fnInsert_BST(ptrRoot,iData);
break;
case 2:
break;
case 3:
fnRecursiveInorder(ptrRoot);
break;
case 4:
fnRecursivePostorder(ptrRoot);
break;
case 5:
fnRecursivePreorder(ptrRoot);
break;
case 6:
exit(1);
}
}
}
/* Program to implement an AVL tree */
/* File Name: AVL.C */
#include <stdio.h>
#include <conio.h>
#include <alloc.h>

#define FALSE 0
#define TRUE 1

struct Node
{
int iData ;
int iBalanceFactor ;
struct Node *ptrLeft ;
struct Node *ptrRight ;
};
typedef struct Node AVLNode;

//Prototype Declaration
AVLNode * fnBuild_Tree ( AVLNode *, int, int * ) ;
AVLNode * fnDelete_Data ( AVLNode *, int, int * ) ;
AVLNode * del ( AVLNode *, AVLNode *, int * ) ;
AVLNode * fnBalance_Right ( AVLNode *, int * ) ;
AVLNode * fnBalance_Left ( AVLNode *, int * ) ;
void fnDisplay ( AVLNode * ) ;
void fnDelete_Tree ( AVLNode * ) ;

AVLNode * fnBuild_Tree ( AVLNode *ptrRoot, int iData, int *h )


// Purpose : This function inserts an element into an AVL tree.
// Input: ptrRoot is root and iData is the data value to be inserted.
// Output : Returns address of root node.
{
AVLNode *node1, *node2 ;

if ( !ptrRoot )
{
ptrRoot = ( AVLNode * ) malloc ( sizeof ( AVLNode ) ) ;
ptrRoot -> iData = iData ;
ptrRoot -> ptrLeft = NULL ;
ptrRoot -> ptrRight = NULL ;
ptrRoot -> iBalanceFactor = 0 ;
*h = TRUE ;
return ( ptrRoot ) ;
}

if ( iData < ptrRoot -> iData )


{
ptrRoot -> ptrLeft = fnBuild_Tree ( ptrRoot -> ptrLeft, iData, h ) ;
/* If ptrLeft subtree is higher */
if ( *h )
{
switch ( ptrRoot -> iBalanceFactor )
{
case 1:
node1 = ptrRoot -> ptrLeft ;
if ( node1 -> iBalanceFactor == 1 )
{
printf ( "\nRight rotation along %d.", ptrRoot -> iData ) ;
ptrRoot -> ptrLeft = node1 -> ptrRight ;
node1 -> ptrRight = ptrRoot ;
ptrRoot -> iBalanceFactor = 0 ;
ptrRoot = node1 ;
}
else
{
printf ( "\nDouble rotation, Left along %d",
node1 -> iData ) ;
node2 = node1 -> ptrRight ;
node1 -> ptrRight = node2 -> ptrLeft ;
printf ( " then Right along %d.\n", ptrRoot -> iData ) ;
node2 -> ptrLeft = node1 ;
ptrRoot -> ptrLeft = node2 -> ptrRight ;
node2 -> ptrRight = ptrRoot ;
if ( node2 -> iBalanceFactor == 1 )
ptrRoot -> iBalanceFactor = -1 ;
else
ptrRoot -> iBalanceFactor = 0 ;
if ( node2 -> iBalanceFactor == -1 )
node1 -> iBalanceFactor = 1 ;
else
node1 -> iBalanceFactor = 0 ;
ptrRoot = node2 ;
}
ptrRoot -> iBalanceFactor = 0 ;
*h = FALSE ;
break ;

case 0:
ptrRoot -> iBalanceFactor = 1 ;
break ;

case -1:
ptrRoot -> iBalanceFactor = 0 ;
*h = FALSE ;
}
}
}

if ( iData > ptrRoot -> iData )


{
ptrRoot -> ptrRight = fnBuild_Tree ( ptrRoot -> ptrRight, iData, h ) ;
/* If the ptrRight subtree is higher */
if ( *h )
{
switch ( ptrRoot -> iBalanceFactor )
{
case 1:
ptrRoot -> iBalanceFactor = 0 ;
*h = FALSE ;
break ;

case 0:
ptrRoot -> iBalanceFactor = -1 ;
break;

case -1:
node1 = ptrRoot -> ptrRight ;
if ( node1 -> iBalanceFactor == -1 )
{
printf ( "\nLeft rotation along %d.", ptrRoot -> iData ) ;
ptrRoot -> ptrRight = node1 -> ptrLeft ;
node1 -> ptrLeft = ptrRoot ;
ptrRoot -> iBalanceFactor = 0 ;
ptrRoot = node1 ;
}
else
{
printf ( "\nDouble rotation, Right along %d",
node1 -> iData ) ;
node2 = node1 -> ptrLeft ;
node1 -> ptrLeft = node2 -> ptrRight ;
node2 -> ptrRight = node1 ;
printf ( " then Left along %d.\n", ptrRoot -> iData ) ;
ptrRoot -> ptrRight = node2 -> ptrLeft ;
node2 -> ptrLeft = ptrRoot ;

if ( node2 -> iBalanceFactor == -1 )


ptrRoot -> iBalanceFactor = 1 ;
else
ptrRoot -> iBalanceFactor = 0 ;
if ( node2 -> iBalanceFactor == 1 )
node1 -> iBalanceFactor = -1 ;
else
node1 -> iBalanceFactor = 0 ;
ptrRoot = node2 ;
}
ptrRoot -> iBalanceFactor = 0 ;
*h = FALSE ;
}
}
}
return ( ptrRoot ) ;
}// End of function

AVLNode * fnDelete_Data ( AVLNode *ptrRoot, int iData, int *h )


// Purpose : This function deletes an item from the tree.
// Input: ptrRoot is the adress of root and iData is the data to be inserted.
// Output : Returns the address of the root.
{
AVLNode *node ;

if ( !ptrRoot )
{
printf ( "\nNo such Data." ) ;
return ( ptrRoot ) ;
}
else
{
if ( iData < ptrRoot -> iData )
{
ptrRoot -> ptrLeft = fnDelete_Data ( ptrRoot -> ptrLeft, iData, h ) ;
if ( *h )
ptrRoot = fnBalance_Right ( ptrRoot, h ) ;
}
else
{
if ( iData > ptrRoot -> iData )
{
ptrRoot -> ptrRight = fnDelete_Data ( ptrRoot -> ptrRight, iData, h ) ;
if ( *h )
ptrRoot = fnBalance_Left ( ptrRoot, h ) ;
}
else
{
node = ptrRoot ;
if ( node -> ptrRight == NULL )
{
ptrRoot = node -> ptrLeft ;
*h = TRUE ;
free ( node ) ;
}
else
{
if ( node -> ptrLeft == NULL )
{
ptrRoot = node -> ptrRight ;
*h = TRUE ;
free ( node ) ;
}
else
{
node -> ptrRight = del ( node -> ptrRight, node, h ) ;
if ( *h )
ptrRoot = fnBalance_Left ( ptrRoot, h ) ;
}
}
}
}
}
return ( ptrRoot ) ;
}

AVLNode * del ( AVLNode *succ, AVLNode *node, int *h )

{
AVLNode *temp = succ ;
if ( succ -> ptrLeft != NULL )
{
succ -> ptrLeft = del ( succ -> ptrLeft, node, h ) ;
if ( *h )
succ = fnBalance_Right ( succ, h ) ;
}
else
{
temp = succ ;
node -> iData = succ -> iData ;
succ = succ -> ptrRight ;
free ( temp ) ;
*h = TRUE ;
}
return ( succ ) ;
}

AVLNode * fnBalance_Right ( AVLNode *ptrRoot, int *h )


// Purpose :This function balances the tree, if right sub-tree is higher.
// Input: ptrRoot is address of the root.
// Output : Returns the address of root.
{
AVLNode *node1, *node2 ;

switch ( ptrRoot -> iBalanceFactor )


{
case 1:
ptrRoot -> iBalanceFactor = 0 ;
break;

case 0:
ptrRoot -> iBalanceFactor = -1 ;
*h = FALSE ;
break;

case -1:
node1 = ptrRoot -> ptrRight ;
if ( node1 -> iBalanceFactor <= 0 )
{
printf ( "\nptrLeft rotation along %d.", ptrRoot -> iData ) ;
ptrRoot -> ptrRight = node1 -> ptrLeft ;
node1 -> ptrLeft = ptrRoot ;
if ( node1 -> iBalanceFactor == 0 )
{
ptrRoot -> iBalanceFactor = -1 ;
node1 -> iBalanceFactor = 1 ;
*h = FALSE ;
}
else
{
ptrRoot -> iBalanceFactor = node1 -> iBalanceFactor = 0 ;
}
ptrRoot = node1 ;
}
else
{
printf ( "\nDouble rotation, ptrRight along %d", node1 -> iData );
node2 = node1 -> ptrLeft ;
node1 -> ptrLeft = node2 -> ptrRight ;
node2 -> ptrRight = node1 ;
printf ( " then ptrLeft along %d.\n", ptrRoot -> iData );
ptrRoot -> ptrRight = node2 -> ptrLeft ;
node2 -> ptrLeft = ptrRoot ;

if ( node2 -> iBalanceFactor == -1 )


ptrRoot -> iBalanceFactor = 1 ;
else
ptrRoot -> iBalanceFactor = 0 ;
if ( node2 -> iBalanceFactor == 1 )
node1 -> iBalanceFactor = -1 ;
else
node1 -> iBalanceFactor = 0 ;
ptrRoot = node2 ;
node2 -> iBalanceFactor = 0 ;
}
}
return ( ptrRoot ) ;
}//End of function

AVLNode * fnBalance_Left ( AVLNode *ptrRoot, int *h )


// Purpose : This function balances the tree, if left sub-tree is higher.
// Input : ptrRoot is address of the root.
// Output : Returns the address of root.
{
AVLNode *node1, *node2 ;

switch ( ptrRoot -> iBalanceFactor )


{
case -1:
ptrRoot -> iBalanceFactor = 0 ;
break ;
case 0:
ptrRoot -> iBalanceFactor = 1 ;
*h = FALSE ;
break ;

case 1:
node1 = ptrRoot -> ptrLeft ;
if ( node1 -> iBalanceFactor >= 0 )
{
printf ( "\nptrRight rotation along %d.", ptrRoot -> iData ) ;
ptrRoot -> ptrLeft = node1 -> ptrRight ;
node1 -> ptrRight = ptrRoot ;
if ( node1 -> iBalanceFactor == 0 )
{
ptrRoot -> iBalanceFactor = 1 ;
node1 -> iBalanceFactor = -1 ;
*h = FALSE ;
}
else
{
ptrRoot -> iBalanceFactor = node1 -> iBalanceFactor = 0 ;
}
ptrRoot = node1 ;
}
else
{
printf ( "\nDouble rotation, ptrLeft along %d", node1 -> iData ) ;
node2 = node1 -> ptrRight ;
node1 -> ptrRight = node2 -> ptrLeft ;
node2 -> ptrLeft = node1 ;
printf ( " then ptrRight along %d.\n", ptrRoot -> iData ) ;
ptrRoot -> ptrLeft = node2 -> ptrRight ;
node2 -> ptrRight = ptrRoot ;

if ( node2 -> iBalanceFactor == 1 )


ptrRoot -> iBalanceFactor = -1 ;
else
ptrRoot -> iBalanceFactor = 0 ;
if ( node2-> iBalanceFactor == -1 )
node1 -> iBalanceFactor = 1 ;
else
node1 -> iBalanceFactor = 0 ;
ptrRoot = node2 ;
node2 -> iBalanceFactor = 0 ;
}
}
return ( ptrRoot ) ;
}//End of function

void fnDisplay ( AVLNode *ptrRoot )


// Purpose : This function displays the nodes of the tree in in-order fashion.
// Input: ptrRoot is address of the root.
// Output : None.
{
if ( ptrRoot != NULL )
{
fnDisplay ( ptrRoot -> ptrLeft ) ;
printf ( "%d\t", ptrRoot -> iData ) ;
fnDisplay ( ptrRoot -> ptrRight ) ;
}
}//End of function

void main( )
{
AVLNode *ptrRoot = NULL ;
int h,iChoice,iData;

clrscr( ) ;
while(1)
{
printf("\n1. Enter new node in AVL tree");
printf("\n2. Delete a node from AVL tree");
printf("\n3. Display Tree");
printf("\n4. Exit");
printf("\nEnter your choice");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("\nEnter data");
scanf("%d",&iData);
ptrRoot = fnBuild_Tree ( ptrRoot, iData, &h );
break;
case 2:
printf("\nEnter data");
scanf("%d",&iData);
ptrRoot = fnDelete_Data ( ptrRoot, iData, &h );
break;
case 3:
printf ( "\nAVL tree:\n" );
fnDisplay (ptrRoot);
break;
case 4:
exit(1);
default:
printf("\nWrong Choice");
}
}

}
10.19 MCQ Chapter 10

1. Consider the following paragraph.


The traversal proceeds as far as possible to the left, then backs up until the first cross road, goes one step
to the right and again as far as possible to the left. Repeat this process until all nodes are visited. What does the
above paragraph describe?
(a) Breadth first traversal (b) Critical path traversal
(c) Shortest path traversal (d) Depth first traversal

Questions No. 2 and No. 3 are based on the following tree structure.

2. Consider the following five statements.


(i) It is a tree.
(ii) It is a binary tree.
(iii) It is an AVL tree.
(iv) It is a binary search tree.
(v) It is a strictly binary tree.
Which of the above statements is correct with respect to the above tree?
(a) (i) only (b) (i) and (ii) only
(c) (i) , (ii) and (iii) only (d) (i), (ii) and (iv) only (e) All

3. Consider the following four statements.


(i) Depth of the above tree is equal to 4.
(ii) C’s proper descendants are I, J, K, L only.
(iii) B’s siblings are C, D, E only.
(iv) K, L, M, N are siblings.
Which of the above statements is correct?
(a) (i) and (ii) only (b) (iii) only
(c) (ii) and (iii) only (d) (ii) only (e) (ii), (iii) and (iv) only

4. Consider the following expression tree representation.


Which of the following expressions is correct in relation to the above tree?
(a) (A*B+C)^((A+B)*C) (b) (A+B*C)^((A+B)*C) (c) (A+B*C)^( A+(B *C))
(d) (A+B^C)*((A+B)*C) (e) (A+B*C)^( A+B*C)

5. Consider the following binary search tree algorithm.


Public Bsnode search ( Bsnode p, int el) {
While (p!=null)
if(el==p.key)
return p;
else
if (el < p.key)
……(i)……..;
else …….(ii)………;
…………(iii)………;
}
Identify suitable entries to fill in the blank positions labeled (i), (ii), (iii) of the above algorithm so that it
will insert el to the binary search tree.
(a) (i) p = p.right (ii) p =p.left; (iii) return null;
(b) (i) p = p.left (ii) p =p.right; (iii) return null;
(c) (i) p = p.left.next (ii) p =p.right.next; (iii) return null;
(d) (i) p = p.right.next (ii) p =p.left.next; (iii) return null;
(e) (i) p = p.right (ii) p =p.left; (iii) return null;

6. A Game tree
(a) A special type of Binary tree
(b) A special type of m-ary tree
(c) A special type of binary search tree
(d) None of these

7. Consider the following statement (Algorithm Segment).


“The method one uses to replace the node being deleted by the rightmost node in its left sub tree or
leftmost node in its right sub tree.”
What does the above statement (algorithm segment) intend to do?
(a) Deleting a node from an AVL, if deleting node has both a left and a right child
(b) Deleting a node from a binary tree
(c) Deleting a node from a binary search tree, if deleting node has both a left and a right child
(d) Deleting a node from a binary search tree, if deleting node is a leaf node

8. Consider the following table.

Which of the above would be correct for inserting a new node into an AVL tree?
(a) (i) only (b) (i) and (ii) only (c) (i), (ii) and (iii) only
(d) (i) (ii) (iii) and (iv) only. (e) (i) (iii) and (iv) only

9. If a binary tree is threaded for an inorder traversal order, a NULL left link of any node is replaced by the
address of its
(a) successor (b) predecessor
(c) root (d) None of these

10. Which of the following process is faster for threaded trees compared with their unthreaded counterparts ?
(a) Insertion (b) Deletion
(c) Traversal (d) None of these

11. Which of the following statement is TRUE in view of a threaded binary tree ? It can have -
(a) NULL links but no structural link. (b) only structural links but no NULL links
(c) structural links and NULL links (d) None of these

12. Which of the following steps is performed first for inorder traversal of a binary tree ?
(a) Traversal of the left subtree in postorder
(b) Processing of "the root node.
(c) Traversal of the left sub tree in inorder.
(d) None of these

13. The preorder traversal of a binary tree begins with


(a) processing of the root node (b) traversal of the right subtree in preorder
(c) None of the above.

14. If a binary tree is threaded for an inorder traversal order, a NULL right link of any node is replaced by the
address of its
(a) successor (b) predecessor
(c) root (d) None of these

15. The postorder traversal of a binary tree begins with


(a) the post order traversal of the left subtree
(b) processing of the root node II
(c) the post order traversal of the right subtree
(d) None of these

16. The inorder traversal of some binary tree produced the sequence DBEAFC, and the postorder traversal of
the same tree produced the sequence DEBFCA. Which of the following is a correct preorder traversal
sequence. ?
(a) DBAECF (b) ABEDFC
(c) ABDECF (d) None of these

17. The inorder traversal of some binary tree produced the sequence DBEAFC, and the postorder traversal of
the same tree produced the sequence DEBFCA. What will be the total number of nodes in the left subtree of
the given tree ?
(a) 1 (c) 5
(b) 4 (d) None of these

18. The inorder traversal of some binary tree produced the sequence CBDAFE, and the postorder traversal of
the same tree produced the sequence CDBFEA. What will be the total numrer
of nodes in the right subtree of the given tree ?
(a) 2 (b) 3
(c) 4 (d) None of these
19. The inorder traversal of some binary tree produced the sequence CBDAFE, and the postorder traversal of
the same tree produced the sequence CDBFEA. What will be the total numrer of nodes in the left subtree
of the given tree ?
(a) 2 (b) 3
(c) 5 (d) None of these

20. The inorder traversal of some binary tree produced the sequence CBDA, and the postorder traversal of the
same tree produced the sequence CDBA. Which of the following statement is TRUE for the given tree ?
Its :
(a) left subtree is. empty, and the total number of nodes in its right subtree is 3.
(b) right subtree is empty, and the total number of nodes in its left subtree is 3.
(c) both left subtree and right subtree must be nonempty
(d) None of the above

21. Which of the following statements is TRUE in view of the threaded binary trees ?
(a) Insertion into a threaded tree is time-consuming but deletion from it is not
(b) both insertions into and deletions from a threaded tree are more time-consuming.
(c) deletion from a threaded tree is time-consuming but the insertion into it is not.
(d) None of these

22. Searching of a node in lexical ordered binary tree is fast, because the number of comparisons
needed to determine if an item exists in the tree will be :
(a) 1 always (b) 2 always
(c) 0 always (d) None of these

23. Which of the following statements is TRUE in view of the lexical ordered binary tree ?
(a) Its left subtree contains nodes whose keys are lexically greater than the key associated with the root
node.
(b) Its left subtree contains nodes whose keys are lexically less than the key associated with the root node.
(c) Its root node contains a key which is lexically greater than most of the keys associated with the nodes
of the right subtree.
(d) None of these

24. If for the given directed tree outdegree of every node is exactly equal to 2 or 0, and the number of nodes at I
is 2 raised to the power of I-1, the tree is called:
(a) binary tree (b) complete binary tree
(c) m-ary tree (d) None of these

25. While performing inorder threading of a binary tree, if the left link represents a structural link, then it is:
(a) replaced with a NULL link (b) left unchanged
(c) replaced with a new thread link. (d) None of these

26. While performing inorder threading of a binary tree, if the right link represents a NULL link, then it is:
(a) replaced with a structural link (b) left unchanged
(c) replaced with a new thread link. (d) None of these

27. For which of the following trees, is the process of traversal is faster than others ?
(a) Threaded binary trees (b) Lexical ordered binary tree
(c) m-ary tree (d) None of these
28. Level of any node of a tree is:
(a) its distance from the root (b) height of its left subtree minus height of its right subtree
(c) height of its right subtree minus height of its left subtree (d) None of these

29. Which of the following statements is TRUE in view of a threaded storage representation of
a binary tree ?
(a) Its inorder threading is different from its postorder threading
(b) The number of NULL links in a threaded binary tree is very small, but it is non-zero.
(c) The number of thread links is reduced to zero.
(d) None of these

30. A dynamic data structure where we can search for desired records in 0 (log n) time is:
(a) heap (b) binary search tree
(c) cicularly linked list (d) array

31. Inorder traversal of the binary search tree implies visiting records in:
(a) the order of increasing magnitude of their key
(b) the order of decreasing magnitude of their key
(c) arbitrary order
(d) None of these

32. A threaded binary tree has following problem:


(a) more time consuming tree traversal
(b) nonsequential memory allocation
(c) additional storage requirement
(d) None of these

33. The tree traversal technique in which the root is traversed before its children is known as
(a) post order traversal (b) pre-order traversal
(c) inorder traversal (d) None of these

34. The maximum number of nodes in a binary tree of depth 5, is :


(a) 31 (b) 16
(c) 32 (d) 15

35. Post order traversal of a binary tree starts with


(a) postorder traversal of the left subtree
(b) postorder traversal of the right subtree
(c) processing of the root node
(d) none of the above

36. How many cycles must be contained in a tree?


(a) I (b) at least
(c) zero (d) 2
37. Suppose that each node in a tree is represented by a self referential structure having two pointers to point to
its left and right children. These pointers are set to NULL if the corresponding child is empty. How many
NULL pointers does a binary tree with "n" internal nodes have?
(a) n (b) n + 1
(c) n – 1 (d) The number depends on the shape of the tree.

38. If the inorder and preorder traversal of a binary tee are D, B, F, E, G, H, A, C and A, B, D, E, F, G, G, H, C
respectively then the postorder traversal of that tree is :
(a) D, F, G, A, B, C, H, E, (b) F, H, D, G, E, B, C, A
(c) C, G, H, F, E, D, B, A (d) D, F, H, G, E, B, C, A

39. In a binary tree, the number of terminal or leaf nodes is 10. The number of nodes with two
children is :
(a) 9 (c) 15
(b) 11 (d) None of the above

40. Number of all possible binary trees with 4 nodes is


(a) 14 (b) 34
(c) 24 (d) none of the above

41. Number of nodes in a complete binary tree of depth k is


(a) 2k (b) 2k
(c) 2k - 1 (d) None of the above

42. The number of rooted binary trees with n nodes is, (GATE-1990)
(a) equal to the number of ways of multiplying (n + 1) matrices
(b) equal to the number of ways of arranging n out of 2n distinct elements.
(c) equal to 1/(n + 1)2n Cn
(d) equal to n !

43. The toral external path length, EPL, of a binary treewith n external nodes is, (EPL= L lw where lw is the
path length of external node w), (GATE-1990)
2
(a) ≤ n always (b) ≥ n log2 n always
2
(c) equal to n always (d) O(n) for some special trees

44. A binary tree T has n leaf nodes. The number of nodes of degree 2 in T is (GATE-1995)
(a) log2 n (b) n - 1
(c) n (d) 2n

45. In the balanced binary tree in Figure given below, how many nodes will become unbalanced
when a node is inserted as a child of the node "g"? (GATE-1996)
(a) 1 (b) 3
(c) 7 (d) 8
46. Which of the following sequences denotes the post order traversal sequence of the tree of
Question above? (GATE-1996)
(a) f e g c d b a (b) g c b d a f e
(c) g c d b f e a (d) f e d g c b a

47. A binary search tree contains the values 1, 2, 3, 4, 5, 6, 7, 8. The tree is traversed in pre-order
and the values are printed out. Which of the following sequences is a valid output? (GATE-1997)
(a) 5 3 1 2 4 7 8 6 (c) 5 3 2 4 1 6 7 8
(b) 5 3 1 2 6 4 8 7 (d) 5 3 1 2 4 7 6 8

48. The minimum number of record movements required to merge five files A (with 10 records),
B (with 20 records), C (with 15 records), D (with 5 records) and E (with 25 records) is: (GATE-1999)
(a) 165 (c) 75
(b) 90 (d) 65

49. Consider the following nested representation of binary trees: (X Y Z) indicates Y and Z are the left and
right subtrees, respectively, of node X. Note that Y and Z may be NULL, or further nested. Which of the
following represents a valid binary tree? (GATE-2000)
(a) (1 2 (4 5 6 7)) (b) (1 ( (2 3 4) 5 6) 7)
(c) (1 (2 3 4)(5 6 7)) (d) (1 (2 3 NULL) (4 5))

50. Let LASTPOST, LASTIN and LASTPRE denote the last vertex visited in a postorder, inorder and preorder
traversal, respectively, of a complete binary tree. Which of the following is always true? (GATE-2000)
(a) LASTIN=LASTPOST (b) LASTIN=LASTPRE
(c) LASTPRE=LASTPOST (d) None of the above

51. The number of possible ordered trees with 3 nodes A, B , C is


(a)16 (b) 12 (c) 6 (d) 10

52. A binary tree in which every non-leaf node has non-empty left and right subtrees is called a strictly binary
tree. Such a tree with 10 leaves
(a) cannot have more than 19 nodes (b) has exactly 19 nodes
(c) has e~actly 17 nodes (d) cannot have more than 17 nodes

53. The depth of a complete binary tree with on' nodes is (log is to the base two)
(a) log(n+l) - 1 (b) log(n)
(c) log (n-l) + 1 (d) log (n) + 1

54. Which of the following traversal techniques lists the nodes of a binary search tree in ascending order?
(a) Post-order (b) In-order (c) Pre-order (d) None of the above
55. The number of possible binary trees with 3 nodes is
(a) 12 (b) 13 c)5 (d) 15

56. The number of possible binary trees with 4 nodes is


(a) 12 (b) 13 (c) 14 (d) 15

57. Which of the following remarks about Trie Indexing is not true?
(a) It is efficient in dealing with strings of variable length.
(b) It is efficient if there are few number of data items.
(c) The number of disk accesses can't exceed the length of the particular string that is searched.
(d) It can handle insertions and deletions, dynamically and efficiently.

58. Which of the following remarks about Trie Indexing is not true?
(a) It is an m-ary tree.
(b) It is a search tree of order m.
(c) Successful searches should terminate in leaf nodes.
(d) Unsuccessful searches may terminate at any level of the tree structure.

59. A binary tree has n leaf nodes. The number of nodes of degree 2 in this tree is
(a) 10g2n (b) n-l (c) n (d) 2n

60. The number of binary trees with 3 nodes which when traversed in post-order gives the
sequence A, B, C is
(a) 3 (b) 9 (c) 7 (d) 5

61. A 3-ary tree is a tree in which every internal node has exactly 3 children. The number of leaf nodes in such a
tree with 6 internal nodes will be
(a) 10 (b) 23 (c) 17 (d) 13

62. A binary search tree contains the values - 1, 2, 3, 4, 5, 6, 7, and 8. The tree is traversed in preorder and the
values are printed out. Which of the following sequences is a valid output?
(a) 5 3 1 2 4 7 8 6 (b) 5 3 1 2 6 4 9 7
(c) 5 3 2 4 1 6 7 8 (d) 5 3 1 2 4 7 6 8

63. Which of the following need not be a binary tree?


(a) Search tree (b) Heap (c) AVL-Tree (d) B-Tree

64. What is the maximum total number of nodes in a tree that has N levels? Note that the root is level (zero)
(a) 22N (b) 2N+ 1-1 (c) 2N - 1 (d) 2N - 2N.

65. How many different binary search trees can be made from three nodes that contain the key values 1,2 and 3?
(a) 30 (b) 20 (c) 10 (d) 15.

66. Tell whether the following tree is:


(a) complete (b) full (c) heap (d) None of these

67. Tell whether the following tree is:

(a) complete and heap (b) full (and complete) (c) full (d) heap.

68. Tell whether the following tree is:

(a) heap (b) heap (and complete) (c) full (and complete) (d) None of these.

69. How many different binary trees can be made from three nodes that contain the key values 1,2 and 3?
(a) 30 (b) 20 (c) 10 (d)5.

Solutions:
1. d 2. a 3. b 4. b 5. b 6. b 7. c 8. d 9. b 10. c 11. d 12. a 13. a 14. a 15. a
16. c 17. d 18. a 19. b 20. b 21. b 22. d 23. b 24. b 25. b 26. c 27. a 28. a 29. a 30. b
31. a 32. d 33. b 34. a 35. a 36. c 37. b 38. b 39. a 40. a 41. d 42. c 43. b 44. b 45. b
46. c 47. d 48. c 49. c 50. b 51. b 52. b 53. a 54. b 55. c 56. c 57. b 58. b 59. b 60. d
61. d 62. d 63. d 64. b 65. d 66. d 67. a 68. c 69. a

10.20 Exercise Chapter 10

1. What is a tree? Give a suitable example with a neat diagram.

2. What is a binary tree? Write a C program to insert new nodes to a binary tree and delete a given node from a
binary tree.

3. State thw two different ways to represent binary tree and explain.

4. Write down the recursive algorithms and find their time complexity for (i) preorder traversal (ii) inorder
traversal and (iii) postorder traversal of a binary tree
5. What is an expression tree? Create expression tree from the following expression and explain the
construction process.
a - b * c + d - log e / f

6. What is a binary search tree? Write a C program to insert new nodes to a binary search tree and delete a
given node from a binary search tree.

7. What is a Heap? Write algorithms to insert new nodes to a heap and delete a given node from a heap.

8. What is a weight balanced binary tree? Briefly describe the Huffman tree and Huffman algorithm.

9. What is a height balanced binary tree? Write algorithms to insert new nodes to a height balanced binary tree
and delete a given node from a height balanced binary tree.

10. What is a Threaded binary tree? Write algorithms to (i) find the inorder successor of a node for a threaded
binary tree, (ii) find the inorder predecessor of a node for a threaded binary tree, (iii) perform the inorder
traversal in a threaded binary tree.

11. What is a m way search tree? Write a application of m way search tree and explain.

12. What is a balanced m way search tree? Write algorithms to insert new nodes to a balanced m way search
tree and delete a given node from a balanced m way search tree.

13. What is a Red-black tree?explain with a suitable diagram.


Chapter 11
GRAPH
11.1 Definition
Graph is non linear data structure. Mathematically graph can be defined by the pair G = (V, E) where,
V = finite and non empty set of vertices.
E = set of edges which are the pair of vertices.
For the graph shown in figure 11.1,
V = {1, 2, 3, 4}
E = {<1, 2>, <1, 3>, <1, 4>, <2, 3>, <3, 4>}
11.2 Basic Terminologies
Undirected graph: A graph, which has unordered pair of vertices, is called undirected graph. In an undirected
graph, pictorially the edges have no direction. Suppose there is an edge between two vertices V 0 and V1, then
the edge can be represented as <V0, V1> or <V1, V0>. The graph shown in the figure 11.1 is an undirected
graph.
1 2

4 3
Figure 11.1: An example of an undirected graph

Directed graph: It is a graph in which each edge is represented by an ordered pair of vertices <V 1, V2> where
V1 is the tail and V2 is the head of the edge. In this type of graph each edge has direction, means <V 1, V2> and
<V2, V1> will represent different edges. Directed graph is also known as diagraph. The directed graph shown in
figure 11.2 can be represented as:
V = {1, 2, 3, 4}
E = {<1, 2>, <2, 3>, <3, 1>, <3, 4>, <4, 1>}

1 2

4 3
Figure 11.2: An example of a directed graph
Weighted graph: A graph is said to be weighted if every edge in the graph is assigned some non-negative value
as weight. The weight may be the distance of the edge or the cost to travel along the edge or some other positive
values depending on some parameters. A weighted graph is also known as network. The graph shown in the
figure 11.3 is an example of a weighted graph.
10
1 8
9
2
12
3
Figure 11.3: An example of a weighted directed graph
Adjacent nodes: A node V1 is adjacent to or neighbor of another node V 2 if there is an edge from node V 1 to
node V2. In figure 11.1, node 1 is adjacent to node 2 and node 2 is adjacent to node 1 also. But in figure 11.2,
node 1 is adjacent to node 2 and node 2 is adjacent from node 1.

Incidence: In an undirected graph the edge <V0, V1> is incident on nodes V0 and V1. In a diagraph the edge
<V0, V1> is incident from node V0 and is incident to node V1.

Path and length of path: Path is a sequence of vertices and edges, in which any two consecutive edges are
incident and no vertex is repeated. The total number of edges on a path is called its length.

2 4
e1 e2 e3 e4

e5 e6
1 3 5

e7 e8
6

Figure 11.4

In the graph shown in figure 11.4 some paths and their path lengths are given below.
Path length
1 e1 2 e2 3 e3 4 e4 5 4
4 e3 3 e2 2 e11 3
1 e5 3 e8 6 2

Cycle and Hamiltonian cycle: A cycle is a path that starts and ends at the same vertex. A cycle is a path
where the last vertex is adjacent to the first. A cycle in which no vertex repeats (such as 1-2-3-1 verus 1-2-3-2-
1) is said to be simple. The shortest cycle in the graph defines its girth, while a simple cycle which passes
through each vertex is said to be a Hamiltonian cycle. In Figur 11.1 the hamiltonian cycle is 1-2-3-4-1. But in
figure 11.4 there is no hamiltonian cycle.

Directed acyclic graph(DAG): A directed acyclic graph is a directd graph that has no cycle. For certain
applications it is convenient to deal with graphs that contain no cycles. For example, a tree is a special kind of
graph that contains no cycles. Obviously, all trees are DAGs. However, not all DAGs are trees. Figure 11.5
shows example of two directed acyclic graph.

1 1

2 3 2 3

4 5 6 7 4
Figure 11.5

Distance: Distance between two vertices V1 and V2 is the length of the shortest path between V1 and V2. In the
figure 11.4, distance between vertices 1 and node 5 is 2.

Walk: Walk is a sequence of vertices and edges, in which any two consecutive edges are incident and vertex
and edge both can be repeated. In figure 11.4, 1 e1 2 e2 3 e6 5 e6 3 e8 6 e7 1 is a walk.

Tour: Tour is a sequence of vertices and edges, in which any two consecutive edges are incident and no edge is
repeated. In figure 11.4, 1 e1 2 e2 3 e3 4 e4 5 e6 3 e8 6 is a tour.

Degree: Number of nodes adjacent from a vertex V is called degree of V and is represented as d g (V). In the
figure 11.1, the degree of the vertices of the graph is:
dg (1) = 3
dg (2) = 2
dg (3) = 3
dg (4) = 2

Indegree: The indegree of a vertex in a graph is the number of edges coming to that vertex or the number of
edges incident to it. In figure 11.6, the indegrees of vertices 1, 2, 3, 4, 5 and 6 are 0, 1, 1, 2, 2 and 2
respectively.

2 4

1 6

3 5

Figure 11.6

Outdegree: The outdegree of a vertex in a graph is the number of edges going outside of that vertex or the
number of edges incident from it. In figure 11.6, the outdegree of vertices 1, 2, 3, 4, 5 and 6 are 2, 3, 1, 1, 2 and
0 respectively.

Source: In a graph, a vertex which has no incoming edges, but has outgoing edges, is called a source. The in
degree of a source vertex is 0. In figure 11.6, node 1 is a source vertex.

Sink: A vertex, which has no outgoing edges, but has incoming edges, is called a sink. The outdegree of a sink
vertex is 0. In figure 11.6, node 6 is a sink vertex.

Pendant Vertex: In a directed graph, a vertex is said to be pendant if its indegree is equal to 1 and outdegree is
equal to 0. In an undirected graph, the degree of a pendant vertex is 1. In figure 11.7, node 5 is pendant vertex.

1 3

5
2 4
Figure 11.7

Complete graph: A graph G is complete iff every node U in G is adjacent to every other node V in G. Total
number of edges in a complete graph = n(n-1)/2 where n = total number of vertices.

4 example of a3complete graph


Figure 11.8: An

Multiple edge: Distinct edges e and e′ are called multiple edges if they connect the same end points i.e. e → (U,
V) and e′ → (U, V). In figure 11.9, the edges e6 and e7 are multiple edges.

Loop: An edge is called loop if it has identical endpoints i.e. e → (U, U).
In figure 11.9, e3 is a loop.

Multigraph: A graph is said to be a multigraph if it contains either multiple edges or loops. Figure 11.9 shows
a multi graph.
e3

e2

e4

e1 e5
e6
e8

e7
Figure 11.9: An example of a multi graph

Chromatic number: The chromatic number of a graph is the least number of colors it takes to color its vertices
so that adjacent vertices have different colors. For example, the graph of figure 11.6 has chromatic number two.

Bipartite graph: A graph G is called bipartite if the vertex set V of G can be partitioned into two disjoint
subsets V1 and V2 such that every edge joins a vertex in V 1 with a vertex in V2. Figure 11.10 shows a bipartite
graph.

V1
V2

V1

V2
Figure 11.10: Example of bipartite graph

Planar graph: A graph is called planar if it can be drawn in the plane so that edges intersect only at their end
points.
Properties:
1. For any planar graph G with n ≥ 3 vertices has at most 3n – 6 edges.
2. Four color theorem: Every planar graph can be colored using no more than four colors.
3. Kuratowski’s theorem: Graph G is non-planar iff it has a sub graph K5 or K3, 3.

Figure 11.11(a): K5 Figure 11.11(b): K3, 3

Connected graph: An undirected graph is connected if there is a path from any node of graph to any other
node, or any node is reachable from any other node. Figure 11.12(a) shows a connected graph.
e2
1 2 e3 1 2
e6
e1 e4 3 3

5 4 5 4
e5

Figure 11.12(a): Connected graph Figure 11.12(b): Disconnected graph

Articulation point: Let G be a connected graph. An articulation point of G is a vertex whose removal (along
with all adjacent edges) disconnects G. In a graph there can be more than one articulation points. In figure
11.12(a) vertex 2 is an articulation point.

Bridge: Let G be a connected graph. A bridge of G is an edge whose removal disconnects G. In figure 11.12(a)
edge e3 is a bridge.

Euler tour: An Euler tour of a connected graph G is a cycle that traverses each edge of G exactly once,
although it may visit a vertex more than once.

11.3 Representation of a graph


There are two ways; we can implement a graph in a computer program. These are:
1. Sequential representation:
In sequential representation we use a 2-dimentional array of order n X n where n is the total number of vertices
in the graph. This is a simple way to represent a graph into memory. A graph with n nodes takes n 2 space to
represent it sequentially in memory and also takes O(n2) time to perform the graph operations and to solve the
graph problems.
Adjacency Matrix:
Suppose G is a graph with n nodes and suppose the vertices of graph G are ordered and are called 1, 2, 3… n.
Then the adjacency matrix A of the graph G is an n X n matrix defined as follows:
A [i][j] = 1 if there is an edge from node i to node j.
=0 Otherwise
Such a matrix which contains only 1 or 0 is called a bit matrix or Boolean matrix. The adjacency matrix of an
undirected simple graph is a symmetric matrix. In the adjacency matrix of an undirected simple graph, the
number of 1 is twice the number of edges in the graph.

The adjacency matrix for the figure 11.13 is: The adjacency matrix for the figure 11.14
is:

1 2 3 4 5 1 2 3 4 5

1 0 1 0 0 1 1 0 1 0 0 1

2 1 0 1 1 1 2 0 0 1 1 0

3 0 1 0 1 0 3 0 0 0 1 0

4 0 1 1 0 1 4 0 0 0 0 1

5 1 1 0 1 0 5 0 1 0 0 0

10
1 2 1 2 1 2 6

3 3 2 4 7 3

5 4 5 4 5 4 8
5
Figure 11.13 Figure 11.14 Figure 11.15

Path Matrix:
Suppose G is a directed graph with n nodes and suppose the vertices of graph G are ordered and are called 1, 2,
3… n. Then the path matrix P of the graph G is an n X n matrix defined as follows:
P [i][j] = 1 if there is a path from vertex i to vertex j.
=0 Otherwise

The path matrix for the figure 11.14 is:

1 2 3 4 5

1 0 1 1 1 1

2 0 1 1 1 1

3 0 1 1 1 1

4 0 1 1 1 1

5 0 1 1 1 1
Weighted matrix:
The weighted matrix W of a weighted graph G with n nodes is an n X n matrix which can be defined as:
W [i][j] = weight of the edge <i, j> if there is an edge from i to j.
= ∞ (i.e. a large value) Otherwise.
The weighted matrix for the figure 11.15 is:

1 2 3 4 5

1 ∞ 10 ∞ ∞ 2

2 ∞ ∞ 6 7 ∞

3 ∞ ∞ ∞ 8 ∞

4 ∞ ∞ ∞ ∞ 5

5 ∞ 4 ∞ ∞ ∞

2. Linked representation:
In the adjacency matrix it is difficult to insert new nodes or edges in the graph. First of all the time complexity
for insertion or deletion of new nodes or edges in the adjacency matrix is high. More over as the array is static
in nature; some spaces in the matrix defined to represent the graph, may be wasted or due to more vertices
underflow in space may occur. Further more if the number of edges in the graph with n nodes is O(n) or
O(nlog2n), then the matrix is sparse; hence a great deal of spaces will be wasted. If the adjacency matrix of the
graph is sparse then it is more efficient to represent the graph through adjacency list. In adjacency list
representation of graph, we will maintain two lists. First list will keep track of all the nodes in the graph and
second list will maintain a list of adjacent nodes for each node to keep the track of the edges.

Structure of a node in the vertex list:


struct VNode
{
struct VNode *ptrNV; // Pointer to next vertex in the list.
int VNo; // Name of the vertex.
struct ENode *ptrAV; // Pointer to adjacent vertex to this node.
};
typedef struct VNode Vertex;

Name of the vertex

ptrNV VNo ptrAV Pointer which points to the first vertex


adjacent to this node.

Pointer which points


to the next vertex in the list.

Structure of an edge node in the edge list:


struct ENode
{
int VNo; // Name of the Vertex.
struct ENode *ptrAV; // Pointer to the destination vertex of the next edge.
};
typedef struct ENode Edge;

VNo ptrAV Pointer which points to


the next adjacent node.

Destination node of the edge

The adjacency list for the graph shown in figure 11.13 is:
1 2 5 N

2 1 3 4 5 N

3 2 4 N

4 2 3 5 N

N 5 1 2 4 N

The adjacency list for the graph shown in figure 11.14 is:
1 2 5 N

2 3 4 N

3 4 N

4 5 N

N 5 2 N

11.4 Operations on a graph


Suppose a graph is maintained in memory by the linked representation. Now we will look for the following
operations on a graph.
11.4.1 Searching in a graph:
Suppose first we want to find a vertex in the graph.
Algorithm 11.1
1. Algorithm fnFindVertex(V)
2. // Purpose : This algorithm finds whether a vertex is in the graph or not.
3. // Input : We have to search for the vertex V in the vertex list.
4. // Output : This algorithm returns the address of the vertex V for a successful search. Otherwise it
returns //NULL.
5. {
6. Vertex *ptrTemp;
7. ptrTemp = ptrStart; // A global variable ptrStart contains the address of the first vertex in the vertex
list
8. while(ptrTemp != NULL) // Continue until vertex list is not empty.
9. {
10. if(ptrTemp->VNo == V)
11. return ptrTemp;
12. ptrTemp = ptrTemp->ptrNV; //Go to next vertex in the vertex list.
13. }
14. return NULL; //Return NULL if vertex does not exist.
15. }//End of algorithm

Now we will find an edge (U, V) in the graph. For this purpose first find the vertex U in the vertex list and then
search the edge list of U to find the vertex V.

Algorithm 11.2
1. Algorithm fnFindEdge(U,V)
2. // Purpose : This algorithm finds whether an edge is in the graph or not.
3. // Input : We have to search for the edge (U, V) in the edge list.
4. // Output : This algorithm returns the address of the edge (U, V) for a successful search. Otherwise it
returns //NULL.
5. {
6. Vertex *ptrTemp1;
7. Edge *ptrTemp2;
8. ptrTemp1 = ptrStart; // A global variable ptrStart contains the address of the first vertex in the vertex
list
9. // Find the vertex U from the vertex list.
10. while(ptrTemp1 != NULL) // Continue until vertex list is not empty.
11. {
12. if(ptrTemp1->VNo == U) // Get the address of the node U from the vertex list.
13. break;
14. ptrTemp1 = ptrTemp1->ptrNV; // Go to next vertex in the vertex list.
15. }
16. // Find the vertex V from the edge list of U.
17. if(ptrTemp1 != NULL) // If U exists in the vertex list, then traverse the edge list of the vertex U.
18. {
19. ptrTemp2 = ptrTemp1->ptrAV;
20. while(ptrTemp2 != NULL) // Continue until the edge list of vertex U is NULL.
21. {
22. if(ptrTemp2->VNo == V)
23. return ptrTemp2;
24. ptrTemp2 = ptrTemp2->ptrAV; // Go to next vertex in the edge list.
25. }
26. }
27. return NULL;
28. }// End of algorithm

Insertion in a graph:
Insert new vertex
To insert a new vertex V the following steps are performed:
Step 1: First check whether V exists in the list or not. If V is already in the list then exit.
Step 2: Create new vertex.
Step 3: Copy V to the data field of the newly created vertex.
Step 4: Set NULL to the adjacent vertex list of it.
Step 5: Set V as the first vertex in the list.
Algorithm 11.3 describes the above steps.
Algorithm 11.3
1. Algorithm fnInsertNode(V)
2. // Purpose : This algorithm inserts a new node at the first of the vertex list.
3. // Input : V is the vertex number to be inserted in the graph.
4. // Output : None.
5. {
6. Vertex ptrNewVertex;
7. if(fnFindVertex(V) != NULL)
8. {
9. print “Duplicate Vertex”;
10. exit();
11. }
12. ptrNewVertex = (Vertex *)(malloc(sizeof(Vertex))); // Create a new vertex.
13. ptrNewVertex->VNo = V; //Copy V to data field of new vertex.
14. ptrNewVertex->ptrAV = NULL; //Set edge field of new vertex to NULL.
15. ptrNewVertex->ptrNV = ptrStart; //Set current first vertex as the next vertex of new one.
16. ptrStart = ptrNewVertex; //Now new vertex is the first vertex in the vertex list.
17. }// End of algorithm

Inert new edge


To insert a new edge (U, V) perform following steps:
Step 1: If U is not in the vertex list or edge (U, V) already exist, then exit.
Step 2: Create new edge.
Step 3: Set V in the data field of new edge.
Step 4: Inert newly created edge V at the beginning of the adjacency vertex list of U.
Algorithm 11.4 describes the process.
Algorithm 11.4
1. Algorithm fnInsertEdge(U, V)
2. // Purpose : This algorithm inserts a new edge at the last of the edge list of vertex U.
3. // Input : The edge is from vertex U to vertex V.
4. // Output : None.
5. {
6. Vertex *ptrLoc1;
7. Edge *ptrLoc2;
8. ptrLoc1 = fnFindVertex(U); //Get the address of the vertex U.
9. ptrLoc2 = fnFindEdge(U, V); // Get the address of the edge (U, V).
10. if(ptrLoc1 != NULL and ptrLoc2 === NULL) // If U is in the vertex list but V is not in the edge list of U.
11. {
12. ptrNewEdge = (Edge *)(malloc(sizeof(Edge))); //Create a new edge.
13. ptrNewEdge->VNo = V; //Copy V to the data field of new edge.
14. ptrNewEdge->ptrAV = ptrLoc1->ptrAV; //Insert V in the edge list of U.
15. ptrLoc1->ptrAV = ptrNewEdge; //Set vertex V as the first edge in the edge list of U.
16. }
17. } // End of algorithm

11.4.3 Deletion from graph:


Delete a vertex
The steps to delete a vertex V are followings:
Step 1: If V does not exist, then exit.
Step 2: Delete all the edges ending at V.
Step 3: Delete all the edges starting with V.
Step 4: Delete V from the list.
Algorithm 11.5 describes the steps to delete a vertex.
Algorithm 11.5
1. Algorithm fnDeleteVertex(V)
2. // Purpose : This algorithm deletes an existing node from the vertex list of a graph.
3. // Input : V is the vertex number to be deleted from the vertex list.
4. // Output : None.
5. {
6. Vertex *ptrLoc1, *ptrTemp;
7. Edge *ptrLoc2, *ptrLoc3;
8. ptrLoc1 = fnFindVertex(V); //Get the address of the vertex V in the vertex list.
9. ptrLoc2 = ptrLoc1->ptrAV; //Get the address of the first vertex in the edge list of V.
10. while(ptrLoc2 ≠ NULL)
11. {
12. ptrLoc3 = ptrLoc2->ptrAV; //Delete all the edges from the edge list of V.
13. free(ptrLoc2);
14. ptrLoc2 = ptrLoc3;
15. }
16. ptrTemp = ptrStart;
17. while(ptrTemp ≠ NULL)
18. {
19. U = ptrTemp->VNo;
20. // Get the address of the edges ending at the vertex V. //Delete all the edges ending at V.
21. fnDeleteEdge(U,V);
22. ptrTemp = ptrTemp->ptrNV;
23. }
24. ptrTemp = ptrStart;
25. while(ptrTemp->ptrNV ≠ ptrLoc1)
26. ptrTemp = ptrTemp->ptrNV; //Delete the node V from the vertex
list.
27. ptrTemp->ptrNV = ptrLoc1->ptrNV;
28. free(ptrLoc1);
29. }// End of algorithm

Delete an edge
The steps to delete an edge (U, V) are:
Step 1: If the edge (U, V) does not exist, then exit.
Step 2: First go the vertex U in the vertex list.
Step 3: Delete vertex V from the adjacency vertex list of U.
See algorithm 11.6.
Algorithm 11.6
1. Algorithm fnDeleteEdge(U, V)
2. // Purpose : This algorithm deletes the edge <U, V> from the edge list of vertex U.
3. // Input : The edge is from vertex U to vertex V.
4. // Output : None.
5. {
6. Vertex *ptrLoc1;
7. Edge *ptrLoc2,*ptrTemp2;
8. ptrLoc1 = fnFindVertex(U); //Find the address of the vertex U in the vertex list.
9. ptrLoc2 = fnFindEdge(U, V); //Find the address of the edge to be deleted.
10. if(ptrLoc1!=NULL) // If the edge exists.
11. {
12. ptrTemp2 = ptrLoc1->ptrAV;
13. //Get the address of the previous edge of <U, V> in the edge list of U.
14. while(ptrTemp2->ptrAV!=ptrLoc2)
15. ptrTemp2 = ptrTemp2->ptrAV;
16. ptrTemp2->ptrAV = ptrLoc2->ptrAV; //Set the link.
17. free(ptrLoc2);
18. }
19. }//End of Algorithm

11.5 Traversal of a graph


There are two standard ways to traverse a graph. First one is Breadth first search (BFS) and the second one is
Depth first search (DFS). The procedure BFS uses a queue to store the unvisited vertices and DFS uses a stack
for this purpose. To distinguish between the visited vertices and unvisited vertices, we give the color to each
and every node of the graph. Initially all the vertices have color WHITE to indicate that they are not yet visited.
When a node is placed into a queue or stack, then the color is changed to BLACK to indicate that the vertex is
now visited.

11.5.1 Breadth first search:


In this traversal technique, first select a vertex S to start the traversal. Initially set the color of all the vertices to
WHITE. Insert S into the queue and change its color to BLACK as now S is a visited vertex. Now delete the
front node N from the queue. Visit all the adjacent vertices of N and insert to queue those neighbors of N that
have the color WHITE. Change their colors to BLACK. Continue this procedure until queue is empty.

Algorithm 11.7
1. Algorithm fnBFS(S)
2. // Purpose : This algorithm finds the BFS traversal of a given graph.
3. // Input : S is the starting node from which we have to start the traversal.
4. // Output : None.
5. {
6. for( all the vertices j of the graph)
7. Color[j] = WHITE. //Initialize the color of all nodes to WHITE.
8. fnInsert(S); //Put the starting node S into queue.
9. Color[S] = BLACK; //Change its color to BLACK.
10. while(queue is not empty)
11. {
12. N = fnDelete(); //Remove the front node N of queue.
13. for(each adjacent vertex j of N) //Visit all the adjacent vertex of N.
14. if(Color[j] == WHITE)
15. {
16. fnInsert(j); //Insert into queue all the neighbors of N of color WHITE.
17. Color[j] = BLACK; //Change their colors to BLACK.
18. }
19. }
20. }// End of algorithm
Time Complexity: If we represent the graph using adjacency list, then we have to traverse each edge at most
twice (for an undirected graph). Hence time complexity is O (e) where e is the total number of edges.
If we represent the graph using adjacency matrix, then we have to check n entries in the matrix for each
vertex to find its adjacent vertices. As there are total n vertices, so time complexity is O (n2).

Example 11.1: Consider the graph shown in the figure 11.16. Suppose we have to find the BFS traversal of this
graph starting from vertex 1.

2 3 4

Figure 11.16
Initially:
Color[1] = Color[2] = Color[3] = Color[4] = Color [5] = WHITE

STEP 1:
Insert starting node 1 into queue and change its Color to Black.

2 3 4

1
5

Status of the graph Content of the queue


Traversal = Ф

STEP 2:
Delete front node 1 from queue and insert into QUEUE nodes 2, 3 and 4 as their color is WHITE.

4
2 3 4 3
2
5

Status of the graph Content of the queue


Traversal = 1
STEP 3:
Delete front node 2 from queue. The adjacent nodes of 2 are vertex 1 and vertex 5. But only the vertex 5 has
color WHITE. So insert only vertex 5 into the queue.

1
5
4
2 3 4 3

Status of the graph Content of the queue


Traversal = 1 2
STEP 4:
Delete node 3 from queue. As all the neighbors of vertex 3 have color BLACK, so no more insertion into queue
takes place.

1
5
4
2 3 4

Status of the graph Content of the queue


Traversal = 1 2 3
STEP 5:
Delete node 4 from queue without any insertion into queue.

1
5

2 3 4

Status of the graph Content of the queue


Traversal = 1 2 3 4
STEP 6:
Delete node 5 from queue.

2 3 4

5
Status of the graph Content of the queue
Traversal = 1 2 3 4 5
As now the queue is empty, so traversal is finished.

11.5.2 Depth first search:


In this traversal technique, we use a stack instead of queue. Like the BFS traversal, initially set the color of all
the vertices to WHITE. Insert starting vertex S into the stack and change its color to BLACK. Now delete the
top node N from the stack. Visit all the adjacent vertices of N and insert to stack those neighbors of N that have
the color WHITE. Change their colors to BLACK. Continue this procedure until stack is empty.

Algorithm 11.8
1. Algorithm fnDFS(S)
2. // Purpose : This algorithm finds the DFS traversal of a given graph.
3. // Input : S is the starting node from which we have to start the traversal.
4. // Output : None.
5. {
6. for( all the vertices j of the graph)
7. Color[j] = WHITE; //Initialize the color of all nodes to WHITE.
8. fnPush(S); //Push the starting node S into stack.
9. Color[S] = BLACK; //Change its color to BLACK.
10. while(stack is not empty)
11. {
12. N = fnPop(); //Pop the top node N of the stack.
13. for(each adjacent vertex j of N) //Visit all the adjacent vertex of N.
14. if(Color[j] == WHITE)
15. {
16. fnPush(j); //Push into stack all the WHITE neighbors of N.
17. Color[j] = BLACK; //Change their colors to BLACK.
18. }
19. }
20. }// End of algorithm

Example 11.2: Consider the graph shown in the figure 11.16. Suppose we have to find the DFS traversal of this
graph starting from vertex 1.

Initially:
Color[1] = Color[2] = Color[3] = Color[4] = Color [5] = WHITE

STEP 1:
Insert starting node 1 into stack and change its color to Black.

2 3 4

1
5
Status of the graph Content of the stack
Traversal = Ф

STEP 2:
Delete top node 1 from stack and insert into stack nodes 2, 3 and 4 (the neighbors of 1) as their color is WHITE.
Change the color of the vertices (2, 3 and 4) to BLACK.

4
2 3 4 3
2
5

Status of the graph Content of the stack


Traversal = 1

STEP 3:
Delete top node 4 from stack. The adjacent nodes of 4 are vertex 1 and vertex 5. But only the vertex 5 has color
WHITE. So insert only vertex 5 into the stack.

5
2 3 4 3
2
5

Status of the graph Content of the stack


Traversal = 1 4

STEP 4:
Delete node 5 from queue. As all the neighbors of vertex 3 have color BLACK, so no more insertion into stack
takes place.

2 3 4 3
2
5

Status of the graph Content of the stack


Traversal = 1 4 5

STEP 5:
Delete node 3 from stack without any insertion into stack.

2 3 4

2
5

Status of the graph Content of the stack


Traversal = 1 4 5 3

STEP 6:
Delete node 2 from stack.

2 3 4

Status of the graph Content of the stack


Traversal = 1 4 5 3 2
As now the stack is empty, so traversal is finished.

11.6 Shortest path calculation (Dijkstra’s algorithm)


Dijkstra's algorithm is a greedy algorithm for solving the single-source, shortest-path problem on an edge-
weighted graph in which all the weights are non-negative. It finds the shortest paths from some initial vertex,
say s, to all the other vertices one-by-one. The essential feature of Dijkstra's algorithm is the order in which the
paths are determined: The paths are discovered in the order of their weighted lengths, starting with the shortest,
and proceeding to the longest.
Dijkstra’s algorithm keeps two sets of vertices:
U Vertices whose shortest paths have already been determined
V-U Remainder vertices.
To differentiate between these two sets, we maintain an array status []. Status[i] = Permanent denotes the vertex
i is in set U and status[i] = Temporary indicates i is in V-U. Here we maintain two more arrays: dist[] and
pred[]. Dist[i] is the length of the shortest path from source node (s) to node i. pred[i] denotes the node
preceding node i in the shortest path. Initially for all the nodes i of the graph, dist[i] = ∞ and pred[i] = NULL
and status[i] = Temporary.
Algorithm 11.9
1. Algorithm fnDijkstra (s, d, n)
2. // Purpose : This algorithm finds the shortest path between the vertex s and vertex d.
3. // Input : s is the starting vertex and d is the destination vertex. n is the total number of vertices in the
graph.
4. // Output : None.
5. {
6. for (i = 1; i <= n; i++)
7. {
8. dist[i] = ∞ ; //Initialize the arrays.
9. pred[i] = NULL;
10. status[i] = Temporary;
11. }
12. status[s] = Permanent;
13. dist[s] = 0;
14. current = s; //current keeps the track of the last permanent vertex.
15. while(current != d) //Continue the procedure until the status of the destination vertex d is Permanent.
16. {
17. dc = dist[current];
18. for(all vertices i, that are adjacent of current and status[i] = Temporary)
19. {
20. newdist = dc + W[current][i]; //W[][] is weighted matrix.
21. if(newdist < dist[i])
22. {
23. dist[i] = newdist;
24. pred[i] = current;
25. }
26. }
27. Choose node k such that status[k] = Temporary and dist[k] is smallest;
28. current = k;
29. status[k] = Permanent;
30. }
31. }// End of algorithm

Example 11.3:
8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5
Figure 11.17

Suppose here s = 1 and d = 5


Step 1:
Now current = 1.
8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5

NODE dist Pred status


1 0 NULL Permanent
2 α NULL Temporary
3 α NULL Temporary
4 α NULL Temporary
5 α NULL Temporary
6 α NULL Temporary
7 α NULL Temporary
8 α NULL Temporary
dc = dist[1] = 0
Vertices 2, 3 and 4 are adjacent to 1 and are Temporary.

dist[2] > dc + weight[1][2] => α > 0 + 8 re label 2 pred[2] = 1


dist[3] > dc + weight[1][3] => α > 0 + 2 re label 3 pred[3] = 1
dist[4] > dc + weight[1][4] => α > 0 + 7 re label 4 pred[4] = 1

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Temporary
4 7 1 Temporary
5 α NULL Temporary
6 α NULL Temporary
7 α NULL Temporary
8 α NULL Temporary

STEP 2:
dist[3] is smallest among the temporary nodes.
So, now current = 3 and make it permanent.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 7 1 Temporary
5 α NULL Temporary
6 α NULL Temporary
7 α NULL Temporary
8 α NULL Temporary

8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5

dc = dist[3] = 2
Here 4 and 7 are adjacent to 3 and are temporary.

dist[4] > dc + weight[3][4] => 7 > 2 + 4 re label 4 pred[4] = 3


dist[7] > dc + weight[3][7] => α > 2 + 3 re label 7 pred[7] = 3

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 6 3 Temporary
5 α NULL Temporary
6 α NULL Temporary
7 5 3 Temporary
8 α NULL Temporary

STEP 3:
dist[7] is smallest among the temporary nodes.
So, now current = vertex 7 and make it permanent.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 6 3 Temporary
5 α NULL Temporary
6 α NULL Temporary
7 5 3 Permanent
8 α NULL Temporary

8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5

dc = dist[7] = 5
Here vertices 4, 5 and 8 are temporary adjacent node to 7.

dist[4] < dc + weight[7][4] 6<5+3 Don’t re label vertex 4


dist[5] > dc + weight[7][5] α>5+4 re label vertex 5 pred[5] = 7
dist[8] > dc + weight[7][8] α>5+5 re label vertex 8 pred[8] = 7

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 6 3 Temporary
5 9 7 Temporary
6 α NULL Temporary
7 5 3 Permanent
8 10 7 Temporary

STEP 4:
dist[4] is smallest among the temporary nodes.
So, now current = vertex 4 and make it permanent.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 6 3 Permanent
5 9 7 Temporary
6 α NULL Temporary
7 5 3 Permanent
8 10 7 Temporary
8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5

dc = dist[4] = 6
Here only vertex 5 is the temporary adjacent node to 4.

dist[5] < dc + weight[4][5] 9<6+9 Don’t re label vertex 5.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Temporary
3 2 1 Permanent
4 6 3 Permanent
5 9 7 Temporary
6 α NULL Temporary
7 5 3 Permanent
8 10 7 Temporary

STEP 5:
dist[2] is smallest among the temporary nodes.
So, now current = vertex 2 and make it permanent.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Permanent
3 2 1 Permanent
4 6 3 Permanent
5 9 7 Temporary
6 α NULL Temporary
7 5 3 Permanent
8 10 7 Temporary
8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5

dc = dist[2] = 8
Here vertex 6 is the temporary adjacent node to B.

dist[6] > dc + weight[2][6] α > 8 + 16 re label vertex 6 pred[6] = 2

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Permanent
3 2 1 Permanent
4 6 3 Permanent
5 9 7 Temporary
6 24 2 Temporary
7 5 3 Permanent
8 10 7 Temporary

STEP 6:
dist[5] is smallest among the temporary nodes.

So, now current = 5 and make it permanent.

NODE dist Pred status


1 0 NULL Permanent
2 8 1 Permanent
3 2 1 Permanent
4 6 3 Permanent
5 9 7 Permanent
6 24 2 Temporary
7 5 3 Permanent
8 10 7 temp
8
1 2

5 2 4 16
7

3 4 5 6
4 9 5

3 6 3 4 8 2

7 8
5
As 5 is our destination node, the process will be stopped here.
pred[5] = 7
pred[7] = 3
pred[3] = 1
So, the shortest path from 1 to 5 is
1→3→7→5
And the cost of the shortest path from vertex 1 to 5 is 9.

11.7 Warshall’s algorithm


Let G be a directed graph with n vertices 1, 2, 3……n. Suppose we want to find the path matrix of G. Warshall
gave an algorithm for this purpose. This algorithm is discussed in this section and a similar algorithm is used to
calculate the shortest path for a weighted graph.
Path Matrix:
First we define n X n Boolean matrices P0, P1………Pn as follows:
Pk[i][j] = 1 If there is a simple path from i to j which does not use any other vertices except possibly 1….k.
=0 Otherwise.
To store the path matrix of the graph G we are taking a two dimensional array P[][] with order n X n. Initially
the path matrix P is same as the adjacency matrix. Now P k[i][j] means we want to traverse from vertex i to
vertex j through vertex k. So Pk[i][j] is 1 if either we can go from vertex i to vertex j through vertex k or vertex j
is already reachable from vertex i through some previous vertices. Hence Pk[i][j] = 1 can occur only if one of
the following conditions is satisfied:
1) Pk-1[i][j] = 1 (i.e. i is reachable from j through some previous vertices)
2) Pk-1[i][k] = 1 and Pk-1[k][j] = 1 ( i.e vertex k is reachable from i and j is reachable from k)
The algorithm to find the path matrix is shown in algorithm 11.10.

Algorithm 11.10
1. Algorithm fnWarshallPM(n)
2. // Purpose : This algorithm finds the path matrix for a directed graph G.
3. // Input : n is the total number of vertices in G.
4. // Output : None.
5. {
6. //First copy the contents of adjacency matrix A to path matrix P.
7. for(i = 1;i<=n;i++)
8. for(j=1;j<=n;j++)
9. P[i][j] = A[i][j];
10. //Compute different iterations of P.
11. for(k=1;k<=n;k++) //k is the iteration number.
12. for(i=1;i<=n;i++)
13. for(j=1;j<=n;j++)
14. P[i][j] = P[i][j] || (P[i][k] && P[k][j]);
15. }// End of algorithm.

15
2
1 2
6 1
2
4 3
1
Figure 11.18

Example 11.4: The path matrix for the figure 11.18 can be calculated as follows:
First find the adjacency matrix A for the graph.

1 1 0 0
A= 0 0 1 0
0 0 0 1
1 0 1 0

Initially: P = A

1 1 0 0
P= 0 0 1 0
0 0 0 1
1 0 1 0

Iteration 1:
1 1 0 0
P1 = 0 0 1 0
0 0 0 1
1 1 1 0

Iteration 2:
1 1 1 0
2
P = 0 0 1 0
0 0 0 1
1 1 1 0

Iteration 3:
1 1 1 1
3
P = 0 0 1 1
0 0 0 1
1 1 1 1
Iteration 4:
1 1 1 1
4
P = 1 1 1 1
1 1 1 1
1 1 1 1

The final path matrix of the graph shown in the figure 11.18 is P4.

Shortest path:
Consider a directed weighted graph G with n vertices. The weighted matrix for G is an n X n matrix. The path
matrix of G tells us whether there is a path between two nodes. Now our goal is to find the path between two
vertices i and j with minimum weight or cost and for this purpose a two dimensional array S[][] is used to store
the weight of the shortest path between vertex i and vertex j. In path matrix the entry P k[i][j] is 1, if either Pk-1[i]
[j] is 1 or (Pk-1[i][k] = 1 and Pk-1[k][j] = 1). Similarly, we can calculate the shortest weighted path from vertex i
to vertex j as follows:
Sk[i][j] = min {weight of Pk-1[i][j], weight of Pk-1[i][k] + weight of Pk-1[k][j]}
= min {Sk-1[i][j], Sk-1[i][k] + Sk-1[k][j]}

The algorithm to find the shortest path is shown in algorithm 11.11.

Algorithm 11.11
1. Algorithm fnWarshallSP(n)
2. // Purpose : This algorithm finds the shortest path for all the pair of vertices of the graph G.
3. // Input : n is the total number of vertices in G.
4. // Output : None.
5. {
6. //First copy the weighted matrix or cost matrix W to shortest path matrix S.
7. for(i = 1;i<=n;i++)
8. for(j=1;j<=n;j++)
9. S[i][j] = W[i][j];
10. //Compute different iterations of P.
11. for(k=1;k<=n;k++) //k is the iteration number.
12. for(i=1;i<=n;i++)
13. for(j=1;j<=n;j++)
14. S[i][j] = min{S[i][j] , (S[i][k] + S[k][j])}; //Compute new distance between i
and j
15. }// End of algorithm

Example 11.5: The shortest path matrix for the graph shown in figure 11.18 can be calculated as follows:
First find the weighted matrix W for the graph.

15 2 ∞ ∞
W= ∞ ∞ 1 ∞
∞ ∞ ∞ 2
6 ∞ 1 ∞

Initially: S = W
15 2 ∞ ∞
S0 = ∞ ∞ 1 ∞
∞ ∞ ∞ 2
6 ∞ 1 ∞

Minimum weight Shortest path


Iteration 1:
15 2 ∞ ∞ 1, 1 1, 2 - -
1
S = ∞ ∞ 1 ∞ - - 2, 3 -
∞ ∞ ∞ 2 - - - 3, 4
6 8 1 ∞ 4, 1 4, 1, 2 4, 3 -

Iteration 2:
15 2 3 ∞ 1, 1 1, 2 1, 2, 3 -
2
S = ∞ ∞ 1 ∞ - - 2, 3 -
∞ ∞ ∞ 2 - - - 3, 4
6 8 1 ∞ 4, 1 4, 1, 2 4, 3 -

Iteration 3:
15 2 3 5 1, 1 1, 2 1, 2, 3 1, 2, 3, 4
S3 = ∞ ∞ 1 3 - - 2, 3 2, 3, 4
∞ ∞ ∞ 2 - - - 3, 4
6 8 1 3 4, 1 4, 1, 2 4, 3 4, 3, 4

Iteration 4:
11 2 3 5 1, 2, 3, 4, 1 1, 2 1, 2, 3 1, 2, 3, 4
4
S = 9 11 1 3 2, 3, 4, 1 2, 3, 4, 1, 2 2, 3 2, 3, 4
8 10 3 2 3, 4, 1 3, 4, 1, 2 3, 4, 3 3, 4
6 8 1 3 4, 1 4, 1, 2 4, 3 4, 3, 4

The final shortest path matrix of the graph shown in the figure 11.18 is S4. We indicate below how some of the
highlighted entries are obtained:

S1[4][2] = min {S0[4][2], S0[4][1] + S0[1][2]} = min {∞, 6 + 2} = 8 [Path is 4, 1, 2]


S2[1][3] = min {S1[1][3], S1[1][2] + S1[2][3]} = min {∞, 2 + 1} = 3 [Path is 1, 2, 3]
S3[1][4] = min {S2[1][4], S2[1][3] + S2[3][4]} = min {∞, 3 + 2} = 5 [Path is 1 → 3, 4]
=> [Path is 1, 2, 3, 4]
S3[4][4] = min {S2[4][4], S2[4][3] + S2[3][4]} = min {∞, 1 + 2} = 3 [Path is 4, 3, 4]
S4[1][1] = min {S3[1][1], S3[1][4] + S3[4][1]} = min {15, 5 + 6} = 11 [Path is 1 → 4, 1]
=> [Path is 1, 2, 3, 4, 1]

/*C Code to implement dijkstra’s function*/


/* File Name: Dijkstra.c */
#include<stdio.h>
#include<conio.h>
#define NULL -1
#define temp 0
#define perm 1
void dijkstra(int, int);
int n;
int weight[20][20];
void dijkstra (int s, int d)
{
int i, k, step, dist[50], pred[20], status[20], current, dc, min, newdist;
for (i = 0; i <= n-1; i++)
{
dist[i] = 9999;
pred[i] = NULL;
status[i] = temp;
}
status[s] = perm;
dist[s] = 0;
current = s;
step=1;
while(current != d)
{
dc = dist[current];
for(i=0;i<=n-1;i++)
{
if(weight[current][i] != 0 && status[i] == temp)
{
newdist = dc + weight[current][i];
if(newdist < dist[i])
{
dist[i] = newdist;
pred[i] = current;
}
}
}
min = 32766;
for(i=0;i<=n-1;i++)
{
if(status[i]== temp && min > dist[i])
{
min = dist[i];
k = i;
}
}
current = k;
status[k] = perm;

// SHOW THE INTERMEDIATE STEPS


printf("Step%d",step);
printf("\nNode\tDist\tPred\tStatus\n");
for(i=0;i<=n-1;i++)
{
printf("%d\t",i);
dist[i] >= 9999?printf("%c\t",224) : printf("%d\t", dist[i]);
pred[i] == -1? printf("NULL\t") : printf("%d\t", pred[i]);
status[i]==1?printf("PERM\n"): printf("TEMP\n");
}
step++;
getch();
}
printf("\n The cost is %d",dist[d]);
printf("\n And the path is\t");
i = d;
while(i != s)
{
printf("%d<-",i);
i = pred[i];
}
printf("%d",s);
}
void main()
{
int i, j, s, d;
clrscr();
printf("\nEnter the number of nodes in the graph");
scanf("%d",&n);
printf("\nEnter the weighted matrix");
for(i=0;i<=n-1;i++)
for(j=0; j<=n-1; j++)
scanf("%d",&weight[i][j]);
printf("\nEnter the source and destination node");
scanf("%d%d",&s,&d);
dijkstra(s,d);
getch();
}
/*C code to implement BFS traversal*/
/* File Name: BFS.c */
#include<stdio.h>
#include<conio.h>
#define MAX 49
#define WHITE 1
#define BLACK 2

void fnDfs(int);
void cqinsert(int);
int cqdelete();
int cqueueempty();
int n;
int adjmat[20][20];
int front = 0, rear = 0, a[50];

void fnDfs(int s)
// Purpose : This function finds the BFS traversal of a given graph.
// Input : s is the starting node from which we have to start the traversal.
// Output : None.
{
int i, n1, Color[20];
for(i = 0; i <= n-1; i++)
Color[i] = WHITE;
cqinsert(s);
Color[s] = BLACK;
while ( !cqueueempty() )
{
n1 = cqdelete();
printf("\t %d", n1);
for(i = 0; i<= n -1; i++)
{
if(adjmat[n1][i] != 0 && Color[i] == WHITE)
{
cqinsert(i) ;
Color[i] = BLACK;
}
}
}
}//End of function
void cqinsert(int item)
{
if(front == (rear + 1) % MAX)
{
printf("\n Queue is full");
return;
}
else
{
rear = (rear + 1) % MAX;
a[rear] = item;
}
}
int cqdelete()
{
int item;
if(front == rear)
{
printf("\n Queue is empty");
return;
}
else
{
front = (front + 1) % MAX;
item = a[front];
}
return item;
}
int cqueueempty()
{
int i;
i = (front == rear)? 1 : 0;
return i;
}
void main()
{
int i, j, s;
clrscr();
printf("\nEnter the number of nodes in the graph");
scanf("%d",&n);
printf("\nEnter the adjacency matrix");
for(i=0;i<=n-1;i++)
for(j=0; j<=n-1; j++)
scanf("%d",&adjmat[i][j]);
printf("\nEnter the starting node");
scanf("%d",&s);
fnDfs(s);
getch();
}
/*C code to implement DFS traversal*/
/* File Name: DFS.c*/
#include<stdio.h>
#include<conio.h>
#define MAX 49
#define WHITE 1
#define BLACK 2

void fnDfs(int);
void push(int);
int pop();
int stackempty();
int n;
int adjmat[20][20];
int top = -1, a[50];
void fnDfs(int s)
// Purpose : This function finds the DFS traversal of a given graph.
// Input: S is the starting node from which we have to start the traversal.
// Output : None.
{
int i, n1, Color[20];
for(i = 0; i <= n-1; i++)
Color[i] = 1;
push(s);
Color[s] = 2;
while ( !stackempty() )
{
n1 = pop();
printf("\t %d", n1);
Color[n1] = 3;
for(i = 0; i<= n -1; i++)
{
if(adjmat[n1][i] != 0 && Color[i] == 1)
{
push(i) ;
Color[i] = 2;
}
}
}
}//End of Function
void push(int item)
{
if(top == MAX - 1)
{
printf("\n Stack is full");
return;
}
else
{
top++;
a[top] = item;
}
}
int pop()
{
int item;
if(top == -1)
{
printf("\n Stack is empty");
return;
}
else
{
item = a[top];
top--;
}
return item;
}
int stackempty()
{
int i;
i = (top == -1)? 1 : 0;
return i;
}
void main()
{
int i, j, s;
clrscr();
printf("\nEnter the number of nodes in the graph");
scanf("%d",&n);
printf("\nEnter the adjacency matrix");
for(i=0;i<=n-1;i++)
for(j=0; j<=n-1; j++)
scanf("%d",&adjmat[i][j]);
printf("\nEnter the starting node");
scanf("%d",&s);
fnDfs(s);
getch();
}

/* C code to implement Warshall’s path matrix and shortest path function */


/* File Name : warshall.c*/
#include<stdio.h>
#include<conio.h>

void fnWarshallPM(int[20][20],int[20][20], int);


void fnWarshallSPM(int[20][20],int[20][20], int);

void fnWarshallPM(int A[20][20],int P[20][20],int n)


// Purpose : This function finds the path matrix for a directed graph G.
// Input: n is the total number of vertices in G.
// Output : None.
{
int i,j,k;
//First copy the contents of adjacency matrix A to path matrix P.
for(i = 1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
P[i][j] = A[i][j];
printf("%d\t",A[i][j]);
}
printf("\n");
}
//Compute different iterations of P.
for(k=1;k<=n;k++) //k is the iteration number.
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
P[i][j] = P[i][j] || (P[i][k] && P[k][j]);
}// End of function

void fnWarshallSPM(int A[20][20],int S[20][20],int n)


// Purpose : This function finds the path matrix for a directed graph G.
// Input : n is the total number of vertices in G.
// Output : None.
{
int i,j,k;
//First copy the contents of adjacency matrix A to path matrix P.
for(i = 1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
S[i][j] = A[i][j];
printf("%d\t",A[i][j]);
}
printf("\n");
}
//Compute different iterations of P.
for(k=1;k<=n;k++) //k is the iteration number.
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
S[i][j] = S[i][j]<(S[i][k] + S[k][j])?S[i][j]:S[i][k] + S[k][j];
}// End of function
void main(void)
{
int i,j,n,iChoice;
int P[20][20],A[20][20],S[20][20];
clrscr();
do
{
printf("1. Show Warshall's path matrix\n");
printf("2. Find shortst path matrix using Warshall's function\n");
printf("3. Exit\n");
printf("Enter your choice\n");
scanf("%d",&iChoice);
switch(iChoice)
{
case 1:
printf("Enter number of nodes in the graph\n");
scanf("%d",&n);
printf("Enter the adjacency matrix\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&A[i][j]);
fnWarshallPM(A,P,n);
printf("The path matrix is\n");
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
printf("%d\t",P[i][j]);
printf("\n");
}
break;
case 2:
printf("Enter number of nodes in the graph\n");
scanf("%d",&n);
printf("Enter the adjacency matrix\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&A[i][j]);
fnWarshallSPM(A,S,n);
printf("The path matrix is\n");
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
printf("%d\t",S[i][j]);
printf("\n");
}
break;
case 3:
exit(1);
default:
printf("Wrong Choice\n");
}
}while(1);
}

11.8 MCQ Chapter 11

1. Adjacency matrix of a diagraph is:


(a) Identity matrix (b) Symmetric Matrix
(c) Asymmetric Matrix (d) None of these

2. What is not true for adjacency matrix of a graph


(a) It is a symmetric matrix (b) It is a bit matrix
(c) Diagonal has all zeroes (d) All are true statements.

3. Backtracking is another name for this method of traversal:


(a) Depth-first (b) Breadth first
(c) D-search (d) none of these.

4. A suitable structure for breadth-first and depth-first traversal of graphs :


(a) Adjacency matrix (b) Edge listing
(c) Adjacency list (d) None of these.

S. Which method of traversal uses queue to hold nodes that are waiting to be processed :
(a) Breadth-first (b) Depth-first
(c) D-search (d) None of these.

6. Which method of traversal does not use stack to hold nodes that are waiting to be processed:
(a) Breadth-first (b) Depth-first
(c) D-search (d) None of these.

7. In which case adjacency list representation of graph is not useful


(a) When number of edges is small
(b) When number of vertices are changing due to insertion and deletion
(c) In breadth-first traversal
(d) It is useful in all above cases.

8. Breadth first search :


(a) scans all incident edges before moving to other vertex
(b) scans adjacent unvisited vertex as soon as possible
(c) is same as backtracking
(d) None of these.

9. If x is the adjacency matrix of a graph then (i,j)th entry of x represents :


(a) No. of edge sequence of length k between i th and j th vertex
(b) No. of path of length k between i th and j th vertex
(c) No. of walks of length k betwen i th and j th vertex
(d) None of these.
10. In an adjacency matrix parallel edges are given by :
(a) Similar columns (b) Similar rows
(c) Not representable (d) None of these.

11. A vertex with degree one in. a graph is called :


(a) Leaf (b) Pendant vertex
(c) End vertex (d) None of these.

12. A digraph in which ,outdegree is same as indegree is called :


(a) balanced (b) symmetric
(c) regular (d) None of these.

13. Graph structure is available in


(a) C (b) C++
(c) Pascal (d) None of these

14. Depth first Traversal of a graph produces:


(a) a spanning forest of the graph (b) a spanning tree of the graph
(c) a minimal spanning tree' (d) None of the above.

15. Suppose that an undirected graph G is represented by an adjacency matrix A. Let B denote the matrix A x A
x A x A x A. Which of the following is true if an element B (i,j) is non. zero?
(a) Vertex j can be reached from vertex i in exactly 5 steps.
(b) Vertex i can be reached from vertex j in exactly 5 steps.
(c) Vertex j cannot be reached from vertex i.
(d) None of the above.

16. The time complexity of Depth First Search algorithm to traverse a graph of n vertices and e edges is :
(a) O(n) if the graph is represented by adjacency matrix
(b) O (e) if the graph is represented by adjacency list
(c) O (e) if the graph is represented by adjacency matrix
(d) None of the above.

17. A graph is planar if and only if, (GATE-1990)


(a) it does not contain subgraphs homomorphic to Ks and K3.3.
(b) it does not contain subgraphs isomorphic to Ks or K3,3'
(c) it does not contain subgraphs isomorphic to Ks and K3,3'
(d) it does not contain subgraph

18. Kruskal's algorithm for finding a minimum spanning tree of a weighted graph G with n vertices
and m edges has the time complexity of : (GATE-1991)
(a) O(n2) (b) O(m n)
(c) O(m + n) (d) O(m log n)
2
(e) O(m ).
19. Consider a simple connected graph G with n vertices and n edges(n > 2). Then, which of the following
statements are true? (More than I choice may be correct) (GATE-1993)
(a) G has no cycles
(b) The graph obtained by removing any edge from G is not connected
(c) G has at least one cycle
(d) The graph obtained by removing any two edges from G is not connected.
(e) None of the above.

20. Which of the following statements is false? (GATE-1994)


(a) Optimal binary search tree construction can be performed efficiently using dynamic programming.
(b) Breadth first search can not be used to find component of a graph.
(c) Give the prefix and postfix walks over 3 binary tree, the binary tree can not be uniquely constructed.
(d) Depth first search can be used to find connected components of a graph.

21.The number of distinct simple graphs with up to 3 nodes is : (GATE-1994)


(a) 15 (b) 10
(c) 7 (d) 9.

22. The minimum number of edges in a connected cyclic graph on n vertices is :


(a) n - 1 (b) n
(c) n + I (d) None of the above.

23. Let G be a graph with 100 vertices numbered 1 to 100. Two vertices i and j are adjacent iff |i – j| = 8 or
|i – j| = 12. The number of connected components in G is : (GATE-1997)
(a) 8 (b) 4 (c) 12 (d) 25

24. The number of articulation points of the following graph is :


(a) 0 (b) 1
(c) 2 (d) 3.

25. Let G be an undirected graph. Consider a depth-first traversal of G, and let T be the resulting depth-first
search tree. Let u be a vertex in G and let v be the first new (unvisited vertex) visited after visiting u in the
traversal. Which of the following statements is always true?
(GATE-2000)
(a) {u, v} must be an edge in G. and u is a descendant of v in T
(b) {u, v} must be an edge in G, and v is a descendant of u in T
(c) If {u, v} is not an edge in G then It is a leaf in T
(d) If {u, v}is not an edge in G then u and v must have the same parent in T.

26. The most appropriate matching for the following pairs :


X: depth-first search 1: heap
Y: breadth-first search 2: queue
Z: sorting 3: stack
(a) X-I, Y - 2, Z - 3 (b) X - 3, Y - 1, Z - 2
(c) X - 3, y,- 2, Z - 1 (d) X - 2, Y - 3, Z - 1.
27. Let G be an undirected connected graph with distinct edge weights. Let emax be the edge with maximum
weight and emin the edge with minimum weight. Which of the following statements
is false? (GATE-2000)
(a) Every minimum spanning tree, of G must contain emin
(b) If emax is in a minimum spanning tree, then its removal must disconnect G
(c) No minimum spanning tree contains emax
(d) G has a unique minimum spanning tree.

28. Consider an undirected un-weighted graph G. Let a breadth first traversal be done starting from a node r. Let
d (r, u) and d (r, v) be the lengths of shortest paths from r to u and v respectively in G. If u is visited before
v during the breadth first traversal, which of the following statement
is correct? (GATE-2001)
(a) d (r, u) < d (r, v) (b) d (r, u) > d (r, v)
(c) d (r, u) ≤ d (r, v) (d) None of these.

29. Consider the graph below. The third row in the transitive closure of the above graph is

1 2 3

(a) 1,1,1 (b) 1,1,0


(c) 1,0,0 (d) 0,1,1

30. The eccentricity of node labeled 5 in the graph below

3 4

5
(a) 6 (b) 7
(c) 8 (d) 5

31. The center of the graph in previous question is the node labeled
(a) 1 (b) 2
(c) 3 (d) 4
32. Consider the graph below. What should be the labels of nodes marked 1 and 2 if the breadth first traversal
yields the list A B C D E?

A B
1

2 C

(a) D and E (b) E and D


(c) unpredictable (d) none of the above

33. Consider the graph below. Which of the following is a valid strong component?

A B

D C

(a) a, c, d (b) a, b, d
(c) b, c, d (d) a, b, c

34. Consider the undirected weighted graph below. The minimum cost spanning tree for this graph has the cost

(a) 18 (b) 20 (c) 24 (d) 22

35. The minimum number of edges in a connected cyclic graph on n vertices is


(a) n-l (b) n (c) n+l (d) none of the above

36. The minimum number of colors needed to color a graph having n (>3) vertices and 2 edges is
(a) 4 (b) 3 (c) 2 (d) 1

37. The maximum degree of any vertex in a simple graph with n vertices is
(a) n (b) n-l (c) n+l (d) 2n-l

38. The number of edges in a regular graph of degree d and n vertices is .


(a) maximum of n, d (b) n+d (c) nd (d) nd/2

Solutions:
1. d 2. d 3. a 4. c 5. a 6. a 7. d 8. a 9. c 10. c 11. b 12. d 13. d 14. b 15. b
16. d 17. b 18. c 19. c 20. b 21. a 22. b 23. a 24. d 25. b 26. c 27. c 28. c 29. d 30. b
31. d 32. a 33. d 34. b 35. b 36. c 37. b 38. d

11. Exercise Chapter 11

1. What is a graph? Differentiate between (i) Undirected and directed graph (ii) cycle and Hamiltonian cycle

2. How can you represent a graph. Discuss the two different ways of representation.

3. Write algorithms for (i) searching for a particular node in a graph

(ii) searching for a particular edge in a graph

(iii) insert a new node in a graph

(iv) insert a new edge in a graph

(v) delete a node from a graph

(vi) delete a edge from a graph

4. Compare BFS and DFS. Write algorithms and compute time complexity for both BFS and DFS to search a
graph for a node

5. How can you find shortest path between two nodes in a graph by Dijkstra’s algorithm, explain by suitable
diagrams and algorithm.

6. Describe Warshall’s algorithm to find shortest path between two nodes in a graph and explain it by suitable
diagram and algorithm.
Chapter 12
SORTING
12.1 Definition
Sorting can be defined as arranging an unordered set of comparable data items in order. We will concentrate on
sorting data that is stored in an array. Let P be an array of n elements P1 P2 P3…..Pn in memory. Sorting P
means arranging the contents of P in either increasing or decreasing order i.e.
P1 ≤ P2 ≤ P3 ≤ P4 ≤ P5 ≤ …….. ≤Pn increasing order
P1 ≥ P2 ≥ P3 ≥ P4 ≥ P5 ≥ …….. ≥Pn decreasing order
In this chapter we will discuss about different sorting techniques to sort a set of data in increasing order.

12.2 The family of sorting methods


There are two main sorting themes: address-based sorting and comparison-based sorting.

Sorting

Comparison-based Address-based
Sorting Sorting

Proxmap Radix
Sort Sort

Bubble Insertion Selection Merge Quick Heap Sort Shell Sort


Sort Sort Sort Sort Sort

12.3 Bubble Sort


Bubble sort can be defined as a comparison based sort by checking each adjacent pair of items in a list in turn,
swapping the items if necessary, and repeating the pass through the list until no swaps are done. Suppose the list
of numbers P[1], P[2], ………, P[n] is in memory. The bubble sort algorithm works as follows:
Iteration 1: Compare P[1] with P[2] and arrange them in ascending order such that P[1] < P[2]. Then compare
P[2] with P[3] and arrange such that P[2] < P[3] and so on. Finally we arrange P[n-1] and P[n].
This step involves n-1 comparisons. During which the largest element bubbles up to its final position.
That is the reason it is called as bubble sort.
Iteration 2: Repeat step 1 with one less comparison. In this case the second largest element will occupy it’s
proper position i.e. we stop after we compare and finally rearrange P[n-2] and P[n-1].
Iteration n-1: Finally we compare P[1] with P[2] and arrange them in such a way that P[1] < P[2].
Example:
P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7]
33 86 57 92 37 48 25 12
During the first pass, the following comparisons are made:

P[0] with P[1] 33 with 86 33 < 86 hence no interchange


P[1] with P[2] 86 with 57 86 > 57 hence interchange
P[2] with P[3] 86 with 92 86 < 92 hence no interchange
P[3] with P[4] 92 with 37 92 > 37 hence interchange
P[4] with P[5] 92 with 48 92 > 48 hence interchange
P[5] with P[6] 92 with 25 92 > 25 hence interchange
P[6] with P[7] 92 with 12 92 > 12 hence interchange

Thus at the end of first pass array will look like:


33 57 86 37 48 25 12 92
The complete set of iteration is depicted below:

Iteration Array elements


0 1 2 3 4 5 6 7
original 33 86 57 92 37 48 25 12
Iteration=1 33 57 86 37 48 25 12 92
Iteration=2 33 57 37 48 25 12 86 92
Iteration=3 33 37 48 25 12 57 86 92
Iteration=4 33 37 25 12 48 57 86 92
Iteration=5 33 25 12 37 48 57 86 92
Iteration=6 25 12 33 37 48 57 86 92
Iteration=7 12 25 33 37 48 57 86 92

For the implementation of algorithm, we have taken a Boolean variable swap to indicate whether the array is
sorted or not and this variable improves the best case time complexity of algorithm from O(n 2) to O(n). The
initial value of swap is TRUE which means initially the array is not sorted. Before starting of each iteration, set
the value of swap to FALSE. During execution of iteration, if at least one swap operation is performed between
adjacent pair then set swap to true to indicate the array is not sorted till now and go for next iteration.

Algorithm 12.1
1. Algorithm fnBubbleSort(P[],n)
2. //Purpose : This algorithm sorts a set of elements using the concept of bubble sort in ascending order.
3. //Input : P[0….n-1] is an array of n elements.
4. //Output : None.
5. // Comments: Here swap is a Boolean variable. After each iteration, TRUE value of swap indicates at least
one interchange has taken place and FALSE value of swap signifies the array is now sorted.
6. {
7. swap = TRUE;
8. for(Iteration = 1; Iteration < n && swap == TRUE; Iteration++)
9. {
10. swap = FALSE; //Set swap to FALSE before starting of each iteration.
11. for(j = 0; j < n- Iteration; j++)
12. {
13. if(P[j] > P[j+1])
14. {
15. swap = TRUE;
16. temp = P[j];
17. P[j] = P[j+1];
18. P[j+1] = temp;
19. }
20. }
21. }
22. } // End of algorithm

Time complexity:
1. Best case: The array is in sorted order. Hence swap will remain false after first iteration and so the process
ends. Therefore the total number of comparisons made will be decided by the inner loop for the first
iteration which is n-1.
Tb(n) = O(n)
2. Average case: On the average the comparison made in the inner loop is (k-1)/2.
f(n) = (n-1)/2 + (n-2)/2 + ……….. + 1/2
= n(n-1)/4
Therefore, Ta(n) = O(n2)
3. Worst case: The array P[] is in reverse order. So for the first iteration n-1 comparisons are needed, for the
second iteration n-2 comparisons are needed and so on. Hence in the worst case -
f(n) = (n-1) + (n-2) + …………… + 1
= n(n-1)/2
Therefore, Tw(n) = O(n2)
Space Complexity:
The space-complexity is O(1), just a few scalar variables. Here we do not count the size of the array being
sorted because that is given, not created specifically for this algorithm.

12.4 Insertion Sort


Suppose an array P[] with n elements P[1], P[2], ……, P[n] is in memory. The insertion sort algorithm scans P
from P[1] to P[n], inserting each element P[k] into it’s proper position in the previously sorted sub-array P[1],
P[2], …….., P[k-1].
Iteration 1:P[1] by itself is trivially sorted.
Iteration 2:P[2] is inserted either before or after P[1] so that P[1] and P[2] is sorted.
…………………………………………………………………………………………………………….
Iteration n-1: P[n] is inserted into it’s proper place in P[1], P[2], ….., P[n-1] so that
P[1], P[2], ……., P[n] is sorted.

Iteration Comments Array elements


0 1 2 3 4 5 6 7
original 33 86 57 92 37 48 25 12
Iteration=1 temp=86. temp > P[0], No interchange. 33 86 57 92 37 48 25 12
Iteration=2 temp=57. temp<P[1]. So insert temp at P[1]. 33 57 86 92 37 48 25 12
Iteration=3 temp=92. temp>P[2], No interchange 33 57 86 92 37 48 25 12
Iteration=4 temp=37. temp<P[3]<P[2]<P[1]. So insert temp at P[1] 33 37 57 86 92 48 25 12
Iteration=5 temp=48. temp<P[4]<P[3]<P[2]. So insert temp at P[2] 33 37 48 57 86 92 25 12
Iteration=6 temp=25. temp<P[5]<P[4]<…<P[0]. So insert temp at P[0]. 25 33 37 48 57 86 92 12
Iteration=7 temp=12. temp<P[6]<P[5]<…<P[0]. So insert temp at P[0]. 12 25 33 37 48 57 86 92

Algorithm 12.2
1. Algorithm fnInsertionSort(P[], n)
2. //Purpose : This algorithm sorts a set of elements using the concept of insertion sort.
3. //Input : P[0….n-1] is an array of n elements which is to be sorted in ascending order.
4. //Output : None.
5. {
6. for(Iteration = 1; Iteration < n; Iteration++)
7. {
8. temp = P[Iteration];
9. for(j = Iteration-1; j >= 0 && temp < P[j]; j--)
10. P[j+1] = P[j];
11. P[j+1] = temp;
12. }
13. }//End of algorithm

Time complexity:
1. Best case: The array is in sorted order then only one comparison will be made in the inner loop. Therefore
the total number of comparisons made will be decided by the outer loop which is n-1.
f(n) = (n-1) X 1
Tb(n) = O(n)

2. Average case: If we were given a random permutation, the chances of the kth insertion requiring 0, 1,
2….k-1 comparisons are equal, and hence 1/k. The expected number of comparisons is for the kth insertion
is:
k
∑ (i-1)/k = k(k-1)/2k = (k-1)/2
i=1
Therefore on the average, number of comparisons in the inner loop is (k-1)/2. Hence,
f(n) = 1/2 + 2/2 + ……..…………+ (n-1)/2
= n(n-1)/4
Therefore, Ta(n) = O(n2)

3. Worst case: The array is in reverse order. So for the first iteration 1 comparison is needed in the inner loop,
for the second iteration 2 comparisons are needed and so on. Hence in the worst case -
f(n) = 1 + 2 +…………………….. + (n-1)
= n(n-1)/2
Therefore, Tw(n) = O(n2)
Space Complexity:
The space-complexity is O(1). Here also we do not count the size of the array being sorted because that is given,
not created specifically for this algorithm.

12.5 Selection Sort


Find the least value in the array, swap it into the leftmost component (where it belongs), and then forget the
leftmost component. Do this repeatedly.
Suppose an array P[] with n elements P[0], P[1], ……, P[n-1] is in memory.

Iteration 0: Find the location loc of the smallest element in the list and then interchange P[loc] and P[0] if loc ≠
0.
Iteration 1: Find the location loc of the smallest element in the sub list of n-1 elements (P[1], P[2],….., P[n-1])
and then interchange P[loc] and P[1] if loc ≠ 1 . Then P[0] and P[1] is sorted.
Iteration 2: Find the location loc of the smallest element in the sub list of n-2 elements (P[2], P[3],….., P[n-1])
and then interchange P[loc] and P[2] if loc ≠ 2. Then P[0], P[1] and P[2] is sorted.
………………………………………………………………………………………………………………………
…...
Iteration n-2: Find the location loc of the smaller element in the sub list of 2 elements (P[n-2] and P[n-1]) and
then interchange P[loc] and P[n-2] if loc ≠ n-2 . Then P[0], P[1],….. P[n-1] is sorted.

Iteration Comments Array elements


(k) 0 1 2 3 4 5 6 7
original 33 86 57 92 37 48 25 12
k=0 Here loc = 7. loc ≠ k Interchange P[loc] and P[k]. 12 86 57 92 37 48 25 33
k=1 Here loc = 6. loc ≠ k Interchange P[loc] and P[k]. 12 25 57 92 37 48 86 33
k=2 Here loc = 7. loc ≠ k Interchange P[loc] and P[k]. 12 25 33 92 37 48 86 57
k=3 Here loc = 4. loc ≠ k Interchange P[loc] and P[k]. 12 25 33 37 92 48 86 57
k=4 Here loc = 5. loc ≠ k Interchange P[loc] and P[k]. 12 25 33 37 48 92 86 57
k=5 Here loc = 7. loc ≠ k Interchange P[loc] and P[k]. 12 25 33 37 48 57 86 92
k=6 Here loc = 6. As loc = k, no interchange. 12 25 33 37 48 57 86 92

Algorithm 12.3
1. Algorithm fnSelectionSort(P[], n)
2. //Purpose : This algorithm sorts a set of elements using the concept of selection sort.
3. //Input : P[0….n-1] is an array of n elements which is to be sorted in ascending order.
4. //Output : None.
5. {
6. for(k = 0; k < n-1; k++)
7. {
8. loc= k; //loc keeps the track of index of the minimum element in the list.
9. for(j = k+1; j < n; j++)
10. if(P[j] < P[loc])
11. loc = j;
12. if(loc != k)
13. {
14. temp = P[k];
15. P[k] = P[loc];
16. P[loc] = temp;
17. }
18. }
19. }//End of algorithm

Time complexity:
1. Best case: The best occurs when the list is in desired order. But unfortunately to get the minimum element
from a set of n-k elements n-k comparisons are required. So for the first iteration we have to go through n-1
elements and hence n-1 comparisons. Similarly for the second iteration n-2 comparisons are needed and so
on. Thus,
f(n) = (n-1) + (n-2) + …………… + 1
= n(n-1)/2
Therefore, T(n) = O(n2)
2. Average case: Here also in first iteration n-1 comparisons are required, in second iteration n-2 comparisons
and so on.
f(n) = (n-1) + (n-2) + …………… + 1
= n(n-1)/2
Therefore, T(n) = O(n2)
3. Worst case: Like the best case and average case here also,

f(n) = (n-1) + (n-2) + …………… + 1


= n(n-1)/2
Therefore, T(n) = O(n2)

Space Complexity:
The space-complexity is O(1). Here also we do not count the size of the array being sorted because that is given,
not created specifically for this algorithm.

12.6 Merge Sort


Divide and Conquer Sorting:
In terms or algorithms, this method has three distinct steps:
Divide: If the input size is too large to deal with in a straightforward manner, divide the data into two or more
disjoint subsets.
Recur: Use divide and conquer to solve the sub problems associated with the data subsets.
Conquer: Take the solutions to the sub problems and “merge” these solutions into a solution for the original
problem.
Algorithm 12.4
1. Algorithm fnSort(list)
2. {
3. if(the list has length > 1)
4. {
5. Partition the list into two lists First half and Second half
6. fnSort(First half)
7. fnSort(Second half)
8. fnCombine(First half and Second half)
9. }
10. }//End of algorithm

Divide: If S has at leas two elements (nothing needs to be done if S has zero or one elements), remove all the
elements from S and put them into two sequences, S1 and S2, each containing about half of the elements of S.
(i.e. S1 contains the first én/2ù elements and S2 contains the remaining ën/2û elements.
Recur: Recursive sort sequences S1 and S2.
Conquer: Put back the elements into S by merging the sorted sequences S1 and S2 into a unique sorted
sequence.

Algorithm 12.5
1. Algorithm fnMsort(low, high)
2. //Purpose : This algorithm sorts a set of elements using the concept of merge sort.
3. //Input : P[low….high] is an array of (high-low+1) elements which is to be sorted in ascending order.
low is the lower index and high is the upper index of the array.
4. //Output : None.
5. {
6. if(low < high)
7. {
8. mid = └(low + high)/2┘;
9. fnMsort(low, mid);
10. fnMsort(mid+1, high);
11. fnMerge(low, mid, high);
12. }
13. }//End of algorithm
Algorithm 12.6
1. Algorithm fnMerge(low, mid, high)
2. //Purpose : This algorithm merges two sorted sub arrays.
3. //Input : The lower index and upper index of two sub arrays are low to mid and mid+1 to high
respectively.
4. //Output : None.
5. {
6. h = low;
7. k = low;
8. j = mid + 1;
9. while(h <= mid && j <= high)
10. {
11. if(P[h] <= P[j])
12. {
13. B[k] = P[h];
14. h = h + 1;
15. }
16. else
17. {
18. B[k] = P[j];
19. j = j+1;
20. }
21. k = k + 1;
22. }
23. if(h < mid)
24. {
25. for(i = h ; i <= mid; i++)
26. {
27. B[k] = P[i];
28. k = k + 1;
29. }
30. }
31. else
32. {
33. for(i = j; i <= high; i++)
34. {
35. B[k] = P[i];
36. k = k+1;
37. }
38. }
39. for(i = low; i <= high; i++)
40. P[i] = B[i];
41. }//End of algorithm

Consider the following array of 10 elements which is to be sorted using merge sort-
P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
33 86 57 92 37 48 25 12 50 76
The tree of calls of merge sort (0, 9) is depicted in the figure 12.1.
33

fnMsort(0,0)

fnMsort(0,1) 86

fnMsort(1,1)
fnMsort(0,2)
57
fnMsort(2,2)

fnMsort(0,4) 92

fnMsort(3,3)

fnMsort(3,4)
37
fnMsort(4,4)
48
fnMsort(0,9) fnMsort(5,5)

fnMsort(5,6) 25
fnMsort(5,7) fnMsort(6,6)
12

fnMsort(7,7)

fnMsort(5,9)
50
fnMsort(8,8)

fnMsort(8,9) 76

fnMsort(9,9)

Figure 12.1: A tree of call of merge sort


The tree of calls of merging is shown in figure 12.2:-

33 86 33 57 86
fnMerge(0,1,1) fnMerge(0,2,2)
33 37 57 86 92

fnMerge(0,3,4)
37 92
fnMerge(3,4,4)
12 25 33 37 48 50 57 76 86 92
25 48 12 25 48 fnMerge(0,5,9)
fnMerge(5,6,6) fnMerge(5,7,7)
12 25 48 50 76
fnMerge(5,8,9)
50 76

fnMerge(8,9,9)

Figure 12.2: A tree of call of merging

node (a, b, c) represents the merging of the arrays (a, b-1) and (b, c). e.g. (0, 5, 9) means merging of (0, 5) and
(6, 9).

Example of merging:
Merging can be performed on two sorted sub arrays.
Here low = 0 mid = 5 high = 9 and k = 0

Iteration Array elements Comments


P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Pass = 1 h=0 j=5 P[j] < P[h]. So, B[k] = P[j]
33 37 57 86 92 12 25 48 50 76 j = j + 1 = 6, k = k + 1 = 1
Pass = 2 h=0 j=6 P[j] < P[h]. So, B[k] = P[j]
33 37 57 86 92 12 25 48 50 76 j = j + 1 = 7, k = k + 1 = 2
Pass = 3 h=0 j=7 P[h] < P[j]. So, B[k] = P[h]
33 37 57 86 92 12 25 48 50 76 h = h + 1 = 1, k = k + 1 = 3
Pass = 4 h=1 j=7 P[h] < P[j]. So, B[k] = P[h]
33 37 57 86 92 12 25 48 50 76 h = h + 1 = 2, k = k + 1 = 4
Pass = 5 h=2 j=7 P[j] < P[h]. So, B[k] = P[j]
33 37 57 86 92 12 25 48 50 76 j = j + 1 = 8, k = k + 1 = 5
Pass = 6 h=2 j=8 P[j] < P[h]. So, B[k] = P[j]
33 37 57 86 92 12 25 48 50 76 j = j + 1 = 9, k = k + 1 = 6
Pass = 7 h=2 j=9 P[h] < P[j]. So, B[k] = P[h]
33 37 57 86 92 12 25 48 50 76 h = h + 1 = 3, k = k + 1 = 7
Pass = 8 h=3 j=9 P[j] < P[h]. So, B[k] = P[j]
33 37 57 86 92 12 25 48 50 76 j = j + 1 = 10, k = k + 1 = 8
B[0] B[1] B[2] B[3] B[4] B[5] B[6] B[7] B[8] B[9]
12 25 33 37 48 50 57 76

Now j(value = 10) > high(value = 9), so program control goes outside the while loop. And as h(value
=3) < mid(value = 5), P[h,.., mid] will be copied into array B[].

B[0] B[1] B[2] B[3] B[4] B[5] B[6] B[7] B[8] B[9]
12 25 33 37 48 50 57 76 86 92

Finally the contents of array B[] will be copied into array P[].

P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
12 25 33 37 48 50 57 76 86 92

Time complexity:
Best case, Average case and Worst case: In the merge sort, the call of merge sort and merging does not
depend on the order of the elements in the array. So the best case, average case and worst case time complexity
are same in case of merge sort. Now let total T(n) time is needed to sort the array. That is the function
fnMsort(low,high) takes T(n) time where n is the total number of elements in the array. So the statement
fnMsort(low,mid) takes T(n/2) time to finish its execution as here we are dividing the array into two equal
halves each having n/2 elements. Similarly fnMsort(mid+1,high) also needs T(n/2) time. Again the statement
fnMerge(low,mid,high) for merge operation takes O(n) time. Hence the recurrence relation to compute the time
complexity for the merge sort is:
T(n) = 2T(n/2) + c*n c = some positive constant
= 2[2T(n/22) + c*n/2] + c*n
= 22T(n/22) + 2c*n
= ……………………………
= ……………………………
= 2kT(n/2k) + kcn
= nT(1) + cnlogn [ Let 2k = n. So, k = logn]
= n + cnlogn [ for n = 1, T(1) = 1]

Therefore, T(n) = O(n logn)


Space complexity:
The space complexity of the recursive algorithm for merge sort is O(n+logn), n for the extra array B[] and
log(n) for the stack space.

Characteristics:
1. It is based on divide and conquer method.
2. Algorithm’s complexity will remain O(n logn) in the best, average and worst case.
3. To execute the algorithm, extra space required is equal to the number of elements in the
array i.e. space complexity = O(n).
4. Time complexity of algorithm merge is O(n) where n = number of records being merged.

12.7 Quick Sort


It follows the Divide and Conquer method. It was devised by C.A.R. Hoare in 1962. Here the division into sub
files is made such that the sorted sub files don’t later to be merged. This is accomplished by rearranging the
elements P[0:n-1] such that P[i] ≤ P[j] for all i, 0≤ i ≤ m and all j, m + 1 ≤ j ≤ n – 1 for some m, 0 ≤ m ≤ n – 1.
Thus the elements in P[0:m-1] and P[m + 1 : n-1] may be independently sorted.
The rearrangement of the elements is done by picking some element of P[], say t = P[s], and then
reordering the other elements, so that all elements appearing before t in P[0:n-1] are less than or equal to t and
all elements appearing after t are greater than or equal to t. This rearranging is referred to as partitioning.

Example:
Given array is:-
P[0] P[1] P[2] P[3] P[4] P[5] P[6]
33 37 57 86 92 27 25

Iteration Array elements Comments


P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] V
Initial I=0 J=7 33
33 37 57 86 92 27 25 +α
Step = 1 I=1 J=6 33 I<J
33 37 57 86 92 12 25 +α interchange P[I] and P[J]
Step = 2 I=2 J=5 33 I<J
33 25 57 86 92 27 37 +α interchange P[I] and P[J]
Step = 3 J=2 I=3 33 I>J
33 25 27 86 92 57 37 +α interchange V and P[J]
Step = 4 33 Now 33 has got it’s
27 25 33 86 92 57 37 +α final position

Now the two sub lists are:


Sub list 1
P[0] P[1]
27 25

Sub list 2
P[3] P[4] P[5] P[6]
86 92 57 37
The same process will be continued for sub lists and at the end all the elements of list will be at its proper
position.

Algorithm 12.7
1. Algorithm fnQsort(p, q)
2. //Purpose : This algorithm sorts an array using quick sort.
3. //Input : The lower index and upper index of the array are p and q respectively.
4. //Output : None.
5. {
6. if(p < q)
7. {
8. j = fnPartition(p, q+1);
9. fnQsort(p, j-1);
10. fnQsort(j+1, q);
11. }
12. }//End of algorithm

Algorithm 12.8
1. Algorithm fnPartition(m, n)
2. //Purpose : This algorithm finds the proper place of the pivot element.
3. //Input : The lower index and upper index of the array are m and n respectively.
4. //Output : None.
5. {
6. P[n] = α; //Assign a large value at the end of the array.
7. v = P[m]; // v is the pivot element.
8. i = m;
9. j = n;
10. do
11. {
12. do
13. {
14. i = i+1;
15. } while(P[i] < v);
16. do
17. {
18. j = j-1;
19. }while(P[j] > v);
20. if(i < j)
21. fnInterchange (i, j);
22. }while(i < j);
23. P[m] = P[j];
24. P[j] = v;
25. return j; //j is the actual position of the pivot element in the sorted array.
26. }//End of algorithm

Algorithm 12.9
1. Algorithm fnInterchange(i, j)
2. {
3. temp = P[i];
4. P[i] = P[j];
5. P[j] = temp;
6. }//End of algorithm

Time complexity:
The time complexity of the above algorithm is given by :-
T(n) = P(n) + T(j – LB) + T(UB – j) where
P(n) = time to partition the given sub file.
T(j – LB) = time to sort left sub file.
T(UB – j) = time to sort right sub file.
1. Best case: The best case occurs when the file is always partitioned in half i.e.
j = └(LB + UB)/2┘
Then the analysis becomes -
T(n) = P(n) + 2T(n/2)
= cn + 2[2T(n/22) + c(n/2)]
= 2cn + 22T(n/22)
= ……………………………
= ……………………………
= kcn + 2kT(n/2k)
= cnlogn + nT(1) [ Let 2k = n. So, k = logn]
= cnlogn + n [for n = 1, T(1) = 1]
Therefore, T(n) = O(n logn)
2. Average case: For the average case analysis, we make the following assumptions:
 The n elements to be sorted are distinct.
 The partitioning element is chosen using a random selection process.
If RANDOM(i, j) is a function that generates a random integer in the interval [i, j], then the selection element is
chosen by replacing the statements v = P[m] and i = m by i = RANDOM(LB, UB) and v = P[i]. Since the
partitioning element v has an equal probability of being the ith smallest element 1 ≤ i ≤ (UB – LB + 1) in P[LB,
UB], hence the two sub files remaining to be sorted will be P[LB, j] and P[j + 1, UB] with probability 1/(UB –
LB + 1); LB ≤ j ≤ UB.
Form this we obtain the recurrence:-
T(n) = (n + 1) + 1/n (k = 1..n)[T(k – 1) + T(n – k)] --------------------- (1)
Where n + 1 = number of comparisons required to partition the whole list by first time.
Multiplying both sides of equation (1) by n we get
nT(n) = n(n + 1) + 2[ T(0) + T(1) + ………… + T(n – 1)] --------------------- (2)
Replacing n by n-1 in equation (2) we get
(n – 1)T(n – 1) = (n – 1)n + 2[T(0) + T(1) + ………… + T(n – 2)] ---------- (3)
Subtracting equation (3) from equation (2) we get
nT(n) – (n – 1)T(n – 1) = 2n + 2T(n – 1)
or, nT(n) = 2n + (n + 1)T(n – 1)
or, T(n)/(n + 1) = 2/(n + 1) + T(n – 1)/n -------------------------------- (4)
Equation (4) is the required recurrence relation in average case.
T(n)/(n + 1) = T(n – 2)/(n – 1) + 2/n + 2/(n + 1)
= T(n – 3)/(n – 2) + 2/(n – 1) + 2/n + 2/(n + 1)
= ………………………………………………
= ………………………………………………
= T(0)/1 + 2 ∑(k = 2.. n+1) 1/k
= 2 ∑(k = 2.. n+1) 1/k [ for n = 0, T(0) = 0]
n+1
≤ 2 ∫1 dx/x = 2[log(n + 1) – log1]
Therefore,
T(n) ≤ 2(n + 1) log(n + 1)
Hence,
T(n) = O(n logn)

3. Worst case: The worst case occurs when, at each invocation of the routine, the current file is partitioned
into two sub files with one of them being empty(j = LB or j = UB) or key element being smallest or largest
one.
Then the analysis becomes -

T(n) = P(n) + T(0) + T(n – 1)


= cn + T(n – 1) [ for n = 0, T(0) = 0]
= cn + c(n – 1) + T(n – 2)
= cn + c(n – 1) + c(n – 2) + T(n – 3)
= ……………………………
= ……………………………
= c∑(k = 1.. n) K + T(0)
= cn(n + 1)/2

Therefore, T(n) = O(n2)

Space complexity:
The space complexity of the recursive algorithm for quick sort is O(logn), log(n) for the stack space.
12.8 Heap Sort

Algorithm 12.10
1. Algorithm fnHeap_sort(length)
2. // Purpose : This algorithm sorts a set of element in ascending order using heap sort.
3. // Input : The elements are stored in the array P[1…length].
4. // Output : None.
5. {
6. heap_size = length;
7. fnBuild_max_heap(length);
8. for(i = length; i>=2; i--)
9. {
10. interchange(1, i);
11. heap_size--;
12. max_heapify(1, heap_size);
13. }
14. }//End of algorithm

Algorithm 12.11
1. Algorithm fnBuild_max_heap(length)
2. //Purpose : This algorithm builds the max heap with the given data.
3. // Input : length of the array.
4. //Output : None.
5. {
6. for(i = length/2; i >=1; i--)
7. max_heapify(i, length);
8. }//End of algorithm

Algorithm 12.12
1. Algorithm max_heapify(i, length)
2. // Purpose : This algorithm adjust the heap with size length in respect to the array position i.
3. // Input : i array position and length is the size.
4. //Output : None.
5. {
6. l = left(i); // index of the left child of i.
7. r = right(i); // index of the right child of i.
8. if(l ≤ heap_size)
9. {
10. if(l <= heap_size && P[l] >= P[i])
11. largest = l;
12. else
13. largest = i;
14. if(r <= heap_size && P[r] > P[largest])
15. largest = r;
16. if(largest != i)
17. {
18. interchange(i, largest);
19. max_heapify(largest, heap_size);
20. }
21. }
22. }//End of Algorithm

Algorithm 12.13
1. Algorithm left( i)
2. // This algorithm calculates the index of the left child of i
3. {
4. return(2*i);
5. }//End of algorithm

Algorithm 12.14
1. Algorithm right( i)
2. // This algorithm calculates the index of the right child of i.
3. {
4. return(2*i+1);
5. }//End of Algorithm

Example:
Consider the following array-

P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]


54 56 46 29 72 65 32 64 48

Steps to create the heap:


1 5
4

2 5 4 3
6 6

i=4 2 5 7 6 6 3 7
9 2 5 2

8 6 4
9
4 8

Step 1:-max_heapify(4, 9) → max_heapify(8, 9)

1 5
4

2 5 4 i=3
6 6

4 6 5 7 6 6 3 7
4 2 5 2

8 2 4
9
9 8

Step 2:-max_heapify(3, 9) → max_heapify(6, 9)


1 5
4

i=2 5 6 3
6 5

4 6 5 7 4 6 3 7
4 2 5 2

8 2 4
9
9 8

Step 3:-max_heapify(2, 9) → max_heapify(5,9)

i= 1 5
4

2 7 6 3
2 5

4 6 5 5 4 6 3 7
4 6 5 2

8 2 4
9
9 8

Step 4:-max_heapify(1, 9) → max_heapify(2,9) → max_heapify(4, 9)

1 7
2

2 6 6 3
4 5

4 5 5 5 4 6 3 7
4 6 5 2

8 2 4
9
9 8

Step 5: Max heap is created

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Original 64 56 46 29 72 65 32 54 48
After creation of heap 72 64 65 54 56 45 32 29 48

Steps to delete the root and create the sorted array in ascending order:
Step 1: Interchange P[1] and P[9]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 1 48 64 65 54 56 45 32 29 72
Now heapify.

1 4 1 6
8 5
max_heapify(1, 8)

2 6 6 3 2 6 3 4
4 5
4 8

4 5 5 5 6 4 3 7 4 5
5 5
6 4
7 3
4 6 5 2 4 6 5 2

8 2
8
2
9 9

Step 2: Interchange P[1] and P[8]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 2 29 64 48 54 56 45 32 65 72
Now heapify.

1 2 1 6
9 4
max_heapify(1, 7) → max_heapify(2, 7) →

2 6 4 3 2 5 3 4
4 8
max_heapify(5, 7) 6 8

4 5 5 5 6 4 3 7 4 5
5 2
6 4
7 3
4 6 5 2 4 9 5 2

Step 3: Interchange P[1] and P[7]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 3 32 56 48 54 29 45 64 65 72
Now heapify.

1 3 1 5
2 6
max_heapify(1, 6) → max_heapify(2, 6) →

2 5 4 3 2 5 3 4
6 8
max_heapify(4, 6) 4 8

4 5 5 2 6 4 4 3
5 2
6 4
4 9 5 2 9 5
Step 4: Interchange P[1] and P[6]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 4 45 54 48 32 29 56 64 65 72
Now heapify.
1 4 1 5
5 4
max_heapify(1, 5) → max_heapify(2, 5)

2 5 4 3 2 4 3 4
4 8
5 8

4 3 5 2 4 3
5 2
2 9 2 9

Step 5: Interchange P[1] and P[5]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 5 29 45 48 32 54 56 64 65 72
Now heapify.
1 2 1 4
9 8
max_heapify(1, 4) → max_heapify(3, 4)

2 4 4 3 2 4 3 2
5 8
5 9

4 3 4 3
2 2

Step 6: Interchange P[1] and P[4]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 6 32 45 29 48 54 56 64 65 72
Now heapify.
1 3 1 4
2 5
max_heapify(1, 3) → max_heapify(3, 3)

2 4 2 3 2 3 3 2
5 9
2 9

Step 7: Interchange P[1] and P[3]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 7 29 32 45 48 54 56 64 65 72
Now heapify.
1 2 1 3
9 2
max_heapify(1, 2) → max_heapify(2, 2)

2 3 2 2
2
9

Step 8: Interchange P[1] and P[2]

Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Step 8 29 32 45 48 54 56 64 65 72
Now heapify.
1 max_heapify(1, 1) 1
2 2
9 9

Now, the sorted array is-


Array elements
P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9]
Finally 29 32 45 48 54 56 64 65 72

12.10 Radix Sort


Bucket sort or radix sort can be used to sort a list of elements. In case of decimal numbers, the base or radix is
10.
First of all the list of elements is sorted according to the first digit thus the elements are arranged in 10
buckets.
In second pass, elements are arranged according to the second digit and so on. This process depends on
the number of digits of the largest number. E.g. if the largest number is 1234 then 4 passes are needed.

Example:
Given array is:-
P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9] P[10]
348 143 361 423 538 128 600 858 12 274 999
Pass 1:

Elements Buckets
0 1 2 3 4 5 6 7 8 9
348 348
143 143
361 361
423 423
538 538
128 128
600 600
855 855
12 12
274 274
999 999
Pass 2:

Elements Buckets
0 1 2 3 4 5 6 7 8 9
600 600
361 361
12 12
143 143
423 423
274 274
855 855
348 348
538 538
128 128
999 999
Pass 3:

Elements Buckets
0 1 2 3 4 5 6 7 8 9
600 600
12 12
128 128
423 423
538 538
143 143
348 348
855 855
361 361
274 274
999 999
Therefore the sorted array is:-

P[0] P[1] P[2] P[3] P[4] P[5] P[6] P[7] P[8] P[9] P[10]
12 128 143 274 348 361 423 538 600 855 999

Algorithm 12.15
1. Algorithm radix(P, n)
2. // Purpose : This algorithm sorts a set of elements in ascending order using radix sort.
3. // Input : P[0….n-1] is an array of n decimal numbers which is to be sorted in ascending order.
4. // Output : None.
5. //Comment: The one dimensional array buck[j] keeps the track of the index of the elements in jth bucket[j][].
6. {
7. div = 1;
8. num = number of digits in the largest number of the array P[].
9. for( pass = 1; pass ≤ num; pass++)
10. {
11. for(k = 0; k < 10; k++)
12. buck[k] = 0;
13. for(i = 0; i < n; i++)
14. {
15. j = (P[i]/div)%10;
16. bucket[j][buck[j]++] = P[i];
17. }
18. i=0;
19. for(k = 0; k < 10; k++)
20. {
21. for(j = 0; j < buck[k]; j++)
22. P[i++] = bucket[k][j];
23. }
24. div*=10;
25. }
26. }// End of Algorithm

Time complexity:
Radix sort is dependent on three things:
 R = Radix (10 for decimal, 26 for alphabets, and 2 for binary).
 D = Number of digits in the largest element.
 N = Size of array of elements.
Therefore, the maximum number of comparisons will be
F(N) = R * D * N

1. Best case: In best case the number of digits in largest element (D) will be log R N. So the
maximum number of comparisons = R * logR N * N
Which is of O(N log N).

2. Worst case: Suppose the number of digits in the largest element (D) is equal to number
of elements in array(N).
Which is of O(N2).

12.11 Comparison study of different sorting


Sorting Technique Best Case Average Case Worst Case
Bubble O(n) O(n2) O(n2)
Insertion O(n) O(n2) O(n2)
2 2
Selection O(n ) O(n ) O(n2)
Merge O(nlogn) O(nlogn) O(nlogn)
2
Quick O(n ) O(nlogn) O(nlogn)
Radix O(n2) O(nlogn) O(nlogn)
Heap O(nlogn) O(nlogn) O(nlogn)

/* Sorting using Bubble Sort*/


/* File Name: BUBBLE.C */
#include<stdio.h>
#include<conio.h>
#define TRUE 1
#define FALSE 0
void fnBubbleSort(int P[],int n)
//Purpose : This algorithm sorts a set of elements using the concept of bubble sort in ascending order.
//Input : P[0...n-1] is an array of n elements.
//Output : None.
//Comments: Here swap is a Boolean variable. After each iteration, TRUE value of swap indicates at least one
interchange has taken place and FALSE value of swap signifies the array is now sorted.
{
int swap,Iteration,j,temp;
swap = TRUE;
for(Iteration = 1; Iteration < n && swap == TRUE; Iteration++)
{
swap = FALSE; //Set swap to FALSE before starting of each iteration.
for(j = 0; j < n- Iteration; j++)
{
if(P[j] > P[j+1])
{
swap = TRUE;
temp = P[j];
P[j] = P[j+1];
P[j+1] = temp;
}
}
}
} // End of algorithm
void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=0;i<n;i++)
scanf("%d",&P[i]);
fnBubbleSort(P,n);
printf("After sorting:\n");
for(i=0;i<n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/*Sorting using insertion Sort*/
/* File Name: INSERTION.C */
#include<stdio.h>
#include<conio.h>

void fnInsertionSort(int P[], int n)


//Purpose : This algorithm sorts a set of elements using the concept of insertion sort.
//Input : P[0….n-1] is an array of n elements which is to be sorted in ascending order.
//Output : None.
{
int Iteration,temp,j;
for(Iteration = 1; Iteration < n; Iteration++)
{
temp = P[Iteration];
for(j = Iteration-1; j >= 0 && temp < P[j]; j--)
P[j+1] = P[j];
P[j+1] = temp;
}
}//End of algorithm

void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=0;i<n;i++)
scanf("%d",&P[i]);
fnInsertionSort(P,n);
printf("After sorting:\n");
for(i=0;i<n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/* Sorting using Selection Sort*/
/* File Name: SELECTION.C */
#include<stdio.h>
#include<conio.h>
#define TRUE 1
#define FALSE 0
void fnSelectionSort(int P[], int n)
//Purpose : This algorithm sorts a set of elements using the concept of selection sort.
//Input : P[0….n-1] is an array of n elements which is to be sorted in ascending order.
//Output : None.
{
int k,loc,j,temp;
for(k = 0; k < n-1; k++)
{
loc= k; //loc keeps the track of index of the minimum element in the list.
for(j = k+1; j < n; j++)
if(P[j] < P[loc])
loc = j;
if(loc != k)
{
temp = P[k];
P[k] = P[loc];
P[loc] = temp;
}
}
}//End of algorithm

void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=0;i<n;i++)
scanf("%d",&P[i]);
fnSelectionSort(P,n);
printf("After sorting:\n");
for(i=0;i<n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/*Sorting using Merge Sort*/
/* File Name: MERGE.C */
#include<stdio.h>
#include<conio.h>

void fnMsort(int[],int,int);
void fnMerge(int[],int,int,int);

void fnMsort(int P[], int low, int high)


//Purpose : This algorithm sorts a set of elements using the concept of merge sort.
//Input : P[low….high] is an array of (high-low+1) elements which is to be sorted in ascending order.
low is the lower index and high is the upper index of the array.
//Output : None.
{
int mid;
if(low < high)
{
mid = (low + high)/2;
fnMsort(P,low, mid);
fnMsort(P,mid+1, high);
fnMerge(P,low, mid, high);
}
}//End of algoritm

void fnMerge(int P[], int low, int mid, int high)


//Purpose : This algorithm merges two sorted sub arrays.
//Input : The lower index and upper index of two sub arrays are low to mid and mid+1 to high
respectively.
//Output : None.
{
int h,k,j,i,B[50];
h = low;
k = low;
j = mid + 1;
while(h <= mid && j <= high)
{
if(P[h] <= P[j])
{
B[k] = P[h];
h = h + 1;
}
else
{
B[k] = P[j];
j = j+1;
}
k = k + 1;
}
if(h < mid)
{
for(i = h ; i <= mid; i++)
{
B[k] = P[i];
k = k + 1;
}
}
else
{
for(i = j; i <= high; i++)
{
B[k] = P[i];
k = k+1;
}
}
for(i = low; i <= high; i++)
P[i] = B[i];
}//End of algorithm

void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=0;i<n;i++)
scanf("%d",&P[i]);
fnMsort(P,0,n-1);
printf("After sorting:\n");
for(i=0;i<n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/*Sorting using Quick Sort*/
/* File Name: QUICK.C */
#include<stdio.h>
#include<conio.h>

void fnQsort(int[],int,int);
int fnPartition(int[],int,int);
void fnInterchange(int[],int,int);

void fnQsort(int P[], int p, int q)


//Purpose : This algorithm sorts an array using quick sort.
//Input : The lower index and upper index of the array are p and q respectively.
//Output : None.
{
int j;
if(p < q)
{
j = fnPartition(P,p, q+1);
fnQsort(P, p, j-1);
fnQsort(P, j+1, q);
}
}//End of algorithm

int fnPartition(int P[], int m, int n)


//Purpose : This algorithm finds the proper place of the pivot element.
//Input : The lower index and upper index of the array are m and n respectively.
//Output : None.
{
int v,i,j;
P[n] = 32767; //Assign a large value at the end of the array.
v = P[m]; // v is the pivot element.
i = m;
j = n;
do
{
do
{
i = i+1;
} while(P[i] < v);
do
{
j = j-1;
}while(P[j] > v);
if(i < j)
fnInterchange (P,i, j);
}while(i < j);
P[m] = P[j];
P[j] = v;
return j; //j is the actual position of the pivot element in the sorted array.
}//End of algorithm

void fnInterchange(int P[],int i,int j)


{
int temp;
temp = P[j];
P[j] = P[i];
P[i]=temp;
}
void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=0;i<n;i++)
scanf("%d",&P[i]);
fnQsort(P,0,n-1);
printf("After sorting:\n");
for(i=0;i<n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/*Sorting using Heap Sort*/
/* File Name: HEAP.C */
#include<stdio.h>
#include<conio.h>

void fnHeap_sort(int[],int);
void fnBuild_Max_Heap(int[],int);
void fnMax_heapify(int[],int,int);
void interchange(int[],int,int);

void fnHeap_sort(int P[], int length)


// Purpose : This algorithm sorts a set of element in ascending order using heap sort.
// Input: The elements are stored in the array P[1…length].
// Output : None.
{
int heap_size,i;
heap_size = length;
fnBuild_Max_Heap(P, length);
for(i = length; i>=2; i--)
{
interchange(P, 1, i);
heap_size--;
fnMax_heapify(P, 1, heap_size);
}
}

void fnBuild_Max_Heap(int P[], int length)


//Purpose : This algorithm builds the max heap with the given data.
//Input : length of the array.
//Output : None.
{
int i;
for(i = length/2; i >=1; i--)
fnMax_heapify(P, i, length);
}
void fnMax_heapify(int P[], int i, int heap_size)
// Purpose : This algorithm adjust the heap with size length in respect to the array position i.
// Input: i array position and length is the size.
//Output : None.
{
int l,r,largest;
l = left(i); // index of the left child of i.
r = right(i); // index of the right child of i.
if(l <= heap_size)
{
if(l <= heap_size && P[l] >= P[i])
largest = l;
else
largest = i;
if(r <= heap_size && P[r] > P[largest])
largest = r;
if(largest != i)
{
interchange(P,i, largest);
fnMax_heapify(P, largest, heap_size);
}
}
}

int left(int i)
{
return(2*i);
}

int right(int i)
{
return(2*i+1);
}

void interchange(int P[],int i,int j)


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

void main(void)
{
int n,P[50],i;
clrscr();
printf("Enter number of elements in the array\n");
scanf("%d",&n);
printf("Enter the elements now\n");
for(i=1;i<=n;i++)
scanf("%d",&P[i]);
fnHeap_sort(P,n);
printf("After sorting:\n");
for(i=1;i<=n;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}
/* C CODE TO IMPLEMENT THE RADIX SORT*/
/* File Name: RADIX.C */
#include<stdio.h>
#include<conio.h>
void radix_sort(int);
int P[100];

void radix_sort(int n)
// Purpose : This algorithm sorts a set of elements in ascending order using radix sort.
// Input: P[0….n-1] is an array of n decimal numbers which is to be sorted in ascending order.
// Output : None.
//Comment: The one dimensional array buck[j] keeps the track of the index of the elements in jth bucket[j][].
{
int bucket[10][5],buck[10],b[10];
int i, j, k, l, num, div, large, passes;
div=1;
num=0;
large=P[0];
for(i=1;i<n;i++)
{
if(P[i] > large)
large=P[i];
}
while(large > 0)
{
num++;
large=large/10;
}
for(passes=0;passes < num; passes++)
{
for(k=0;k<10;k++)
buck[k]=0;
for(i=0;i<n;i++)
{
j=(P[i]/div)%10;
bucket[j][buck[j]++]=P[i];
}
i=0;
for(k=0;k<10;k++)
{
for(j=0;j<buck[k];j++)
P[i++]=bucket[k][j];
}
div*=10;
}
}//End of Function
void main()
{
int n, i;
clrscr();
printf("Enter number of elements");
scanf("%d",&n);
printf("Enter the elements now");
for(i=0;i<=n-1;i++)
scanf("%d",&P[i]);
radix_sort(n);
printf("After sorting\n");
for(i=0;i<=n-1;i++)
printf("%d\t",P[i]);
printf("\n");
getch();
}

12.12 MCQ Chapter 12

1. Name the sort for which time is not proportional to n2:


(a) Selection sort (b) Bubble sort
(c) Quick sort (d) None of these

2. The element at the root of the heap is


(a) largest
(b) smallest
(c) depending on type of heap it may smallest or largest.
(d) None of these

3. This sort does not use divide and conquer methodology


(a) Merge sort (b) Quick sort
(c) Bubble sort (d) None of these

4. Name the sort in which array to be sorted is partitioned again & again in such a way that all elements less
than or equal to partitioning element appear before it and those which are greater appear after it
(a) Merge sort (b) Quick sort
(c) Selection sort (d) None of these.

5. This sort inserts each elements A (K) into proper position in the previously sorted sub array A (1) ...A (K-1).
(a) Insertion sort (b) Radix sort
(c) Merge sort (d) bubble sort

6. This sort finds location LOC of smallest element in A (K), ..., A (N) and then interchange A (LOC) with A
(K) for K = 1, ..., N - 1.
(a) Shuffle sort (b) Quick sort
(c) Heap sort (d) None of these

7. In this sort, file is divided into subfiles which are to be independently sorted and then merged.
(a) quick sort (b) heap sort
(c) Bubble sort (d) None of these

8. This searching method requires that all keys must reside in internal memory
(a) Binary search (b) sequential search
(c) Hashing (d) None of these

9. Average case time complexity of the quick sort algorithm is more than
(a) O(N log2 N) (b) O(N ln N)
2
(c) O(N ) (d) O(N3)

10. Worst case time complexity of the heap sort algorithm is


(a) O(N log2 N) (b) O(N ln N)
2
(c) O(N ) (d) O(N3)

11. For sorting contiguous list of records quicksort may be preferred over merge sort because:
(a) it requires less time always
(b) it does not require extra space for an auxiliary storage.
(c) it requires less programming effort
(d) some programming languages does not support recursion

12. In quicksort, a desirable choice for the partitioning element will be :


(a) first element of the list
(b) last element of the list
(c) median of the list
(d) a randomly chosen element of the list

13. Stability of a sorting algorithm is important for


(a) sorting records on the basis of multiple keys
(b) worst case performance of the sorting algorithm
(c) sorting alphanumeric keys because they are likely to be same.
(d) None of the above.

14. the heap (represented by an array) constructed from the list of numbers 30, 10, 80, 60, 15, 55, 17 is:
(a) 60, 80, 55, 30, 10, 17, 15 (b) 80, 55, 60, 15, 10, 30, 17
(c) 80, 60, 30, 17, 55, 15, 10 (d) None of the above

15. Which of the following sorting algorithms has a worst case time complexity of 0 (n2) to sort n records?
(a) Quick sort
(b) Merge sort
(c) Heap sort
(d) None of the above

16. The complexity of the comparison based sorting algorithm is: (GATE-1990)
(a) θ(n log n) (b) θ(n)
2
(c) θ(n ) (d) θ(n √ n)

17. Merge sort uses


(a) Divide and conquer strategy
(b) Backtracking approach
(c) Heuristic Search
(d) Greedy approach

18. For merging two sorted lists of sizes m and n into a sorted list of size m + n, we require
comparisons of (GATE-1995)
(a) O(m)
(b) O(n)
(c) O(m+n)
(d) O(log m + log n)
19. The minimum number of interchanges needed to convert the array (GATE-1996)
89, 19, 40, 17, 12, 10, 2, 5, 7, 11, 6, 9, 70
into a heap with the maximum element at the root is
(a) 0 (b) 1
(c) 2 (d) 3

20. A sorting technique is called stable if: (GATE-1999)


(a) It takes O(n log n) time .
(b) It maintains the relative order of occurrences of non-distinct elements
(c) It uses divide and conquer paradigm
(d) It takes O(n) space

21. Suppose we want to arrange the n numbers sorted in an array such that all negative values occur before all
positive ones. Minimum number of exchanges required in the worst case is (GATE-1999)
(a) n - 1
(b) n
(c) n + 1
(d) None of the above

22. If one uses straight two-way merge sort algorithm to sort the following elements in ascending order 20,
47, 15, 8, 4, 40, 9, 30, 12, 17, then the order of these elements after the second pass of the algorithm is:
(GATE-1999)
(a) 8, 9, 15, 20, 47, 4, 12, 17, 30, 40 (b) 8, 15, 20, 47, 4, 9, 30, 40, 12, 17
(c) 15, 20, 47, 4, 8, 9, 12, 30, 40, 17 (d) 4, 8, 9, 15, 20, 47, 12, 17, 30, 40

23. Let s be a sorted array of n integers. Let t(n) denote the time taken for the most efficient algorithm to
determine if there are two elements with sum less than 1000 in s. Which of the following statements is
true? (GATE-2000)
(a) t (n) is O(1)
(b) n ≤ t (n) ≤ n log2 n
(c) n log2 n ≤ t (n) < n C2
(d) t (n) = nC2

24. The number of swappings needed to sort the numbers 8, 22, 7, 9, 31, 19, 5, 13 in ascending order, using
bubble sort is

(a) 11 (b) 12 (c) 13 (d) 14

25. Given two sorted list of size m and n respectively. The number of comparisons needed in the worst case
by the merge sort algorithm will be
(a) m x n (b) maximum of m, n (c) minimum of m, n (d) m + n - 1

26. Sorting is not useful for


(a) report generation
(b) minimizing the storage needed
(c) responding to queries easily
(c) making searching easier and efficient

27. Choose the correct statements.


(a) Internal sorting is used if the number of items to be sorted is very large.
(b) External sorting is used if the number of items to be sorted is very large.
(c) External sorting needs auxiliary storage.
(d) Internal sorting needs auxiliary storage.

28. A sorting technique that guarantees, that records with the same primary key occurs iti the same order in
the sorted list as in the original unsorted list is said to be
(a) stable (b) consistent (c) external (d) linear

29. The way a card game player arranges his cards as he picks them up one by one, is an example of
(a) bubble sort (b) selection sort (c) insertion sort (d) merge sort

30. You want to check whether a given set of items is sorted. Which of the following sorting methods will be
the most efficient if it is already in sorted order?
(a) Bubble sort (b) Selection sort (c) Insertion sort (d) Merge sort

31. The average number of comparisons performed by the merge sort algorithm, in merging two sorted lists of
length 2 is
(a) 8/3 (b) 8/5 (c) 11/7 (d) 11/6

32. Which of the following sorting methods will be the best if number of swappings done, is the only measure
of efficiency?
(a) Bubble sort (b) Selection sort (c) Insertion sort (d) Quick sort

33. You are asked to sort 15 randomly generated numbers. You should prefer
(a) bubb1esort (b) quick sort (c) merge sort (d) heap sort

34. As part of the maintenance work, you are entrusted with the work of rearranging the library
books in a shelf in proper order, at the end of each day. The ideal choice will be
(a) bubble sort (b) insertion sort (c) selection sort (d) heap sort

35. The maximum number of comparisons needed to sort 7 items using radix sort is (assume each
item is a 4 digit decimal number)
(a) 280 (b) 40 (c) 47 (d) 38

36. Which of the following algorithms exhibits the unnatural behavior that, minimum number of
comparisons are needed if the list to be sorted is in the reverse order and maximum number
of comparisons are needed if they are already in sorted order?
(a) Heap sort (b) Radix sort
(c) Binary insertion sort (d) There can't be any such sorting method

37. Which of the following sorting algorithm has the worst time complexity of nlog (n)?
(a) Heap sort (b) Quick sort (c) Insertion sort (d) Selection sort

38. Which of the following sorting methods sorts a given set of items that is already in sorted
order or in reverse sorted order with equal speed?
(a) Heap sort (b) Quick sort (c) Insertion sort (d) Selection sort

39. Merge sort uses


(a) divide and conquer strategy
(b) backtracking approach
(c) heuristic search
(d) greedy approach

40. For merging two sorted lists of sizes m and n into a sorted list of size m+n, we require
comparisons of
(a) O(m) (b) O(n) (c) O(m+n) (d) O(log(m) + log(n))

41. A machine needs a minimum of 100 see to sort 1000 names by quick sort. The minimum time needed to
sort 100 names will be approximately
(a) 50.2 see (b) 6.7 see (c) 72.7 see (d) 11.2 see

42. A machine took 200 see to sort 200 names, using bubble sort. In 800 see, it can approximately sort .
(a) 400 names (b) 800 names (c) 750 names (d) 800 names

43. The correct order of arrangement of the names Bradman, Lamb, May, Boon, Border, Underwood and
Boycott, so that the quicksort algorithm makes the least number of comparisons is
(a) Bradman, Border, Boon, Boycott, May, Lamb, Underwood
(b) Bradman, Border, Boycott, Boon, May, Underwood, Lamb
(c) Underwood, Border, Boon Boycott, May, Lamb, Bradman
(d) Bradman, May, Lamb, Border, Boon, Boycott, Underwood

44. Which of the following is useful in implementing quick sort?


(a) Stack (b) Set (c) List (d) Queue

45. Which of the following algorithm design technique is used in the quick sort algorithm?
(a) Dynamic programming (b) Backtracking
(c) Divide and conquer (d) Greedy method

46. Assume 5 buffer pages are available to sort a file of 105 pages. The cost of sorting using m-way merge
sort is
(a) 206 (b) 618 (c) 840 (d) 926
47. A sorting procedure is called to sort a list of 100 integers that have been read from a file. If all 100 values
are zero, what would the execution requirements (in terms of Big-O) be if the sort used was MergeSort?
(a) O(N) (b) O(N2) (c) O(N log2 N) (d) None of these.

48. A list is ordered from smallest to largest when a sort is called. Which sort would take the longest time to
execute?
(a) HeapSort (b) Bubble sort .
(c) QuickSort (with the first element as the split value) (d) SelectionSort.

49. A list is ordered from smallest to largest when a sort is called. Which sort would take the shortest time to
execute?
(a) Heapsort (b) ShortBubble (c) QuickSort (d) SelectionSort.

50. Identify one or more correct answers: Sorting an array of pointers to list elements, rather than sorting the
elements themselves, is a good idea when
(a) the number of elements is vary large
(b) the individual elements are large in size
(c) the sort is recursive
(d) there are multiple keys on which to sort the elements.

Solutions:
1. b 2. c 3. c 4. b 5. a 6. d 7. d 8. a 9. b 10. a 11. b 12. c 13. c 14. b,c 15. a
16. a 17. a 18. c 19. b 20. b 21. a 22. b 23. b 24. d 25. d 26. b 27. b,c 28. a 29. c 30. c
31. a 32. b 33. a 34. b 35. a 36. c 37. a 38. b 39. a 40. c 41. b 42. a 43. a,b 44. a 45. c
46. c 47. c 48. c 49. d 50. b

12.13 Exercise Chapter 12

1. What is a sorting? State different types of sorting method and compare between them.

2. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
bubble sort. Find out the time complexity of your algorithm.

3. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
insertion sort. Find out the time complexity of your algorithm.

4. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
selection sort. Find out the time complexity of your algorithm.

5. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
merge sort. Find out the time complexity of your algorithm.

6. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
quick sort. Find out the time complexity of your algorithm.

7. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
heap sort. Find out the time complexity of your algorithm.

8. Write down the algorithm for bubble sort and explain how you can sort a unsorted array of integers by using
radix sort. Find out the time complexity of your algorithm.
Chapter - 13
SEARCHING
13.1 INTRODUCTION
Searching is a process to find an element from a set of elements stored in an order or randomly. In this chapter
we will discuss about two popular searching techniques 1) Linear/Sequential search and 2) Binary search.

13.2 SEQUENTIAL SEARCH


Sequential search is nothing but searching an element in linear way. This can be in array or in linked list. The
array or linked list may be in sorted or unsorted order. We have a need to start the search from beginning and
scan the elements one by one until the end of array or linked list. For successful search it returns the location of
the element, otherwise returns the failure notification.

1. Sequential search in array: Algorithm 13.1 describes the procedure.

Algorithm 13.1
1. Algorithm fnSequential_search_in_Array(arrData, item, n)
2. // Purpose : This algorithm finds an element from an array of elements.
3. // Input : n is the number of elements in the array arrData and item is the data to be searched.
4. // Output : For successful search it returns position of the item in the array, otherwise returns -1.
5. {
6. for(i = 0; i <= n-1; i++)
7. {
8. if(arrData[i] == item)
9. {
10. return i;
11. break;
12. }
13. }
14. return -1;
15. }// End of Algorithm.

Consider the following array arrData[0:4].

arrData[0] arrData[1] arrData[2] arrData[3] arrData[4]


10 15 8 9 25

If the item = 9 then the above algorithm returns 3.


If the item be 20 then the above algorithm returns -1.

2. Sequential search in linked list: Algorithm 13.2 describes the procedure.

Algorithm 13.2
1. Algorithm fnSequential_search_in_LinkedList(ptrStart, item)
2. // Purpose : This algorithm finds an element from a linked list.
3. // Input : ptrStart is the starting address of the list and item is the data to be searched.
4. // Output : For successful search it returns position of the item in the array, otherwise returns -1.
5. {
6. SLLNode *ptrTemp;
7. ptrTemp = ptrStart;
8. iPosition = 1;
9. while(ptrTemp != NULL)
10. {
11. if(ptrTemp->iData == item)
12. return iPosition;
13. ptrTemp = ptrTmp->ptrNext;
14. iPosition = iPosition + 1;
15. }
16. return -1;
17. }//End of Algorithm.

Consider the following linked list:


ptrStart
10 15 8 9 25 NULL

If item = 9 then the above algorithm returns 4.


If item be 20 then the above algorithm returns -1.

3. Time complexity
4. Best case: If the desired record is present in the first position of the search table, then only one comparison
is made.
Therefore, Tb(n) = O(1)

5. Average case: The element can be found at any position in the list and the probability is 1/n. Now if the
element is found at first position then only one comparison is required, for the second position two comparisons
are required and so on. Hence,
f(n) = 1/n + 2/n + …… + n/n
= (1 + 2 + ………...+ n)/n
= n(n + 1)/2n
= (n + 1)/2
Therefore, Ta(n) = O(n)

6. Worst case: For an unsuccessful search, the search table will be visited till the last record.
Therefore, Tw(n) = O(n)

13.3 BINARY SEARCH

Binary search is performed over a sorted array of elements. The logic behind this technique is given below:
1. First find the middle element of the array.
2. Compare the middle element with the item.
3. There are three cases.
a) If it is the desired element then search is successful.
b) If it is less than the desired element then search only the first half of the array.
c) If it is greater than the desired element search in the second half of the array.
Now in linked list random access is not possible. So first we have to calculate the position of the middle
element and then sequentially we have to traverse to the middle position. That’s why it is hard to implement the
binary search over a linked list.

Example:
Consider the following sorted array:
arrData[0] arrData [1] arrData [2] arrData [3] arrData [4] arrData [5] arrData [6]
19 21 27 30 35 40 43

And the item to be searched is = 27.

Iteration Array elements Comments


[0] [1] [2] [3] [4] [5] [6]
original l=0 h=6 mid = (6+0)/2 = 3. item < arrData[mid]
19 21 27 30 35 40 43 So, now search in arrData[l:mid-1] i.e. arrData[0:2].
Step = 1 l=0 h=2 mid = (2+0)/2 = 1. item > arrData[mid]
19 21 27 30 35 40 43 So, now search in arrData[mid+1:h]i.e. arrData[2:2].
Step = 2 l=h=2 mid = (2+2)/2 = 2. item = arrData[mid]. So, successful
19 21 27 30 35 40 43 search. Return the index of the item in the array i.e. here 2.

Now, let the item = 42

Iteration Array elements Comments


[0] [1] [2] [3] [4] [5] [6]
original l = 0 h=6 mid = (6+0)/2 = 3. item > arrData[mid]
19 21 27 30 35 40 43 So, now search in arrData[mid+1 : h] i.e. arrData[4:6].
Step = 1 l=4 h=6 mid = (4+6)/2 = 5. item > arrData[mid]
19 21 27 30 35 40 43 So, now search in arrData[mid+1:h]i.e. arrData[6:6].
Step = 2 l=h=6 mid = (6+6)/2 = 6. item < arrData[mid].
19 21 27 30 35 40 43 So, now search in arrData[l:mid-1]i.e. arrData[6:5].
Step = 3 h=5 l=6 As now l > h the process is stopped and this
19 21 27 30 35 40 43 is an unsuccessful search. So, return -1.

Algorithm 13.3
1. Algorithm fnBinarySearch_NonRecursive(arrData, low, high, item)
2. // Purpose : This algorithm finds an element from a sorted array in a non recursive approach.
3. // Input : low and high are the lower and upper index of array arrData and item is the data to be searched.
4. // Output : For successful search it returns position of the item, otherwise returns -1.
5. {
6. while(low > high)
7. {
8. mid = (low + high)/2;
9. if(item == arrData[mid])
10. return mid;
11. else if(item < arrData[mid])
12. high = mid – 1;
13. else
14. low = mid + 1;
15. }
16. return -1;
17. }// End of Algorithm
Algorithm 12.4
1. Algorithm fnBinarySearch_Recursive(arrData, low, high, item)
2. // Purpose : This algorithm finds an element from a sorted array in a recursive approach.
3. // Input: low and high are the lower and upper index of array arrData and item is the data to be searched.
4. // Output : For successful search it returns position of the item, otherwise returns -1.
5. {
6. if(low > high)
7. return -1;
8. else
9. {
10. mid = (low + high)/2;
11. if(item == arrData[mid])
12. return mid;
13. else if(item < arrData[mid])
14. fnBinarySearch_Recursive(arrData, low, mid – 1, item);
15. else
16. fnBinarySearch_Recursive(arrData, mid + 1, high, item);
17. }
18. }// End of Algorithm

Time complexity:
1. Best case: If the desired record is present in the mid position of the search table, then only one comparison
is made.
Therefore, Tb(n) = O(1)

2. Average case and Worst case: Suppose T(n) unit of time is required to search the array of n elements. Now
in the second iteration the array is halved. So, in the second iteration T(n/2) time is required and so on. Hence
we get the following recurrence relation:
T(n) = c + T(n/2) c = some constant time required to find the middle element.
= c + c + T(n/22)
= ……………….
= ……………….
= kc + T(n/2k)
= c logn + T(1) [ Let 2k = n. So, k = logn]

Therefore, T(n) = O(log n)

13.4 MCQ Chapter 13


1. Which of the following is not possible as a balance factor of any node of an AVL tree?
(a) 1 (b) 2
(c) 0 (d) none of the above

2. A B-tree of order n is also called


(a) (n - n) - 1 tree (b) n - (n - 2) tree
(c) (n - 1) - n tree (d) none of these

3. A balanced search tree (B-tree) is one in which every node has between:
(a) t - 1 and 2t - 1 children (b) t and 2t children
(c) └ t / 2 ┘ and t children (d) None of these
Where t is a constant.

4. What is the maximum number of nodes in a heap with 8 leaf nodes?


(a) 15 (b) 16
(c) 17 (d) 31

5. How many distinct binary search tree can be formed which contains the integers 1, 2, 3 ?
(a) 6 (b) 5
(c) 4 (d) 3

6. Maximum possible height of an AVL Tree with 7 nodes is :


(a) 3 (b) 4
(c) 5 (d) none of the above

7. Which of the following is a hash function ?


(a) Quadratic probing (b) Chaining
(c) Open addressing (d) Folding

8. The average number of key comparisons done in successful sequential search in a list of length
n is (GATE-1996)
(a) log n (b) (n - 1)/2
(c) nl2 (d) (n + 1)/2

9. A binary search tree is generated by inserting in order the following integers:


50. 15, 62, 5. 20.. 58, 91. 3. 8, 37. 60. 24 (GATE-1996)
The number of nodes in the left subtree and right subtree of the root respectively is
(a) (4. 7) (b) (7. 4)
(c) (8, 3) (d) (3. 8)

10. An advantage of chained hash table (external sorting) over the open addressing scheme is
(a) Worst case complexity of search operations is less (GATE-1996)
(b) Space used is less
(c) Deletion is easier
(d) None of the above

11. Which of the following statements is false? (GATE-1998)


(a) A tree with n nodes has n - 1 edges.
(b) A labeled rooted binary tree can be uniquely constructed given its postorder and preorder results.
(c) A complete binary tree with n internal nodes has (n + 1) leaves.
(d) The maximum number of nodes in a binary tree of height h is (2h + 1 - 1).

12. A complete n-ary tree is one in which every node has 0 or n sons. If x is the number of
internal nodes of a complete n-ary tree, the number of leaves in it is given by: (GATE-199B)
(a) x (n - 1) + 1 (b) xn - 1
(c) xn + 1 (d) x (n + 1)

13. Which of the following is correct? (GATE-1999)


(a) B-trees are for sorting data on disk and B+-trees are for main memory
(b) Range queries are faster on B+ trees
(c) B-trees are primary indexes and B+ trees are for secondary indexes
(d) The height of a B+ tree is independent of the number of records.

14. B+ -trees are preferred to binary trees in database because


(a) Disk capacities are greater than memory capacities
(b) Disk access is much slower than memory access
(c) Disk data transfer rates are much less than memory data transfer rates
(d) Disks are more reliable than memory.

15. The average successful search time for sequential search on 'n' items is
(a) n / 2 (b) (n-1) / 2 (c) (n+l) / 2 (d) log (n + 1)

16. The order of the binary search algorithm is


(a) n (b) n2 (c) nlog(n) (d) log(n)

17. Which is the best?


(a) Binary search
(b) Sequential search
(c) Indexing
(d) Can’t say as all are best in their own application

18. The element being searched for is not in an array of 100 elements. What is the average number of
comparisons needed in a sequential search to determine that the element is not there if the elements are
completely unordered?
(a) 100 (b) 50 (c) 75 (d) 25

19. The element being searched for is not in an array of 100 elements. What is the average number of
comparisons needed in a sequential search to determine that the element is not there if the elements are ordered
from smallest to largest?
(a) 100 (b) 50 (c) 75 (d) 25

20. The element being searched for is not in an array of 100 elements. What is the average number of
comparisons needed in a sequential search to determine that the element is not there if the elements are ordered
from largest to smallest?
(a) 100 (b) 50 (c) 75 (d) 25

21. The element being searched for is in an array of 100 elements. What is the average number of comparisons
needed in a sequential search to determine the position of the element if the elements are completely unordered?
(a) 100 (b) 50 (c) 75 (d) 25

Solutions:
1. b 2. d 3. a 4. a 5. b 6. b 7. d 8. d 9. b 10. b 11. b 12. a 13. b 14. a 15. a
16. d 17. d 18. a 19. b 20. b 21. c

13.5 Exercise Chapter 13

1. What is a searching? What are thedifferent types of searching? Briefly describe them with a suitable
example.
2. Write an algorithm to find an element in a sorted array by sequential search and explain it by a suitable
example and diagram. Find out the time complexity.

3. Write an algorithm to find an element in a single linked list by sequential search and explain it by a suitable
example and diagram. Find out the time complexity.

4. Write an algorithm to find an element in a double linked list by sequential search and explain it by a suitable
example and diagram. Find out the time complexity.

5. Write an algorithm to find an element in a sorted array by binary search and explain it by a suitable example
and diagram. Find out the time complexity.

6. Write an algorithm to find an element in a single linked list by binary search and explain it by a suitable
example and diagram. Find out the time complexity.

7. Write an algorithm to find an element in a double linked list by binary search and explain it by a suitable
example and diagram. Find out the time complexity.
CHAPTER 14
HASHING
14.1 Introduction
In this chapter we examine data structures which are designed specifically with the objective of providing
efficient insertion and find operations. In order to meet the design objective, certain concessions are made.
Specifically, we do not require that there be any specific ordering of the items in the list. In addition, while we
still require the ability to remove items from the list, it is not our primary objective to make removal as efficient
as the insertion and find operations. Ideally we would build a data structure for which both the insertion and
find operations are O(1) in the worst case.
In the case of an ordered list, the cost of an insertion is O(1) and the cost of the find operation using
linear search is O(n). For a sorted list, the cost of insertion is O(n) and the cost of the find operation using
binary search is O(n logn) for the array implementation.
In order to meet the performance objective of constant time insert and find operations, we need a way to
do them without performing a search. I.e., given an item k, we need to be able to determine directly from k the
array position where it is to be stored.
Therefore, hashing is a searching technique which takes constant time (O(1)) to find an item from a list.

14.2 Hash table


A hash table is an array of m buckets, together with a hash function H(k) that translates each key k to a bucket
index (in the range 0…m–1). A bucket has number of slots and each slot is capable to hold one record.
Hash function Hash table (array of buckets)

key value 0

k1 1
2
k2 2
m-2
3
k3 2
4
k4 5
5

kn 4 m–2
Hashing (translating keys to m–1
bucket indices)

Figure 14.1
Principles of Hash tables:
1. Each key k has a home bucket in the hash table, namely the bucket with index H(k).
2. To insert a new entry with key k into the hash table, assign that entry to k’s home bucket.
3. To search for an entry with key k in the hash table, look in k’s home bucket.
4. To delete an entry with key k from the hash table, look in k’s home bucket.
5. The hash function must be consistent:
k1 = k2 => H(k1) = H(k2).
6. In general, the hash function is many-to-one.
7. Always prefer a hash function that makes collisions relatively infrequent.
14.3 Hash functions

In this section we will discuss about several hashing functions. Now we assume that we are dealing with integer
valued keys. A hash function can be defined as a function which takes key (k) as input and produces a bucket
index as output.

H(k) = L

Where,H is a hash function.


k is a set of keys.
L is bucket indices (or set of memory addresses).

Some of the popular hash functions are described below:

1. Division method: Perhaps the simplest of all the methods of hashing a key k is to divide k by M and then to
use the remainder as the bucket index. This is called the division method of hashing. In this case, the hash
function is

H(k) = k mod M

In certain situations, some extra care is needed in the selection of a suitable value for M. For example, it
is often convenient to make M an even number. But this means that H(k) is even if k is even; and H(k) is odd if
k is odd. If all possible keys are equiprobable, then this is not a problem. However if, say, even keys are more
likely than odd keys, the function H(k) = k mod M will not spread the hashed values of those keys evenly.
Similar problem may occur for odd value of M.

Similarly, it is often tempting to let M be a power of two. E.g., M = 2q for some integer q>1. In this case,
the hash function H(k) = k mod M simply extracts the bottom q bits of the binary representation of k. While this
hash function is quite easy to compute, it is not a desirable function because it does not depend on all the bits in
the binary representation of k.

For these reasons M is often chosen to be a prime number. For example, suppose there is a bias in the
way the keys are created that makes it more likely for a key to be a multiple of some small constant, say two or
three. Then making M a prime increases the likelihood that those keys are spread out evenly. Also, if M is a
prime number, the division of k by that prime number depends on all the bits of k, not just the bottom q bits, for
some small constant q.

Example 14.1: Consider a hash table with 7 slots and the keys are 49, 123, 68, and 90. Suppose we are taking
the value of M = 7.
Then,
H(49) = 49 mod 7 = 0
H(123) = 123 mod 7 = 4
H(68) = 68 mod 7 = 5
H(90) = 90 mod 7 = 6

The hash table will look like:


Division method Hash table (array of buckets)

key value 0 49

49 0 1
123 4
2
68 5
3
90 6
4 123

5 68

6 90

Algorithm 14.1 describes an algorithm which creates a bucket index according to division method.
Algorithm 14.1
1. Algorithm fnDivision(k, M)
2. // Purpose : This algorithm produces the bucket index for a key using division method.
3. // Input : The key k and the prime number M.
4. // Output : The bucket address L.
5. {
6. L = k%M; //Get the remainder.
7. return L;
8. }// End of algorithm

Disadvantages:
A potential disadvantage of the division method is due to the property that consecutive keys map to consecutive
hash values. While this ensures that consecutive keys do not collide, it does mean that consecutive array
locations will be occupied. In certain implementations this can lead to degradation in performance. In the
following sections we consider hashing methods that tend to scatter consecutive keys.

2. Middle square method: In middle square method the key k is squared and then some particulars bits from
the middle of k2 (for example 3rd and 4th LSB) are extracted to get the desired key. The positions of these
particular bits are same for all the keys. For example, if the 3 rd and 4th LSB are considered for the square of key
k1, then for k2 also 3rd and 4th LSB are considered. Therefore the hash function is:
H(k) = some bits of (k2)

Example 14.2: Consider a hash table with 10 slots and the keys are 49, 123, 68, and 100. Here we are taking
the 3rd LSB from the square of each key.
Then,
H(49) = 3rd LSB(2401) = 4
H(123) = 3rd LSB(15129) = 1
H(68) = 3rd LSB(4624) = 6
H(100) = 3rd LSB(10000) = 0

The hash table looks like:


Middle square method Hash table (array of buckets)

key value 0 100

49 4 1 123
123 1
2
68 6
3
100 0
4 49

6 68

Algorithm 14.2 describes an algorithm which creates a bucket index according to middle square method.
Algorithm 14.2
1. Algorithm fnMidSquare(k, b1, b2)
2. // Purpose : This algorithm produces the bucket index for a key using middle square method.
3. // Input : k is the key and from b1 to b2 bits are to be extracted from k2.
4. // Output : The bucket index L.
5. {
6. k1 = k*k/ 10(b1-1); //Discard the left most bits and store the number in k1.
(b2-b1+1)
7. L = k1 % 10 ; //Extract leftmost (b2-b1) bits from k1.
8. return L;
9. }// End of algorithm

3. Folding method: In folding method the key k is partitioned into a number of parts k1, k2, k3….kr, where
each part except possibly the last has the same number of digits as the required address. Then the parts are
added together ignoring the last carry. Therefore the hash function is:
H(k) = k1 + k2 + k3 + …. + kr where the carry is ignored.

Example 14.3: Consider a hash table with 10 slots and the keys are 49, 123, 68, and 90. Here we are
partitioning the key with size one.
Then,
H(49) = 4 + 9 = 3 ignoring the carry.
H(123) = 1 + 2 + 3 = 6
H(68) = 6 + 8 = 4 ignoring the carry.
H(90) = 9 + 0 = 9

The hash table looks like:


Folding method Hash table (array of buckets)

key value 0
49 3 1
123 6
2
68 4
3 49
90 9
4 68

6 123

9 90

The algorithm 14.3 illustrates the folding method of hashing:


Algorithm 14.3
1. Algorithm fnFolding(k)
2. // Purpose : This algorithm produces the bucket index for a key using folding method.
3. // Input : The key k.
4. // Output : The bucket address L.
5. {
6. q = 1;
7. p = 0;
8. L = 0;
9. while(k!=0)
10. {
11. p = p + 10q * (k%10);
12. k = k/10;
13. q=q+1;
14. if(q=size of each partition)
15. {
16. L = L + p;
17. p = 0;
18. q = 0;
19. }
20. }
21. if(length of L > size of each partition)
22. Discard the MSB from L.
23. return L
24. }// End of algorithm
14.4 Collision

If two keys k1 and k2 produce same bucket index L for some hash function, then it is called collision. To
resolute the collision different collision resolution techniques are available. The collision resolution techniques
can be classified into two broad categories:
1. Open addressing
2. Chaining
They are discussed in next section in details.

14.5 Clustering

Clustering occurs when keys group together in the hash table and many collisions. Clustering causes collision
and collisions make the clustering problem worse.

14.6 Collision resolution techniques

1. Open addressing: The general procedure for open addressing can be stated as:
i) All keys are stored in hash table only.
ii) When collisions occur, use a systematic (consistent) procedure to store elements in free slots of the
hash table.
Depending on the systematic procedure open addressing can be of the following types:

A) Linear probing: Suppose two keys k1 and k2 yield same memory location (or bucket index) L.
H(k1) = L and H(k2) = L
Now to resolve this collision, search the memory for an available place like:
L, L+1 …… L + i …….
Store the key in the first available place.

Example 14.4: Consider a hash table with 7 slots and the keys are 49, 123, 68, and 90. Suppose we are using
division method to produce bucket index and we are taking the value of M = 7. The hash table is circular i.e.
bucket index 0 comes after bucket index 6. Collision resolution technique is linear probing.
Now,
H(49) = 0 Store 49 at 0th location in hash table.
H(123) = 5 Store 124 at 5th location in hash table.
H(68) = 5 Cell 5 in hash table is already occupied. So check next cell. Cell 6 is empty. Store 68 in 6th cell.
H(90) = 5 Cell 5, 6 and 0 are already occupied. So store 90 at cell 1 in the hash table.

The hash table will look like:


Division method Hash table (array of buckets)

key value 0 49

49 0 1 90
123 5
2
68 5
3
90 5
4
5 123

6 68

B) Quadratic probing: Suppose two keys k1 and k2 yield same memory location (or bucket index) L.
H(k1) = L and H(k2) = L
Now to resolve this collision, search the memory for an available place like:
L, L+12, L+22 …… L + i2 …….
Store the key in the first available place.

Example 14.4: Consider a hash table with 7 slots and the keys are 49, 123, 68, and 90. Suppose we are using
division method to produce bucket index and we are taking the value of M = 7. The hash table is circular i.e.
bucket index 0 comes after bucket index 6. Collision resolution technique is quadratic probing.
Now,
H(49) = 0 Store 49 at 0th location in hash table.
H(123) = 5 Store 124 at 5th location in hash table.
H(68) = 5 Cell 5 in hash table is already occupied. So check 5+12 cell. Cell 6 is empty. Store 68 in 6th cell
H(90) = 5 Cell 5 and 5 + 12 are already occupied. Now check cell 5 + 2 2. As the list is circular 5+ 2 2 or 9 is
interpreted as cell 2 (9%7). Cell 2 is empty. So store 90 at cell 2 in the hash table.

The hash table will look like:

Division method Hash table (array of buckets)

key value 0 49

49 0 1
123 5 90
2
68 5
3
90 5
4
5 123

6 68
C) Double hashing: Here a second hash function is used to resolve the collisions. If two keys k1 and k2 yield
the same memory location, then search the memory linearly for an available place like:
L, L+H’, L+2H’……..L + iH’……
Store the key at the first available memory location.

D) Rehashing: If at any stage the hash table becomes full or overflow then it will be very difficult to find the
free slot for new key. In such a situation create a new hash table which is double in length than the previous
one. Then scan the previous hash table and for each key calculate the new bucket index and store them into
newly created hash table.

2. Chaining: The general procedure for chaining is:


i) Store all elements that hash to the same slot in a linked list.
ii) Store a pointer to the head of the linked list in the hash table slot.

Example 14.5: Consider a hash table with 7 slots and the keys are 49, 123, 68, and 87. Suppose we are using
division method to produce bucket index and we are taking the value of M = 7. The hash table is circular i.e.
bucket index 0 comes after bucket index 6. Collision resolution technique is chaining.
Now,
H(49) = 0 Store 49 at 0th location in hash table.
H(123) = 5 Store 124 at 5th location in hash table.
H(68) = 5 Cell 5 in hash table is already occupied. So check 5+12 cell. Cell 6 is empty. Store 68 in 6th cell.
H(87) = 3 Cell 5 and 5 + 12 are already occupied. Now check cell 5 + 2 2. As the list is circular 5+ 2 2 or 9 is
interpreted as cell 2 (9%7). Cell 2 is empty. So store 90 at cell 2 in the hash table.

The hash table will look like:

Division method Hash table (array of addresses)

key value 0 49 N

49 0 1
123 5
2
68 5
3 87 N
87 3
4
5 123 68 N

Comparison study of open addressing and chaining:

Collision resolution technique Advantages Disadvantages


Open addressing 1. Save space since we don’t have 1. Clustering problem.
pointers. 2. Priory knowledge is required
2. Fast rehashing. about maximum number of
3. Fast access through use of main elements.
table space. 3. Multiple collisions may occur.
Chaining 1. Unlimited number of elements Overhead of multiple linked lists.
2. Unlimited number of collisions
3. No clustering problem.
4. Deletion is simpler

14.7 Load factor


Suppose there are n items in the bucket and bucket size is m. Then the load factor can be defined as –
Load factor = occupied space/total space
λ = n/m.
λ=0 indicates an empty bucket. λ = 0.5 signifies that the bucket is half full. Similarly λ=1 means full bucket. For
open addressing λ can never exceed 1, but for chaining there is no bar on the value of λ.

14.8 Analysis of open addressing and chaining


In our analysis, we will count the number of probes (or search) required for a successful search and the number
of probes (or search) required for an unsuccessful search.
U(λ) = number of probes required for an unsuccessful search.
S(λ) = number of probes required for a successful search.
The probability (P(h)) that a probe hits an occupied space is λ and the probability (P(m)) that a probe hits an
empty space is 1- λ.
P(h) = λ and P(m) = 1- λ
Now the probability that one probe is required for an unsuccessful search is (1- λ). The probability that the
unsuccessful search terminates after two probes is λ(1- λ). Similarly the probability that the exactly k probes are
required for an unsuccessful search is λk-1(1- λ).
For linear probing, the average number of probe required for an unsuccessful search is:
U(λ) = 1/2(1+1/(1- λ)2)
And for a successful search, average number of probe is:
S(λ) = 1/2(1+1/(1- λ))
For chaining, the average numbers of probes required for an unsuccessful search and for a successful search
are approximately:
U(λ) = e- λ + λ and S(λ) = 1 + λ /2

/* Program to implement some hash functions */


/* File Name: Hash_Function.C */
#include<stdio.h>
#include<conio.h>
#include<math.h>

int fnDivision(int k, int M)


// Purpose : This algorithm produces the bucket index for a key using division method.
// Input: The key k and the prime number M.
// Output : The bucket address L.
{
int L;
L = k%M; //Get the remainder.
return L;
}// End of algorithm

int fnMidSquare(int k, int b1, int b2)


// Purpose : This algorithm produces the bucket index for a key using middle square method.
// Input: k is the key and from b1 to b2 bits are to be extracted from k2.
// Output : The bucket index L.
{
int k1,L;
k1 = (k*k)/ fnpow(10,(b1)); //Discard the left most bits and store the number in k1.
L = k1 % fnpow(10,(b2-b1+1)); //Extract leftmost (b2-b1) bits from k1.
return L;
}// End of algorithm
int fnpow(int x, int y)
{
int i,x1=1;
for(i=1;i<=y;i++)
x1=x1*x;
return x1;
}

void main(void)
{
clrscr();
printf("Division Method = %d",fnDivision(20,9));
printf("Mid Square of %d = %d",99*99,fnMidSquare(99,2,2));
getch();
}

14.9 MCQ Chapter 14

1. Consider the figure below

0 S7
1 S1
2
3 S4
4 S2
5
6 S5
7
8 S6
9 S3
A hash table with 10 buckets with one slot per bucket is depicted in the above Fig. The symbols, S 1 to S7
are initially entered using a hashing function with linear probing. The maximum number of comparisons
needed in searching an item that is not present is
(a) 4 (b) 5 (c) 6 (d) 3

2. A hash function f defined as f (key) = key mod 7, with linear probing, is used to insert the keys 37, 38, 72,
48, 98 , 11, 56, into a table indexed from 0 to 6. What will be the location of key II?
(a) 3 (b) 4 (c) 5 (d) 6
3. The average search time of hashing, with linear probing will be less if the load factor
(a) is far less than one (b) equals one
(c) is far greater than one (d) none of the above

4. A hash table can store a maximum of 10 records. Currently there are records in locations 1,3, 4, 7, 8, 9, 10.
The probability of a new record going into location 2, with a hash function resolving collisions by linear
probing is
(a) 0.6 (b) 0.1 (c) 0.2 (d) 0.5

5. Consider a hashing function that resolves collision by quadratic probing. Assume the address space is
indexed from 1 to 8. Which of the following locations will never be probed if a collision occurs at position
4?
(a) 4 (b) 5 (c) 8 (d) 2

6. A hash table has space for 100 records. What is the probability of collision before the table is 10% full?
(a) 0.45 (b) 0.5 (c) 0.3 (d) 0.34 (approximately)

7. A hash function randomly distributes records one by one in a space that can hold x number of records. The
probability that the mth record is the first record to result in collision is
(a) (x-1) (x-2)...(x-(m-2))(m-l) / xm-l
(b) (x-1) (x-2)... (x-(m-l))(m-l) / xm-l
(c) (x-1) (x-2)... (x-(m-2))(m-l) / xm
(d) (x-1) (x-2)... (x-(m-l))(m-l) / xm

8. If the hashing function is the remainder on division, then clustering is more likely to occur if the storage
space is divided into 40 sectors rather than 41. This conclusion is
(a) more likely to be false (b) more likely to be true
(c) is always false (d) none of the above

Solutions:
1. b 2. c 3. a 4. a 5. d 6. a 7. a 8. b

14.5 Exercise Chapter 14

1. What is hashing? Define hash table and explain by a suitable diagram. State the principles of hash table.
2. Define hash function. State different types of hash functions, give their algorithms and explain them by
suitable diagrams.
3. What are collision and clustering? What is collision resolution technique? Compare between different types
of collision resolution technique.
4. Discuss different types of collision resolution technique with suitable diagrams.
5. What is load factor? Analyse different types of collision resolution technique with help of load factor.
CHAPTER 15
FILES

Introduction

In our present banking systems only ATM card is sufficient to operate all operations of an account like (i)
To see the balance (ii) To withdraw amount (iii) To generate mini statement to see previous transactions
both deposit and withdrawal etc. . But one can have access if ATM card is placed on an ATM machine
and with typing the proper password. Thus no paper, no cheque book etc are required and at the same time
no intervention and permission of bank officials are also required for operating a bank account. This is
possible only because all data are stored electronically in non volatile ( Tape, Disk, Drum, CD, DVD etc.)
devices with the help of computers and brought back the same when necessary. Not only in banking but
all most all spheres of life nowadays have been driven by computers, where storing of data, program etc.
are very essential for future and repeated usage, which leads to evolve various areas of computer sc. like
Data Base Management System, Data Mining, Image Processing, Bio-Informatics etc. In context of data
structure we will discuss very limitied types of files and mechanism how to handle these files for various
operations.

15.2 Definition
(1) Collection of data in non-volatile memory of a computer.
(2) Collection of related records in non-volatile memory of a computer.
The definition (1) suggests to put any characters, numbers and symbols to put together and to store in
secondary storage device with a specific name. To retriev back the same in future with supply of the
specified name the computer will display the content i.e. what was stored in the secondary storage for the
same. This type of file is called non-structured file, i.e. no particular structure or the format is required to
store data in such type of file. The usage of such file is generally in the area of text processing, image
processing and in program development. But the essential areas of computing like Banking, Hospitals,
Insurance and Universisties need data to be stored in secondary storage device in the structured format for
processing. Where the basic format is record, i.e. each data should belong to a particular heading termed
as fields and when a group of headings are defined together for a particular entity or things a record
structure is created. Whenever all the relevant data are written under the corresponding headings or fields
a record is created and when all the possible records are collected together a structured file is formed. e.g.
an account of any bank is represented as

100100 Sourav Ganguly 25000

where the first number is account number, the second one is name and the third one is balance, like wise
all the accounts of the bank could be represented as

File: Account

Account Number Name Balance


100100 Sourav Ganguly 25000
100101 Ashoke Mukherjee 12000
100102 Suman Gupta 13000
… … …
… … …
… … …
15.3 File Classification:

Non Structured file:


Text File
A text file is a kind of computer file that is structured as a sequence of lines. A text file exists within a computer
file system. The end of a text file is denoted by placing one or more special characters, known as an end-of-file
marker, after the last line in a text file.
(.txt) is a filename extension for files consisting of text usually contain very little formatting (ex: only bolding
or italics). The precise definition of the .txt format is not specified, but typically matches the format accepted by
the system terminal or simple text editor. Files with the .txt extension can easily be read or opened by any
program that reads text and, for that reason, are considered universal (or platform independent).
The ASCII character set is the most common format for English-language text files, and is generally assumed to
be the default file format in many situations. For accented and other non-ASCII characters, it is necessary to
choose a character encoding. In many systems, this is chosen on the basis of the default locale setting on the
computer it is read on. Common character encodings include ISO 8859-1 for many European languages.
Because many encodings have only a limited repertoire of characters, they are often only usable to represent
text in a limited subset of human languages. Unicode is an attempt to create a common standard for representing
all known languages, and most known character sets are subsets of the very large Unicode character set.
Although there are multiple character encodings available for Unicode, the most common is UTF-8, which has
the advantage of being backwards-compatible with ASCII: that is, every ASCII text file is also a UTF-8 text file
with identical meaning.

System File
A system file is a computer file important to the operating system. More specifically, it refers to the files which
are used to build the operating system unning on a computer system. Examples of system files are: command
file, Executable file, Dynamic Link laboratory file etc.

Command File (.COM):


The command file having file name extension (.com) has been used in various computer systems for different
purposes. Originally, the term stood for "Command file" and was a text file containing commands to be issued
to the operating system. In MS-DOS and compatible DOSes, and in 8-bit CP/M, a COM file is a simple type of
executable file.
The COM format is perhaps the simplest executable format of all; it contains no metadata, only code and data,
and is loaded at offset 0x0100 of some segment of memory and executed. Because of how the segmentation
model works, there is no need for relocation. In Intel 86(Early version of x86) CPU architecture, only 65,536
bytes of memory could be addressed (address range 0x0000 to 0xFFFF). Under CP/M, the first page of this
memory, from 0x0000 to 0x00FF was reserved for system use, and any user program had to be loaded at
exactly 0x0100 to be executed. COM files fit this model perfectly. Note that there was no possibility of running
more than one program or command at a time: the program loaded at 0x0100 was run, and no other. Although
the file format is the same in MS-DOS and CP/M, this does not mean that CP/M programs can be directly
executed under MS-DOS or vice versa; MS-DOS COM files contain x86 instructions, while CP/M COM files
contain 8080, 8085 or Z80 instructions. Additionally, MS-DOS COM files often depend on operating system
traps supplied exclusively by MS-DOS via interrupt 21h. It is possible to construct a fat COM file which both
processor families can execute. Files may have names ending in .COM, but not be in the simple format
described above; this is indicated by a magic number at the start of the file. For example, the
COMMAND.COM file in DR-DOS 6 is actually in DOS executable format, indicated by the first two bytes
being MZ (0x4D 0x5A). Under CP/M 3, if the first byte of a COM file is 0xC9 then this indicates the presence
of a 256-byte header; since 0xC9 corresponds to the 8080 instruction RET, this means that the COM file will
immediately terminate if run on an earlier version of CP/M that does not support this extension.

Executable File (.EXE)


EXE is the common filename extension denoting an executable file (a program) in the OpenVMS, DOS,
Microsoft Windows, ReactOS, and OS/2 operating systems. Besides the executable program itself, many EXE
files contain other components called resources, such as bitmaps and icons which the executable program may
use for its graphical user interface.
The DOS executable file format differs from the COM executable, which is limited to slightly less than 64 KB
in size and since it lacks relocation information, can only contain one code segment. The DOS executable
header contains such relocation information, which allows multiple segments to be loaded at arbitrary memory
addresses, and support executables larger than 64 KB.
There are several main EXE file formats.
(i) DOS executable: These can only be identified by the ASCII string "MZ" or the hexadecimal 4D 5A at the
beginning of the file (the "magic number"), although, in fact, "ZM" or the hexadecimal 5A 4D works as well.
These executables can be run from DOS, and most Windows versions can execute them using a sort of
emulation.
(ii) 16-bit New Executable: Introduced with Multitasking MS-DOS 4.0, these can be identified by the "NE" in
ASCII. These cannot be run by any other version of DOS but can be run by all Windows and OS/2 versions.
(iii) Mixed 16/32-bit Linear Executable: Introduced with OS/2 2.0, these can be identified by the "LE" in
ASCII. This format is not used for OS/2 applications anymore, but instead for VxD drivers under Windows 3.x
and Windows 9x, and by some DOS extenders.
(iv) 32-bit Linear Executable: Introduced with OS/2 2.0, these can be identified by the "LX" in ASCII. These
can only be run by OS/2 2.0 and higher. They are also used by some DOS extenders.
(v) 32-bit Portable Executable: Introduced with Windows NT, these are the most complex and can be identified
by the "PE" in ASCII. These can be run by all versions of Windows NT, and also Windows 95 and higher,
partially also in DOS using HX DOS Extender. They are also used in BeOS R3, however the format used by
BeOS somewhat violates the PE specification as it doesn't specify a correct subsystem.
(vi) 64-bit Portable Executable: Introduced by 64-bit versions of Windows, these are PE files with a CPU type
corresponding to a 64-bit instruction set such as x86-64 or IA-64. These can only be run by 64-bit editions of
Microsoft Windows, such as Windows XP 64-Bit Edition or Windows Server 2003 64-Bit Edition, running on
machines with the CPU type specified in the file.

Dynamic Link Laboratory File(.DLL, .OCX, .DRV)


Dynamic-link library file or DLL file, is Microsoft's implementation of the shared library concept in the
Microsoft Windows and OS/2 operating systems. These libraries usually have the file extension DLL, OCX (for
libraries containing ActiveX controls), or DRV (for legacy system drivers). The file formats for DLLs are the
same as for Windows EXE files — that is, Portable Executable (PE) for 32-bit Windows, and New Executable
(NE) for 16-bit Windows. As with EXEs, DLLs can contain code, data, and resources, in any combination. It
can also have icon libraries, sometimes having the extension ICL, and font files, having the extensions FON
and FOT.
Structured File
Sequential File
Definition: When records are stored in seceondary storage media as per the ordering of a search key field of
every records.
Example: file: ClassGrade
Roll No. Name Grade
001 Sourav Ganguly A
002 Ashoke Mukherjee B
003 Suman Gupta B
… … …
… … …
… … …

The file ClassGrade is a sequential file where each record is ordered on Roll No.

Realization of Sequential file in Computer:


Though to create and to access any data file programming is required in any language but the underlying
operating system(OS) has only the low level system calls to create and to access files in secondary storage
devices, since OS maintains blocks, clusters, sectors etc. in file allocation table for providing spaces to create
any data file. There are following things to be remembered always to work with file.
1) First of all a file must be opened in workspace for either putting data into it or to retrieve data out
from the file. There are following modes on which a file can be opened.
i) Mode: Output: Which is essential to open a file at first time creation.
ii) Mode: Input: Which is essential to open a file for retrieval of data.
iii) Mode: Input/Output : Which is essential for updation purpose.

Once a data file has been created with Output mode and dumping of reords on it have been done the file must
be closed. After closing the file if by mistake the file is reopened in Output mode all the data written earlier will
be lost. Thus, to access next the Input mode is essential.

In ‘C’ language the modes are represented as:


(i) Output: “R” (Read only)
(ii) Input: “A” (Append only), “W” (Write only)
(iii) Input/ Output:“W+” (Read and write), “R+” (Read and write), “A+” (Append and write)

Secondly, in ‘C’ language “File Pointer” is very essential to keep track of positions of records as bellow:

Address Record Pointer


2200 A 2203
2201 A EOF
File pointer 2202 D 2200
Start(BOF) 2203 T 2201

Index Sequential File


The retrieval of a record from a sequential file, on average, requires traverse of half of the total file. This makes
the enquiries not only inefficientbut very time consuming for a large file. To improve this, a type of indexing
technique ia added to sequential file and is named as index sequential file.

Definition: A sequential file that is indexed for faster search is called index sequential file.
Example: Index for starting page numbers of a dictionary file

Words Starting with letters Page #


A 3
B 42
C 86
D 158
… …
… …
X 807
Y 809
Z 812

An index is a set of <key, address> pairs. Indexing associates a set of objects to a set of ordered quantities,
which are usually smaller in number, giving us a mechanism to search faster.Indexes are created from a
sequential or sorted set of primary keys from a sequential file. The indexes provides random access to the
record fields of the file, while the sequential nature of the file provides easy access to the subsequent records as
well as sequential processing.

Index can be of three types:


(iv) Implicit Index:
(v) Limit Index
(vi) Multilevel Index

15.4 Example Programs:

15.4.1 Non Structured file (Read and Write a Text File)

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

void fnReadFile(FILE*);
void fnWriteFile(FILE*);
void main(void)
{
FILE *fp1,*fp2;
int Choice;
clrscr();
while(1)
{
printf("\nMENU");
printf("\n1. Read from file");
printf("\n2. Write into file");
printf("\n3. Exit");
printf("\nEnter your choise");
scanf("%d",&Choice);
switch(Choice)
{
case 1:
fp1=fopen("test1.txt","r");
fnReadFile(fp1);
fclose(fp1);
break;
case 2: fp1=fopen("test1.txt","a+");
fnWriteFile(fp1);
fclose(fp1);
break;
case 3:
exit();
}
}
getch();
}
void fnReadFile(FILE *Source)
{
char c;
while((c=getc(Source))!=EOF)
printf("%c",c);
}
void fnWriteFile(FILE *Source)
{
char c;
while((c=getche())!='q')
putc(c,Source);
}

Structured File (Read and Write a Sequential File)

#include<stdio.h>
#include<conio.h>
struct student
{
int iRoll;
char cName[50];
char cStream[20];
};
typedef struct student student;
void fnReadFile(FILE*);
void fnWriteFile(FILE*);
void main(void)
{
FILE *fp1,*fp2;
int Choice;
clrscr();
while(1)
{
printf("\nMENU");
printf("\n1. Read from file");
printf("\n2. Write into file");
printf("\n3. Exit");
printf("\nEnter your choise");
scanf("%d",&Choice);
switch(Choice)
{
case 1:
fp1=fopen("test1.txt","r");
fnReadFile(fp1);
fclose(fp1);
break;
case 2: fp1=fopen("test1.txt","a+");
fnWriteFile(fp1);
fclose(fp1);
break;
case 3:
exit();
}
}
}
void fnReadFile(FILE *Source)
{
student s;
while(fread(&s,sizeof(student),1,Source))
printf("\n%d\t%s\t%s",s.iRoll,s.cName,s.cStream);
}
void fnWriteFile(FILE *Source)
{
student s;
printf("\nEnter Student's roll, name and stream");
fflush(stdin);
scanf("%d",&s.iRoll);
fflush(stdin);
gets(s.cName);
fflush(stdin);
gets(s.cStream);
fwrite(&s,sizeof(student),1,Source);
}

15.4 MCQ Chapter 15

1. Six files F1, F2, F3 , F4, F5 and F6 have 100, 200, 50, SO, 120, 150 number of records respectively. In what
order should they be stored so as to optimize access time? Assume each file is accessed with the same
frequency.
(a) F3, F4, F1, F5, F6, F2
(b) F2, F.6, F5, F1, F4, F3
(c) F1, F2, F3, F4, F5, F6
(d) Ordering is immaterial as all files are accessed with the same frequency.

2. Pick the correct statements.


(a) Sequential file organization is suitable for batch processing.
(b) Sequential file organization is suitable for interactive processing.
(c) Indexed sequential file organization supports both batch and interactive processing.
(d) Relative file can't be accessed sequentially.

3. Which of the following file organizations is preferred for secondary key processing?
(a) Indexed sequential file organization
(b) Two-way linked list
(c) Inverted file organization
(d) Sequential file organization

Solutions:
1. a 2. a,c 3. c

Exercise Chapter 15

1. Discuss the differences between the following file organizations:


(a) (i) Sequential
(ii) Index sequential
(b) (i) Text
(ii) System
(c) (i) Command file
(ii) Executable file
Compare their storage and access efficiencies. State one applications o each file organisations.

2. What is index sequential file organisations? What are the advantages and disadvantages of index sequential
files?

3. Write a C program to append some text after rhe contents of a simple text file “text.txt”.

4. Give algorithms for insertion and deletion records from a Sequential file.

5. What is index? What are the various types of indexing? State the advantages of using indexing over a
sequential file. Can this indexing be used in non structured file?
VARIOUS QUESTIONS ON DATA STRUCTURE AND
ALGORITHM
(As because size of interger datatype in different C compiler is different, we have used size_t instead of int
datatype. For simplicity Let size_t as int datatype with 4 byte size wherever mentioned)

1. Describe one good method for precisely specifying what a function must do, without indicating how the
function accomplishes its work. Provide an example, using a small function.

2. What is a precondition? What is a postcondition?

3. It's recommended that the precondition be checked at the start of a function. What's a good approach if a
function discovers that its precondition is not true?

4. Is it always possible for a function to check that its precondition is true?

5. Suppose that you accidently call a correctly implemented function, but the precondition is false. Is the
function guaranteed to halt with a nice message? Is the function allowed to erase everything on your hard drive?

6. Write the first few lines of this function so that it uses the assert facility to check its precondition:
void exam(int i)
// Precondition: i is not equal to 42.
...

7. Give a concise formula that gives the approximate number of digits in a positive integer. The integer is
written in base 10.

8. Why is the order of an algorithm generally more important than the speed of the processor?

9. Convert each time formula to the best possible big-O notation. Do not include any spurious constants in your
big-O answer.
Time Formula
10n
2n²
3 times log (base 2) of n
2n² + 10n

10. Write the simplest big-O expression to describe the number of operations required for the following
algorithm:
for (i = 1; i < N; ++i)
{
...statements that require exactly i operations...
}

11. You are at a computer science cocktail party and you meet a student who has just started working with a
debugger. With about three or four sentences, explain the basic features of your debugger and how they help
you find bugs.
12. Suppose that p is an int variable. Write several lines of code that will make p point to an array of 100
integers, place the numbers 0 through 99 into the array components, and return the array to the heap. Do not use
malloc.

13. What happens if you call new but the heap is out of memory?

14. Draw a picture of memory after these statements:


int i = 42;
int k = 80;
int* p1;
int* p2;
p1 = &i;
p2 = &k;

15. Suppose that we execute the statements from the previous question, and then we execute these statements:
*p1 = *p2;
Draw a picture of memory after this additional statement.

16. Draw a picture of memory after these statements:


int i = 42;
int k = 80;
int* p1;
int* p2;
p1 = &i;
p2 = &k;
p1 = p2;

17. Here is a function prototype:


void exam(const double data[]);
Write two or three sentences to tell all the information that you know about the parameter.

18. Consider the following prototype:


function foo (const int * p);
What restriction does the const keyword provide within the implementation of foo?

18. Consider a bag structure that stores the items in a dynamic array. Use an example with pictures to explain
what goes wrong if the automatic assignment operator is used for this bag.

19. Consider the bag from Question no 18 (using a dynamic array). If the insert function is activated, and the
bag is full, then the bag is resized with the statement:
reserve(used + 1);
Describe a problem with this approach, and provide a better solution.

20. Consider the bag from Question no 18 (using a dynamic array). The pointer to the dynamic array is called
data, and the size of the dynamic array is stored in a private member variable called capacity. Write the
following new member function:
void bag::triple_capacity( )
// Postcondition: The capacity of the bag's dynamic array has been
// tripled. The bag still contains the same items that it previously
// had.
Do not use the reserve function, do not use realloc, and do not cause a heap leak. Do make sure that both data
and capacity have new values that correctly indicate the new larger array.
21. Implement functions to meet the following specificaions. In each case, you should make an appropriate
choice for the kind of parameter to use
A. A void function with one parameter (a polynomial p). The function prints the value of p evaluated at x=1,
x=2, x=3,..., up to x=10.
B. A void function with one parameter (a polynomial p). The function deletes the largest term in p, and this
change should affect the actual argument.

22. This question is relevant if you implemented the polynomial class with a dynamic array. Which of the
polynomial operators needed a nested loop in its implementation?

23. This question is relevant if you implemented the polynomial class with a dynamic array. Draw a picture of
the values in the first five elements of your dynamic array and tell me the value of the current degree after these
statements:
polynomial p(3);
p.assign_coef(4.1, 4);
p.assign_coef(2.1, 2);

24. Suppose that a main function has executed the three statements shown in the previous problem. Give an
example of one statement that can now be executed by the main function, causing the degree of p to drop to 2.

25. This question is relevant if you implemented a polynomial that included some calculus-based operations
such as derivative and integral. Write a new function that meets the following specification:
double slope(const polynomial& p, double x)
// POSTCONDITION: The return value is equal to the slope of the
// polynomial p, evaluated at the point x.

26. What is the one statement that can be used to insert a new node at the head of a linked list. Assume that the
list's head_pointer is called head_ptr and the that the data for the new node is called entry.

27. Suppose that p is a pointer to a node in a linked list, and *p is not the tail node. What are the steps to
removing the node after *p? Use one short English sentence for each step.

28. Suppose we are using the usual node definition (with member functions called data and link). Your program
is using a node* variable called head_ptr to point to the first node of a linked list (or head_ptr == NULL for the
empty list). Write a few lines of C++ code that will print all the double numbers on the list.

29. Suppose we are using the usual node definition (with member functions called data and link), and that
locate_ptr is pointing to a node in a linked list. Write a statement that will make locate_ptr point to the next
node in the list (if there is one). If there is no next node, then your statement should set locate_ptr to NULL.

30. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
size_t count_42s(const node* head_ptr);
// Precondition: head_ptr is the head pointer of a linked list.
// The list might be empty or it might be non-empty.
// Postcondition: The return value is the number of occurrences
// of 42 in the data field of a node on the linked list.
// The list itself is unchanged.

31. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
bool has_42(const node* head_ptr);
// Precondition: head_ptr is the head pointer of a linked list.
// The list might be empty or it might be non-empty.
// Postcondition: The return value is true if the list has at least
// one occurrence of the number 42 in the data part of a node.

32. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
bool all_42s(const node* head_ptr);
// Precondition: head_ptr is the head pointer of a linked list.
// The list might be empty or it might be non-empty.
// Postcondition: The return value is true if every node in the
// list contains 42 in the data part. NOTE: If the list is empty,
// then the function returns true.

33. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link. The data field is an int.)
int sum(const node* head_ptr);
// Precondition: head_ptr is the head pointer of a linked list.
// The list might be empty or it might be non-empty.
// Postcondition: The return value is the sum of all the data components
// of all the nodes. NOTE: If the list is empty, the function returns 0.

34. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link. The data field is an int.)
int product(const node* head_ptr);
// Precondition: head_ptr is the head pointer of a linked list.
// The list might be empty or it might be non-empty.
// Postcondition: The return value is the product of all the data components
// of all the nodes. NOTE: If the list is empty, the function returns 1.

35. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
void list_tail_insert(node* head_ptr, const node::value_type& entry);
// Precondition: head_ptr is the head pointer of a non-empty
// linked list.
// Postcondition: A new node has been added at the tail end
// of the list. The data in the new node is taken from the
// parameter called entry.

36. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
bool data_is_on(const node* head_ptr, const node* p);
// Precondition: head_ptr is the head pointer of a linked list
// (which might be empty, or might be non-empty). The pointer p
// is a non-NULL pointer to some node on some linked list.
// Postcondition: The return value is true if the data in *p
// appears somewhere in a data field of a node in head_ptr's
// linked list. Otherwise the return value is false.
// None of the nodes on any lists are changed.
37. Implement the following function as a new function for the linked list toolkit. (Use the usual node definition
with member variables called data and link.)
bool is_on(const node* head_ptr, const node* p);
// Precondition: head_ptr is the head pointer of a linked list
// (which might be empty, or might be non-empty). The pointer p
// is a non-NULL pointer to some node on some linked list.
// Postcondition: The return value is true if p actually points to
// one of the nodes in the head_ptr's linked list. For example,
// p might point to the head node of this list, or the second node,
// or the third node, and so on. Otherwise the return value is
// false. None of the nodes on any lists are changed.

38. Implement the following function as a new function for the linked list toolkit. Do not call any of the other
toolkit functions from within your implementation. (Use the usual node definition with member variables called
data and link.)
void list_insert_zero(node* previous_ptr);
// Precondition: previous_ptr is a pointer to a node on a linked list.
// Postcondition: A new node has been added to the list after
// the node that previous_ptr points to. The new node contains 0.

39. Suppose that p, q, and r are all pointers to nodes in a linked list with 15 nodes. The pointer p points to the
first node, q points to the 8th node, and r points to the last node. Write a few lines of code that will make a new
copy of the list. You code should set THREE new pointers called x, y, and z so that: x points to the first node of
the copy, y points to the 8th node of the copy, and z points to the last node of the copy. Your code may NOT
contain any loops, but it can use the toolkit functions.

40. Suppose that a_ptr and b_ptr are node pointers. Write one clear sentence to tell me when the expression
(a_ptr==b_ptr) will be true.

41. Compare the worst-case big-O time analysis for these two functions: The insert function for the bag that is
implemented using a fixed-sized array, and the insert function for the bag that is implemented using a linked
list.

42. Compare the worst-case big-O time analysis for these two functions: The erase_one function for the bag that
is implemented using a fixed-sized array, and the erase_one function for the bag that is implemented using a
linked list.

43. Tell about one of the sequence operations that is more efficient because the class keeps a tail pointer as a
private member variable. Provide a specific example showing why the operation would be less efficient without
the tail pointer.

44. Tell about one of the sequence operations that is easier to program because the class keeps a precursor
(rather than just a cursor). Provide a specific example showing why the operation would be harder to program
without the precursor.

45. Compare the worst-case big-O time analysis for these two functions: The insert function for the sequence
that is implemented using a fixed-sized array, and the insert function for the sequence that is implemented using
a linked list.

46. Compare the worst-case big-O time analysis for these two functions: The remove function for the sequence
that is implemented using a fixed-sized array, and the remove function for the sequence that is implemented
using a linked list.
47. Describe a situation where storing items in an array is clearly better than storing items on a linked list.

48. Consider the code below which is supposed to shift the values from data[0] through data[n-1] rightward one
position (so these values now reside in data[1] through data[n]).

size_t i;
for (i = 0; i < n; ++i)
data[i+1] = data[i];
There is a bug in the above code. Write one sentence to describe the bug and write new code that does the job
correctly.

49. Explain what modifications would be needed to make the parenthesis matching algorithm check expressions
with different kinds of parentheses such as (), [] and {}'s.

50. Complete the body of this function. You do not need to check the precondition. You may use the stack
template class.

bool balanced(const char p[ ], size_t n)


// Precondition: p[0]...p[n-1] contains n characters, each of which
// is '(', ')', '{' or '}'.
// Postcondition: The function returns true if the characters form a
// sequence of correctly balanced parentheses with each '(' matching
// a ')' and each '{' matching a '}'. Note that a sequence such as
// ( { ) } is NOT balanced because when we draw lines to match the
// parentheses to their partners, the lines cross each other. On the
// other hand, ( { } ) amd { ( ) } are both balanced.

51. The following code is going to be executed with THREE pushes and ONE pop:

stack<int> s;
s.push(1);
s.push(2);
s.push(3);
s.pop( );
Suppose that s is represented by a partially filled array. Draw the state of the elements of s after the above code:

52. Suppose that s in te above question is represented by a linked list. Draw the state of the private member
variables of s after the above code:

53. Implement the following function. You may use the stack template class and the stack operations of push,
pop, peek, is_empty, and size. You may also use cin.peek( ) and use "cin>>i" to read an integer.

int evaluate_postfix_from_cin( )
// Precondition (Which is not checked): The next input line of cin is a
// properly formed postfix expression consisting of integers,
// the binary operations + and -, and spaces.
// Postcondition: The function has read the next input line (including
// the newline) and returned the value of the postfix expression.
{
int i;
stack<int> s;

54. Complete the body of this function. Use a queue of characters to store the input line as it is being read.

size_t counter( )
// Precondition: There is a line of input waiting to be read from cin.
// Postcondition: A line of input has been read from cin, up to but not
// including the newline character. The return value of the function
// is the number of times that the LAST character of the line appeared
// somewhere in this line.
// EXAMPLE Input: ABBXDXXZX
// The value returned by counter would be 4 for this input since there
// are 4 X's in the input line.
{
size_t answer = 0;
queue q;

55. I am going to execute this code with THREE inserts and ONE get_front:

queue<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.get_front( );
Suppose that s is represented by a circular array. Draw the state of the elemets of s after the above code:

56. I am going to execute this code with THREE insert and ONE get_front:

queue<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.get_front( );
Suppose that s is represented by a linked list. Draw the state of the elemets of s after the above code:

57. Describe why it is a bad idea to implement a linked list version a queue which uses the head of the list as the
rear of the queue.

58. Write a recursive function that has one parameter which is a size_t value called x. The function prints x
asterisks, followed by x exclamation points. Do NOT use any loops. Do NOT use any variables other than x.

59. Implement the following function. Do not use any local variables or loops.
void pattern(unsigned int n)
// Precondition: n > 0;
// Postcondition: The output consists of lines of integers. The first line
// is the number n. The next line is the number 2n. The next line is
// the number 4n, and so on until you reach a number that is larger than
// 4242. This list of numbers is then repeated backward until you get back
// to n.
/* Example output with n = 840:
840
1680
3360
6720
6720
3360
1680
840

60. Write a recursive function with two unsigned int parameters, m and n. The precondition requires 0 <= m and
m <= n. The function prints a line of m asterisks, then a line of m+1 asterisks, and so on up to a line of n
asterisks. Then the same pattern is repeated backward: a line of n asterisks, then n-1, and so on down to n. The
only loop allowed in your implementation is a loop to print a line of m asterisks. You may have two copies of
this loop in different places of the implementation.

61. This question involves a game with teddy bears. The game starts when I give you some bears. You can then
give back some bears, but you must follow these rules (where n is the number of bears that you have):
If n is even, then you may give back exactly n/2 bears.
If n is divisible by 3 or 4, then you may multiply the last two digits of n and give back this many bears. (By the
way, the last digit of n is n%10, and the next-to-last digit is ((n%100)/10).
If n is divisible by 5, then you may give back exactly 42 bears.
The goal of the game is to end up with EXACTLY 42 bears.

For example, suppose that you start with 250 bears. Then you could make these moves:
--Start with 250 bears.
--Since 250 is divisible by 5, you may return 42 of the bears, leaving you with 208 bears.
--Since 208 is even, you may return half of the bears, leaving you with 104 bears.
--Since 104 is even, you may return half of the bears, leaving you with 52 bears.
--Since 52 is divisible by 4, you may multiply the last two digits (resulting in 10) and return these 10 bears. This
leaves you with 42 bears.
--You have reached the goal!

62. Write a recursive function to meet this specification:


bool bears(int n)
// Postcondition: A true return value means that it is possible to win
// the bear game by starting with n bears. A false return value means that
// it is not possible to win the bear game by starting with n bears.
// Examples:
// bear(250) is true (as shown above)
// bear(42) is true
// bear(84) is true
// bear(53) is false
// bear(41) is false
// Hint: To test whether n is even, use the expression ((n % 2) == 0).

63. Write a recursive function with this prototype:


void permute(string first, string second)
// Postcondition: The output consists of lines of Strings. Each String
// is made by writing some rearrangement of first followed by all
// of second.
// For example, if first is "CAT" and second is "MAN", then there will
// be six lines of output:
// CATMAN
// ACTMAN
// TCAMAN
// CTAMAN
// ATCMAN
// TACMAN
Hints: The stopping case occurs when the length of first is zero (in which case the function prints second). Some
string member functions that will help you for a String s:
s[s.length( ) - 1] is a copy of the last character of s.
s.length( ) is the length of s.
cout << s << endl; will print s.
s = c + s; will insert the character c at the front of s.
s += c; will append the character c to the end of s.
s.erase(0, 1); will remove one character from the front of s.
s.erase(s.length( )-1, 1) will remove the last character from s.

64. Write a function with this prototype:


void numbers(ostream& outs, const string& prefix, unsigned int levels);
The function prints output to the ostream outs. The output consists of the string prefix followed by "section
numbers" of the form 1.1., 1.2., 1.3., and so on. The levels argument determines how may levels the section
numbers have. For example, if levels is 2, then the section numbers have the form x.y. If levels is 3, then section
numbers have the form x.y.z. The digits permitted in each level are always '1' through '9'. As an example, if
prefix is the string "THERBLIG" and levels is 2, then the function would print 81 strings. In this case, the
function starts by printing:
THERBLIG1.1.
THERBLIG1.2.
THERBLIG1.3.
and ends by printing:
THERBLIG9.7.
THERBLIG9.8.
THERBLIG9.9.
The stopping case occurs when levels reaches zero (in which case the prefix is printed once by itself followed
by nothing else).

65. Write a function with one positive int parameter called n. The function will write 2^n-1 integers (where ^ is
the exponentiation operation). Here are the patterns of output for various values of n:
n=1: Output is: 1
n=2: Output is: 1 2 1
n=3: Output is: 1 2 1 3 1 2 1
n=4: Output is: 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1
And so on. Note that the output for n always consists of the output for n-1, followed by n itself, followed by a
second copy of the output for n-1.

66. The first step of the maze search algorithm was to step forward and write your name on the ground. What is
the importance of writing your name on the ground?

67. Consider the following function:


bool dead_end()
// Postcondition: The return value is true if the direction directly
// in front is a dead end (i.e., a direction that cannot contain the
// tapestry).
// Library facilities used: useful.h (from Appendix 1).
{
return inquire("Are you facing a wall?")
||
inquire("Is your name written in front of you?");
}
Explain why the function dead_end sometimes asks 2 questions and sometimes asks only 1.

68. What two properties must a variant expression have to guarantee that a recursive function terminates?

Here is a small binary tree:


14
/ \
2 11
/\ /\
1 3 10 30
/ /
7 40
Circle all the leaves. Put a square box around the root. Draw a star around each ancestor of the node that
contains 10. Put a big X through every descendant of the node the contains 10.

69. Draw a full binary tree with at least 6 nodes.

70. Draw a complete binary tree with exactly six nodes. Put a different value in each node. Then draw an array
with six components and show where each of the six node values would be placed in the array (using the usual
array representation of a complete binary tree).

71. Write the structure for a new node definition that could be used for a node in a tree where: (1) Each node
contains int data, (2) Each node has up to four children, and (3) Each node also has a pointer to its parent. Store
the pointers to the children in an array of four pointers.

72. Draw a binary taxonomy tree that can be used for these four animals: Rabbit, Horse, Whale, Snake.

73. Using the binary_tree_node from the above example, write a function to meet the following specification.
Check as much of the precondition as possible. No recursion is needed.
void subswap(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a non-empty binary tree.
// Postcondition: The original left subtree has been moved and is now the right
// subtree, and the original right subtree is now the left subtree.
// Example original tree: Example new tree:
// 1 1
// /\ /\
// 2 3 3 2
// /\ /\
// 4 5 4 5

74. Here is a small binary tree:


14
/ \
2 11
/\ /\
1 3 10 30
/ /
7 40
Write the order of the nodes visited in:
A. An in-order traversal:
B. A pre-order traversal:
C. A post-order traversal:

75. Using the binary_tree_node from above example, Write a recursive function to meet the following
specification. You do not need to check the precondition.
void increase(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree.
// Postcondition: Every node of the tree has had its data increased by one.

76. Using the binary_tree_node from the above example, write a recursive function to meet the following
specification. You do not need to check the precondition.
size_t many_nodes(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree.
// Postcondition: The return value is the number of nodes in the tree.
// NOTES: The empty tree has 0 nodes, and a tree with just a root has
// 1 node.

77. Using the binary_tree_node from the above example, write a recursive function to meet the following
specification. You do not need to check the precondition.
int tree_depth(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree.
// Postcondition: The return value is the depth of the binary tree.
// NOTES: The empty tree has a depth of -1 and a tree with just a root
// has a depth of 0.

78. Using the binary_tree_node from the above example, write a function to meet the following specification.
You do not need to check the precondition.
size_t count42(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree (but
// NOT NECESSARILY a search tree).
// Postcondition: The return value indicates how many times 42 appears
// in the tree. NOTE: If the tree is empty, the function returns zero.

79. Using the binary_tree_node from the above example, write a function to meet the following specification.
You do not need to check the precondition.
bool has_42(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree (but
// NOT NECESSARILY a search tree).
// Postcondition: The return value indicates whether 42 appears somewhere
// in the tree. NOTE: If the tree is empty, the function returns false.

80. Using the binary_tree_node from the above example, write a function to meet the following specification.
You do not need to check the precondition.
bool all_42(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree (but
// NOT NECESSARILY a search tree).
// Postcondition: The return value is true if every node in the tree
// contains 42. NOTE: If the tree is empty, the function returns true.

81. Using the binary_tree_node from the above example, write a recursive function to meet the following
specification. You do not need to check the precondition.
int sum_all(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary tree.
// Postcondition: The return value is the sum of all the data in all the nodes.
// NOTES: The return value for the empty tree is zero.

82. Suppose that we want to create a binary search tree where each node contains information of some data type
called Item (which has a default constructor and a correct value semantics). What additional factor is required
for the Item data type?

83. Suppose that a binary search tree contains the number 42 at a node with two children. Write two or three
clear sentences to describe the process required to delete the 42 from the tree.

84. Using the binary_tree_node from page 465, write a function to meet the following specification. You do not
need to check the precondition. Make the function as efficient as possible (do not visit nodes unnecessarily):
size_t count42(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a binary SEARCH tree.
// Postcondition: The return value indicates how many times 42 appears
// in the tree.

85. Using the binary_tree_node from the above example, write a function to meet the following specification.
You do not need to check the precondition. Make the function as efficient as possible (do not visit nodes
unnecessarily):
int max(binary_tree_node<Item>* root_ptr)
// Precondition: root_ptr is the root pointer of a nonempty binary SEARCH
// tree.
// Postcondition: The return value is the largest value in the tree.

86. Using the binary_tree_node from the above example, write a function to meet the following specification.
You do not need to check the precondition.
void insert_one_42(binary_tree_node<Item>*& root_ptr)
// Precondition: root_ptr is the root pointer of a binary SEARCH tree.
// Postcondition: One copy of the number 42 has been added to the binary
// search tree.

87. Suppose that we want to create a heap where each node contains information of some data type called Item
(which has a default constructor and a correct value semantics). What additional factor is required for the Item
data type?

88. A heap is a binary tree where the entries can be compared using the usual six comparison operations (that
form a total order semantics). Write the two rules that the binary tree must follow in order for the structure to
actually be a heap.

89. Give two different reasons to explain why the following binary tree is not a heap:
91
/ \
77 46
/ \ \
68 81 11
90. Draw a new heap that is created by inserting 82 into the following heap:
91
/ \
77 66
/ \ / \
68 1 3 11

91. Draw a new heap that is created by removing one item from the following heap:
91
/ \
77 66
/ \ / \
68 1 3 11

92. Suppose that you are performing a reheapification downward. Write a precise condition that describes the
situation that causes the reheapification to stop.

93. Suppose that you are performing a reheapification upward. Write a precise condition that describes the
situation that causes the reheapification to stop.

94. Suppose that a non-leaf node in a B-tree contains 42 entries. How many children does the node have?

95. Draw an example of a B-tree with four nodes and seven integer entries. The value of MINIMUM is 1 for
this tree.

96. Draw a new B-tree that is created by inserting 82 into the following B-tree. For this example, the minimum
number of items in each node is 1. Note that the rightmost leaf starts with two entries, 71 and 93.
56
/ \
7 66
/ \ / \
2 8 63 71 and 93

97. Draw a new B-tree that is created by deleting 63 from the following B-tree. For this example, the minimum
number of items in each node is 1. Note that the rightmost leaf starts with two entries, 71 and 93.
56
/ \
7 66
/ \ / \
2 8 63 71 and 93

98. Suppose that a B-tree is declared so that MAXIMUM (the maximum number of items in a node) is 84. What
is the value of MINIMUM (the minimum number of items in a non-root node)?
Suppose that a B-tree is declared so that MAXIMUM (the maximum number of items in a node) is 84. Write
one clear sentence to describe why each node's data array is set up to hold up to 85 items (one more than
MAXIMUM).

99. Suppose that a and b are two positive integers and n is some non-negative number. Write an equation to
show the relationship between log base a of n and log base b of n. Give a derivation to show that the
relationship is valid.
100. Here is an array with exactly 15 elements:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Suppose that we are doing a serial search for an element. Circle any elements that will be found by examining
two or fewer numbers from the array.

101. Here is an array with exactly 15 elements:


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Suppose that we are doing a binary search for an element. Circle any elements that will be found by examining
two or fewer numbers from the array.

102. Implement the body of the following function using a binary search of the array. You do not need to check
the precondition. All your local variables must be size_t variables.
bool has_42(const int data[ ], size_t n)
// Precondition: The elements data[0]...data[n-1] are sorted from smallest
// to largest. The value of n might be zero (indicating an empty
// array).
// Postcondition: A true return value indicates that the number 42 appears in
// data[0]...data[n-1]. A false return value indicates that 42 doesn’t appear.

103. Draw a hash table with open addressing and a size of 9. Use the hash function "k%9". Insert the keys: 5,
29, 20, 0, 27 and 18 into your table (in that order).

104. Suppose you are building an open address hash table with double hashing. The hash table capacity is n, so
that the valid hash table indexes range from 0 to n. Fill in the blanks:
In order to ensure that every array position is examined, the value returned by the second hash function must be
________________________ with respect to n.

105. One way to ensure this good behavior is to make n be _______________, and have the return value of the
second hash function range from _________ to _________ (including the end points).

106. Draw a hash table with chaining and a size of 9. Use the hash function "k%9" to insert the keys 5, 29, 20,
0, and 18 into your table.

107. Suppose that I have the following record_type definition for a record in a hash table:
struct record_type
{
int key;
... other stuff may also appear here ...
};
The hash table uses open addressing with linear probing. The table size is a global constant called CAPACITY.
Locations of the table that have NEVER been used will contain the key -1. Locations of the table that were once
used but are now vacant will contain the key -2. All valid keys will be non-negative, and the hash function is:
size_t hash(int key)
{
return (key % CAPACITY);
}
Complete the implementation of the following function. There is no need to check the precondition, but your
code must be as efficient as possible.
bool key_occurs(const record_type data[ ], int search_key)
// Precondition: data[0]...data[CAPACITY-1] is an open address hash table
// as described above.
// Postcondition: If search_key occurs as a key of a record in the table, then
// the function returns true; otherwise the function returns false.

108. Suppose that an open-address hash table has a capacity of 81 and it contains 81 elements. What is the
table's load factor? (An appoximation is fine.)

109. I plan to put 1000 items in a hash table, and I want the average number of accesses in a successful search
to be about 2.0

A. About how big should the array be if I use open addressing with linear probing? NOTE: For a load factor of
A, the average number of accesses is generally ½(1+1/(1-A)).

B. About how big should the array be if I use chained hashing? NOTE: For a load factor of A, the average
number of accesses is generally (1+A/2).

110. Here is an array of ten integers:


5 3 8 9 1 7 0 2 6 4
Draw this array after the FIRST iteration of the large loop in a selection sort (sorting from smallest to largest).

111. Here is an array of ten integers:


5 3 8 9 1 7 0 2 6 4
Draw this array after the FIRST iteration of the large loop in an insertion sort (sorting from smallest to largest).
This iteration has shifted at least one item in the array!

112. Suppose that you are writing a program that has the usual selectionsort function available:
void selectionsort(int data[ ], size_t n);
Your program also has an integer array called x, with 10 elements. Write two function calls: The first call uses
selectionsort to sort all of x; the second call uses selectionsort to sort x[3]..x[9].

113. Describe a case where quicksort will result in quadratic behavior.

114. Here is an array which has just been partitioned by the first step of quicksort:
3, 0, 2, 4, 5, 8, 7, 6, 9
Which of these elements could be the pivot? (There may be more than one possibility!)

115. Give a concise accurate description of a good way for quicksort to choose a pivot element. Your approach
should be better than "use the entry at location [0]".

116. Give a concise accurate description of a good way for quicksort to improve its performance by using
insertionsort.

117. Here is an array of ten integers: 5 3 8 9 1 7 0 2 6 4


Suppose we partition this array using quicksort's partition function and using 5 for the pivot. Draw the resulting
array after the partition finishes.

118. Here is an array of ten integers: 5 3 8 9 1 7 0 2 6 4


Draw this array after the TWO recursive calls of merge sort are completed, and before the final merge step has
occured.

119. Write two or three clear sentences to describe how a heapsort works.

120. Suppose that you are writing a program that has these two functions available:
int compare_ints(const void* p1, const void* p2);
// Precondition: p1 and p2 are really pointers to integers.
// Postcondition: The return value is:
// (a) negative if *p1 < *p2
// (b) zero if *p1 == *p2
// (c) positive if *p1 > *p2
void qsort(
void* base,
size_t n,
size_t bytes,
int compar(const void*, const void*)
);
// Same specification as the standard library qsort function.
Your program also has an integer array called x, with 10 elements. Write two function calls: The first call uses
qsort to sort all of x; the second call uses qsort to sort x[3]..x[9].

121. Suppose that you implement quicksort nonrecursively using a stack, as in your last programming
assignment. You use your algorithm to sort an array of 100 items, and at the start of the final iteration of the
while loop, the stack contains just two numbers: 10 (on top) and 90 (on bottom). Write one or two clear
sentences to describe which parts of the array are sorted at this point, and which parts of the array remain to be
sorted.

122. Draw a directed graph with five vertices and seven edges. Exactly one of the edges should be a loop, and
do not have any multiple edges.

123. Draw an undirected graph with five edges and four vertices. The vertices should be called v1, v2, v3 and
v4--and there must be a path of length three from v1 to v4. Draw a squiggly line along this path from v1 to v4.

124. Draw the directed graph that corresponds to this adjacency matrix:
0 1 2 3
0 | true false true false |
1 | true false false false |
2 | false false false true |
3 | true false true false |

125. Draw the edge lists that correspond to the graph from the previous question.

126. What are the benefits of using an external iterator as opposed to an internal iterator?

127. How may Djikstra's algorithm be modified to determine if it is possible to get from a given vertex to all
other vertices in the graph?

128. In Djikstra's shortest path algorithm, what technique is used to choose the next vertex to process?

You might also like