DS Book
DS Book
DS Book
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
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
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
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.
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).
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.
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.
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.
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:
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.
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.
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. 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
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.
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
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
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
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.
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.
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.
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.
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
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) 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.
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
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 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
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)
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.
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)
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).
c1*g(n)
f(n)
c2*g(n)
Time
n0
n (Problem Size)
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 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).
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.
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.
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.
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., .
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).
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))
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.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:
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.
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.
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
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
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
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
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)
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
4. Define Algorithm and design an algorithm to find out the total number of even and odd number in a
group of 50 numbers.
6. What are the differences between bottom up and top down approach of programming?
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.).
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
l1 l1+1 …. i1 …. u1
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
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).
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
Example:
Consider an array arrData[] ={10,30,40,50};
Index 0 1 2 3
Element 10 30 40 50
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
A= 9 2 B= 8 4
6 5 1 7
C= 9+8 2+4 = 17 6
6+1 5+7 7 12
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
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
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]
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
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.
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.
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
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
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;
----------------------
};
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;
(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);
Solutions:
1. d 2. a 3. b 4. a 5. c 6. c 7. d 8. b 9. a 10. b
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 Σ.
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.
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.
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}
f-1(L) = { s | f(s) Є L}
for any language L. Simple single-letter substitution ciphers are examples of string homomorphisms.
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:
= π∑ (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}
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
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.
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.
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
π∑ (L) ÷ a = π∑ (L) ( s ÷ a )
The prefixes of a string is the set of all prefixes to a string, with respect to a given language:
A language is called prefix closed if Pref (s) = L. Clearly, the prefix closure operator is idempotent:
The prefix relation is a binary relation s t such that if and only if s Є Pref L(t).
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.
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.
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.
The various String algorithms can be classified by the number of patterns each uses.
Let m be the length of the pattern and let n be the length of the searchable text.
average Θ(n+m),
Rabin-Karp string search algorithm Θ(m)
worst Θ(n m)
The Boyer–Moore string search algorithm has been the standard benchmark for the practical string search
literature.
Naturally, the patterns can not be enumerated in this case. They are represented usually by a regular grammar or
regular expression.
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:
copies the string s2 into the character array s1. The base address of s1 is returned.
copies at most n characters of the string s2 into the character array s1. The base address of s1 is
returned.
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.
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.
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.
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.
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.
returns the length of the longest substring of s1 that begins at the start of s1and consists only of the
characters found in 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.
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.
returns a pointer to the first instance of string s2 in s1. Returns a NULL pointer if s2 is not encountered
in s1.
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.
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.
1. Bubble Sort
2. Insertion Sort
3. Selection Sort
4. Merge Sort
5. Quick Sort
6. Heap Sort
7. Radix Sort
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.
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.
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.
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
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
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
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
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.
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
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.
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.
1 1 15 top 1 15
0 25 top 0 25 0 25 top
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
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;
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 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
Example 1: Convert the following infix expression to its corresponding postfix notation:
I = A+B*C/(D+E)
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
+ 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
( top
/
Oprtstk
( 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
/ 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 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
(
top
Oprtstk
( top
Oprtstk
+ top
(
Oprtstk
+ 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
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
/ 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
+ 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
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.
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.
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
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.
//Prototype Declaration
void fnPush( int[], int );
int fnPop(int[]);
void fnDisplay(int[]);
int fnFull();
int fnEmpty();
int top=-1;
//Prototype Declaration
void fnPush(int);
int fnPop();
void fnDisplay();
int fnEmpty();
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;
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];
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
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
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.
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
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
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
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
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.
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.
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
Thus here the first inserted item is deleted first. So queue is called a FIFO (First In First Out) list.
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
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 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
fnQInsertion(10)
ptrNewNode
ptrNewNode->iData = 10;
10
ptrNewNode
ptrNewNode->ptrNext = NULL
10 NULL
ptrNewNode
front = rear
fnQDelete()
10 NULL
front
iData = front->iData = 10
ptrDeleteNode = front
10 NULL
ptrDeleteNode = front
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.
4 .
.
2 n-1
1 0
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.
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
//Prototype Declaration
void fnQInsertion(int[], int);
int fnQDeletion(int[]);
void fnQDisplay (int []);
int fnQFull();
int fnQEmpty();
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;
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();
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();
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);
}
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
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
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
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?
6. How can you implement a queue using stack? write a C program to implement that.
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.
12 B 15 C 18 D 20 NULL
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.
10 20 40 NULL
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).
ptrStart
Suppose we want to find the address of the third node in the list. So iPosition = 3.
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).
10 20 40 NULL
ptrTemp1
ptrNewNode
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.
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
30
ptrNewNode
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
10 20 30 NULL
ptrStart=ptrNewNode
20 30 40 NULL
ptrNodeP ptrNodeQ
NULL
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.
ptrStart
Node A Node P
10 P 20 NULL
ptrStart ptrNodeP
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
10 20 30 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
Step 4:
Check the condition: ptrTemp3 ≠ NULL
ptrTemp1 = ptrTemp2;
ptrTemp2 = ptrTemp3;
ptrTemp3 = ptrTemp3->ptrNext;
ptrTemp2->ptrNext = ptrTemp1;
ptrTemp3 = NULL
10 NULL 20 30
Step 5:
Now set ptrStart to ptrTemp2 as currently ptrTemp2 is the first node.
ptrStart = ptrTemp2;
10 NULL 20 30
ptrStart
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
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
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
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.
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
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 C
B 25
ptrNodeC
B 25 D
ptrNodeC
B 25 D
ptrNodeC
B 25 D
ptrNodeC
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.
ptrNodeP
25
ptrNodeP
NULL 25
ptrNodeP
Node P
NULL 25 A
ptrNodeP
Node P
NULL 25 A
ptrNodeP
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;
Node P
D 25 NULL
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
(1)
(2)
ptrNodeB->ptrRight->ptrLeft = ptrNodeB->ptrleft
(1)
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
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
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);
//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
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);
/* 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;
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;
}
}
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
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
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
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
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
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.
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
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.
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
1. What is a single Linked List? What are the advantages and disadvantages of single Linked List?
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
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?
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
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..
fn1() fn1()
{ {
−−−−−− ; −−−−−− ;
fn1(); −−−−−− ;
} fn2();
}
fn2()
{
fn1();
−−−−−− ;
}
Depending on the number of recursive calls recursion can be further categorized as:
(a) Linear recursion (b) Binary recursion (c) Multiple recursion
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
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
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)
6
Figure 9.2: A recursion tree to compute the value of 2 .
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)
[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
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
A B C A B C
A B C A B 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)
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.
call return 6
fnFact1(2,3)
call return 6
fnFact1(6,1)
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);
}
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.
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
Solutions:
1. a 2. b 3. a 4. d 5. c 6. c 7. d 8. a 9. b 10. a
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.
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
(d)
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
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
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
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.
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
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
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.
B C
D E F G
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.
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
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
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
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
- +
+ C A -
A B B C
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
B G
A C F H
D I J
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
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.
F F
M P
Reheap
F P F M
P P
F M F M
B B
P P
Reheap
F M I M
B I B F
P Q
Reheap
I M I P
B F Q B F M
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
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
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
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
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
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
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
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
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
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
+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
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
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.
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
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
G M
N A D N N J N L N N X N Z N
N B N C N
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 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
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
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
D K
C E J M
N N N N N N N N
//Prototype Declaration
void fnRecursivePreorder (TreeNode *);
void fnRecursiveInorder(TreeNode *);
void fnRecursivePostorder(TreeNode *);
TreeNode *fnInsert_BST( TreeNode *, int);
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 * ) ;
if ( !ptrRoot )
{
ptrRoot = ( AVLNode * ) malloc ( sizeof ( AVLNode ) ) ;
ptrRoot -> iData = iData ;
ptrRoot -> ptrLeft = NULL ;
ptrRoot -> ptrRight = NULL ;
ptrRoot -> iBalanceFactor = 0 ;
*h = TRUE ;
return ( ptrRoot ) ;
}
case 0:
ptrRoot -> iBalanceFactor = 1 ;
break ;
case -1:
ptrRoot -> iBalanceFactor = 0 ;
*h = FALSE ;
}
}
}
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 ( !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 *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 ) ;
}
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 ;
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 ;
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
Questions No. 2 and No. 3 are based on the following tree structure.
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
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
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
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
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
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
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
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
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
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.
(a) complete and heap (b) full (and complete) (c) full (d) heap.
(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
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.
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.
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.
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
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.
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
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.
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
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
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
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
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
1
5
4
2 3 4 3
1
5
4
2 3 4
1
5
2 3 4
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.
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
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
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
STEP 5:
Delete node 3 from stack without any insertion into stack.
2 3 4
2
5
STEP 6:
Delete node 2 from stack.
2 3 4
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
5 2 4 16
7
3 4 5 6
4 9 5
3 6 3 4 8 2
7 8
5
STEP 2:
dist[3] is smallest among the temporary nodes.
So, now current = 3 and make it permanent.
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.
STEP 3:
dist[7] is smallest among the temporary nodes.
So, now current = vertex 7 and make it permanent.
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.
STEP 4:
dist[4] is smallest among the temporary nodes.
So, now current = vertex 4 and make it permanent.
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.
STEP 5:
dist[2] is smallest among the temporary nodes.
So, now current = vertex 2 and make it permanent.
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.
STEP 6:
dist[5] is smallest among the temporary nodes.
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.
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]}
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 ∞
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:
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();
}
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.
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.
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.
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
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.
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
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
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
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
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
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.
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.
Sorting
Comparison-based Address-based
Sorting Sorting
Proxmap Radix
Sort Sort
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.
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.
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.
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,
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.
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)
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)
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
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]
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.
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
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 -
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-
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
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
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
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
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
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
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
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
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
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
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
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
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).
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 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 fnHeap_sort(int[],int);
void fnBuild_Max_Heap(int[],int);
void fnMax_heapify(int[],int,int);
void interchange(int[],int,int);
int left(int i)
{
return(2*i);
}
int right(int i)
{
return(2*i+1);
}
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();
}
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)
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
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)
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
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
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
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
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
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
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.
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.
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.
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)
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
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]
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.
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
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
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
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)
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)
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
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.
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
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
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
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
key value 0
49 3 1
123 6
2
68 4
3 49
90 9
4 68
6 123
9 90
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.
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.
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.
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.
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.
key value 0 49 N
49 0 1
123 5
2
68 5
3 87 N
87 3
4
5 123 68 N
void main(void)
{
clrscr();
printf("Division Method = %d",fnDivision(20,9));
printf("Mid Square of %d = %d",99*99,fnMidSquare(99,2,2));
getch();
}
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
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
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
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.
The file ClassGrade is a sequential file where each record is ordered on Roll No.
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.
Secondly, in ‘C’ language “File Pointer” is very essential to keep track of positions of records as bellow:
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
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.
#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);
}
#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);
}
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.
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
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.
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?
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?
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.
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.
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!
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?
68. What two properties must a variant expression have to guarantee that a recursive function terminates?
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
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.
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).
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].
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.
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?