Algorithm and Data Structure Using C
Algorithm and Data Structure Using C
OPTION: IT
CREDITS: 12
January, 2026
~1~
Assessment Methods
4. Final theoretical Exam and Final Practical Exam. (20 marks and 30 marks).
~2~
Table of Contents
LEARNING UNIT 1-USE ALGORITHM TO SOLVE PROBLEMS........................................................8
Learning Outcome 1.1 Describe algorithm..............................................................................................8
1.1.1 Introduction to Algorithm.............................................................................................................8
1.1.2 Key concepts of algorithm in programming...............................................................................12
Learning Outcome 1.2 Design algorithm to solve a problem.................................................................14
1.2.1 Computer Systems and Sub-systems..........................................................................................14
1.2.2 Top-down design........................................................................................................................15
1.2.3 Algorithm purpose......................................................................................................................17
1.2.4 Test Data.....................................................................................................................................18
1.2.5 Trace tables.................................................................................................................................19
1.2.6 Validation and Verification.........................................................................................................21
1.2.7 Producing an Algorithm..............................................................................................................22
Learning Outcome 1.3 Select Algorithm type to solve a problem.........................................................22
Learning Outcome 1.4 Use Simple recursive algorithms type...............................................................23
1.4.1 The Three Laws of Recursion.....................................................................................................23
1.4.2 Examples on recursive algorithms..............................................................................................24
Learning Outcome 1.5 Use Backtracking Algorithms Type....................................................................26
1.5.1 Description of backtracking algorithm.......................................................................................26
1.5.2 Non-recursive backtracking, using a stack.................................................................................28
1.5.3 Keeping backtracking simple......................................................................................................30
1.5.4 Example of Application of Backtracking with constraint satisfaction.......................................31
Learning Outcome 1.6 Use Divide and conquer algorithms Type..........................................................34
1.6.1 Introduction to Divide and Conquer...........................................................................................34
1.6.2 Fundamentals of Divide and Conquer........................................................................................35
1.6.3 General algorithm for Divide and Conquer................................................................................35
1.6.4 Recurrence Relation for DAC algorithm....................................................................................36
1.6.5 Applications of Divide and Conquer..........................................................................................36
Learning Outcome 1.7 Use Dynamic programming algorithms type.....................................................53
1.7.1 Characteristics of Dynamic Programming..................................................................................54
1.7.2 Dynamic Programming Methods................................................................................................55
1.7.3 Solving a dynamic programming problem.................................................................................58
~3~
Learning Outcome 1.8 Use Greedy Algorithms Type.............................................................................63
1.8.1 Description of greedy algorithm.................................................................................................64
1.8.2 Use of greedy approach for solving the activity selection problem............................................69
1.8.3 Greedy method vs Dynamic Programming.................................................................................73
Learning Outcome 1.9 Use Branch and bound algorithms Type............................................................73
1.9.1 Branch and bound algorithm introduction..................................................................................73
Learning Outcome 1.10 Use Brute force algorithms type......................................................................76
1.10.1. Introduction to brute force algorithm.......................................................................................76
1.10.2 Implementing the brute force search.........................................................................................78
1.10.3 Combinatorial explosion...........................................................................................................79
1.10.4 Speeding up brute-force searches.............................................................................................83
1.10.5 Reordering the search space......................................................................................................83
1.10.6 Alternatives to brute-force search.............................................................................................84
Learning Outcome 1.11 Use Randomized Algorithms Type...................................................................84
1.11.1 introduction to randomized algorithms.....................................................................................84
1.11.2 Computational complexity........................................................................................................85
1.11.3 Derandomization.......................................................................................................................85
Learning Outcome 1.12: Write algorithm pseudocode..........................................................................85
1.12.1 Description of algorithm pseudocode.......................................................................................86
1.12.2 Difference Between Algorithm and Pseudocode......................................................................86
1.12.3 Use of pseudocode syntax.........................................................................................................88
Learning Outcome 1.13: Write algorithm flowchart............................................................................104
1.13.1 Description of algorithm flowchart.........................................................................................104
1.13.2 Difference Between Algorithm and Flowchart.......................................................................106
1.13.3 Examples of flowchart............................................................................................................107
Learning outcome 1.14 Apply Algorithm complexity to measure its speed in execution....................110
LEARNING UNIT 2: APPLY C PROGRAMMING FUNDAMENTALS..............................................................113
Learning Outcome 2.1. Setup the environment of C programming.....................................................113
2.1.1 Introduction to C.......................................................................................................................113
2.1.2 Description of the environment of C programming..................................................................117
2.1.3 Steps to install tool to compile and execute C programs..........................................................118
2.1.4 Some commonly used shortcut keys of Dev-C ++...................................................................121
2.1.5 Some C compilers for UNIX/LINUX Operating System.........................................................122
Learning Outcome 2.2 Use C Syntax....................................................................................................122
~4~
2.2.1 Structure of a C program...........................................................................................................122
2.2.2 Programming rules....................................................................................................................125
2.2.3 The ‘C’ Character Set...............................................................................................................126
2.2.4 Delimiters..................................................................................................................................127
2.2.5 The ‘C’ keywords.....................................................................................................................128
2.2.6 Identifiers..................................................................................................................................128
2.2.7 Input and Output in c................................................................................................................129
Learning Outcome 2.3: Describe Data Types.......................................................................................130
2.3.1 Overview of Data types............................................................................................................131
2.3.2. Description of 4 Data types in C..............................................................................................131
2.3.3 Data type modifiers in C...........................................................................................................133
2.3.4. Entire Data Types in C............................................................................................................135
2.3.5 Examples of Application of data types.....................................................................................136
Learning Outcome 2.4: Use variables..................................................................................................137
2.4.1 Description of variables............................................................................................................137
2.4.2 Key Differences between Identifier and Variable....................................................................139
2.4.3 Types of Variable......................................................................................................................139
Learning Outcome 2.5: Use Constants.................................................................................................141
2.5.1 Description of constants in C....................................................................................................141
2.5.2 Creation and Use of Constants in C..........................................................................................143
Learning Outcome 2.6: Use operators.................................................................................................144
2.6.1. Description and application of operators in C.........................................................................145
2.6.2 Priority of Operations and their Clubbing................................................................................155
Learning Outcome 2.7: Use conditions................................................................................................157
2.7.1 Description of Decision control statements in C......................................................................158
2.7.2 Application of Decision Control statements.............................................................................158
Learning Outcome 2.8: Use Loops.......................................................................................................165
2.8.1 Description of loops in C programming...................................................................................165
2.8.2 Application of loop control statements.....................................................................................165
Learning Outcome 2.9: Use arrays.......................................................................................................175
2.9.1 Description of arrays in C programming..................................................................................175
2.9.2 Single Dimensional Array.........................................................................................................176
2.9.3 Multi Dimensional Array..........................................................................................................178
Learning Outcome 2.10: Use functions in C Programming..................................................................181
~5~
2.10.1 Description of functions..........................................................................................................181
2.10.2 Using of functions syntaxes....................................................................................................187
2.10.3 Category of functions..............................................................................................................189
2.10.5 Call by value and call by reference.........................................................................................193
2.10.6 Storage class............................................................................................................................197
Learning Outcome 2.11: Manage memory using pointers...................................................................204
2.11.1 Memory management in C programming introduction..........................................................204
2.11.2 Description of pointers in C....................................................................................................204
2.11.3 Using Operators that are used with Pointers...........................................................................205
2.11.4 Application of pointers in C....................................................................................................210
2.11.5 Use of Static Memory Allocation..........................................................................................217
2.11.6 Use of Dynamic Memory Allocation......................................................................................219
Learning Outcome 2.12: Use structure................................................................................................239
2.12.1 Description of structure in C...................................................................................................239
2.12.2 Use of Array Structures in C...................................................................................................242
2.12.3 Nested Structure in C..............................................................................................................245
2.12.4 Passing Structure to Function.................................................................................................250
2.12.5 Pointers to Structures in C......................................................................................................251
2.12.6 typedef in C.............................................................................................................................254
Learning Outcome 2.13: Use file I/O in C.............................................................................................258
2.13.1 Description of file I/O.............................................................................................................258
2.13.2 Steps for File Operations........................................................................................................259
LEARNING UNIT 3: APPLY DATA STRUCTURE USING C.............................................................................265
Learning outcome 3.1 Use Data Structure Type to use Solving a Problem..........................................265
3.1.1 Description of Data Structures using C....................................................................................265
3.1.2 Searching...................................................................................................................................269
3.1.3 List of Data Structures using C.................................................................................................283
Learning output 3.2 Use Array Data Structure Using C Programming Techniques..............................283
3.2.1 Description of array data structure............................................................................................283
3.2.2 Application of array structure using C......................................................................................284
3.2.3 Using arrays data structure operations......................................................................................284
Learning outcome 3.3 Use Linked List Data Structure Using C Programming Techniques...................285
3.3.1 Description of use of stack data structure using C....................................................................285
3.3.2 Applications of stack.................................................................................................................286
~6~
3.3.3 Types of Linked List.................................................................................................................286
3.3.4 Basic Operations on Linked List...............................................................................................290
3.3.5 Demonstration of Linked List in a C program..........................................................................295
Learning outcome 3.4. Use Stack Data Structure Using C Programming Techniques..........................304
3.4.1 Description of use of stack data structure using C....................................................................304
3.4.2 Applications of stack................................................................................................................306
3.4.3 Operations on Stack..................................................................................................................306
3.4.4 Demonstration of Stack – using Array......................................................................................306
3.4.5 Demonstration of Stack – using LinkedList.............................................................................310
Learning outcome 3.5 Use Queue Data Structure Using C Programming Techniques.........................316
3.5.1 Description of queue data structure using C.............................................................................316
3.5.2 Operations on Queue.................................................................................................................319
3.5.4 Demonstration of Queue- using LinkedList.............................................................................325
Learning Outcome 3.6 Use Trees Data Structure Using C Programming Techniques..........................332
3.6.1 Description of tree data structure..............................................................................................332
3.6.2 General tree...............................................................................................................................333
3.6.3 Important Terms........................................................................................................................335
3.6.4 Applications of trees.................................................................................................................336
3.6.5 Binary Tree...............................................................................................................................336
3.6.6 Binary Search Tree...................................................................................................................350
3.6.7 AVL Tree..................................................................................................................................357
3.6.8 Heap..........................................................................................................................................363
Learning outcome 3.7 Use Hashing Data Structure Using C Programming Techniques.......................367
3.7.1 Description of hashing data structure.......................................................................................367
3.7.2 Implementation.........................................................................................................................367
Learning outcome 3.8 Use Graph Data Structure Using C Programming Techniques..........................374
3.8.1 Introduction to graph in data structure......................................................................................374
3.8.2 Graph Terminology...................................................................................................................375
3.8.3 Graph to a Tree.........................................................................................................................377
3.8.4 Graph Traversal........................................................................................................................378
Exercises..............................................................................................................................................386
References:..............................................................................................................................................398
~7~
~8~
LEARNING UNIT 1-USE ALGORITHM TO SOLVE PROBLEMS
Learning Outcomes:
1.1 Describe algorithm.
1.2 Design Algorithm to solve a problem
1.3 Select Algorithm type to solve a problem
1.4 Use simple recursive algorithms type
1.5 Use Backtracking algorithms type
1.6 Use Divide and conquer algorithms type
1.7 Use Dynamic programming algorithms type
1.8 Use Greedy algorithms type
1.9 Use Branch and bound algorithms type
1.10 Use Brute force algorithms type
1.11 Use Randomized algorithms type
1.12 Write Algorithm pseudocode
1.13 Write Algorithm flowchart
1.14 Apply Algorithm complexity to measure its speed
in execution
~9~
Algorithms are generally created independent of underlying languages, i.e. an algorithm can be
implemented in more than one programming language.
b. Characteristics of an Algorithm
1. Finiteness: An algorithm should have finite number of steps and it should end after a
finite time.
2. Input: An algorithm may have many inputs or no inputs at all.
3. Output: It should result at least one output.
4. Definiteness: Each step must be clear, well-defined and precise. There should be no any
ambiguity.
5. Effectiveness: Each step must be simple and should take a finite amount of time.
• Aid in the management and handling of huge amounts of data, even if there are several large
databases involved.
• Simplify applicability of program.
• Knowledge of algorithms, in combination with data structures, enables a programmer to
write the proper codes quickly and efficiently.
• Choosing the right algorithms (and data structure) will ensure the program runs fast while
also maintaining integrity and reliability of data.
d. Expressing an algorithm
~ 10 ~
of a particular implementation language. Programming languages are primarily intended for
expressing algorithms in a form that can be executed by a computer, but are often used as a way
to define or document algorithms.
e. Writing algorithm
There are no well-defined standards for writing algorithms. Rather, it is problem and resource
dependent. Algorithms are never written to support a particular programming code.
As we know that all programming languages share basic code constructs like loops (do, for,
while), flow-control (if-else), etc. These common constructs can be used to write an algorithm.
We write algorithms in a step-by-step manner, but it is not always the case. Algorithm writing is
a process and is executed after the problem domain is well-defined. That is, we should know the
problem domain, for which we are designing a solution.
Example:
Problem − Design an algorithm to add two numbers and display the result.
Step 1 − START
Step 2 − declare three integers a, b & c
Step 3 − define values of a & b
Step 4 − add values of a & b
Step 5 − store output of step 4 to c
Step 6 − print c
Step 7 − STOP
~ 11 ~
Algorithms tell the programmers how to code the program. Alternatively, the algorithm can be
written as −
In design and analysis of algorithms, usually the second method is used to describe an
algorithm. It makes it easy for the analyst to analyze the algorithm ignoring all unwanted
definitions. He can observe what operations are being used and how the process is flowing.
We design an algorithm to get a solution of a given problem. A problem can be solved in more
than one ways.
Hence, many solution algorithms can be derived for a given problem. The next step is to
analyze those proposed solution algorithms and implement the best suitable solution.
f. Variables
~ 12 ~
Variables are symbolic names given to data where the value of the data stored may
change during the execution of the program. In effect, a variable is a named area of memory
used to store data.
In a program every variable has:
Name(identifier)
Data type (a set of values and a set of operations on those values).
Size
Address (memory location)
Value
a. Source code: A computer program's source code is the collection of files needed to
convert from human-readable form to some kind of computer-executable form.
b. Machine code: Sometimes referred to as machine language or object code, machine
code is a collection of binary digits or bits that the computer reads and interprets.
Machine language is the only language a computer is capable of understanding.
c. Editing: The first step in developing an application in programming is to write/edit
the source code. The source code contains all the instructions which needs to be executed
by the machine in a text format (understandable by humans).
~ 13 ~
d. Compiling vs interpreting: To convert source code into machine code, we use either
a compiler or an interpreter.
It will search all the object files and replace all the undefined references with the referenced
machine code within the library files. At the end of the linking process we are going to have
an executable file.
Interpreter Compiler
Translates program one statement Scans the entire program and translates it as
at a time. a whole into machine code.
Loading
The final step is the loading of the program file into the computer’s memory so that it can be
executed. This is performed by a loader. Usually the linked will include the loader into the
~ 14 ~
executable file. When we run the executable it will trigger the loader which will load the
program into the memory and the execution will begin.
You also receive a compiler error, if accidentally use the wrong punctuation or place
punctuation in the wrong place.
b) Run-time error:
c) Logic error:
Your application runs but produces incorrect result. Perhaps the results of calculation are
incorrect or the wrong text appears, or the text is ok but appears in the wrong location.
~ 15 ~
1.2.2 Top-down design
Use top-down design, structure diagrams, library routines, sub-routines and Standard methods of
solution
a. Top-down design
Top-down design is breaking down a system into smaller sub-systems and breaking these sub-
systems down into smaller sub-systems until each sub-system performs one task.
This is a method of breaking down a problem into smaller problems.
Doing this allows several programmers to work on different sub-systems; thus, making the
design process more efficient.
b. Structure diagrams
~ 16 ~
c. Library routines
A library routine is a set of programming instructions for a specific task available to use.
It is debugged and tested before being made available.
d. Sub-routines
A sub-routine is a set of programming instructions for a given task that forms a sub-system.
Sub-routines are written in high-level programming languages (they may be referred to as
functions or procedures).
~ 17 ~
1.2.3 Algorithm purpose
Work out the purpose of a given algorithm with given Value 45
Solution
Input data: 45
Example: 10 DIV 3 = 3
Example: 10 MOD 3 = 1
~ 18 ~
Purpose: Converts a decimal number into binary.
~ 19 ~
a. Normal Test Data:
Normal test data consists of values which are expected to be accepted by an algorithm.
Test data: 5, 10
Expected result: both values should be accepted.
Abnormal test data consists of values which are expected to be rejected by an algorithm.
Test data: 45, -19
Expected result: both values should be rejected.
Extreme test data consists of the smallest and largest possible values which are accepted by an
algorithm.
Test data: 0, 25
Expected result: both values should be accepted.
Boundary test data is used to determine where the smallest and largest values occur.
Test data: 25, 26
Expected result: 25 should be accepted, 26 should be rejected.
~ 20 ~
Input data:
1.8, 2.0, 1.0, 1.3, 1.0, 2.5, 2.0, 1.3, 1.8, 1.3, -1
Solution:
~ 21 ~
1.2.6 Validation and Verification
a. Validation is checking if a value is reasonable before being accepted into a computer
system.
b. Verification is checking if data has been accurately input into a computer or transferred
rom one part of a computer system to another.
~ 22 ~
Checksum - A checksum is another method of error detection in data transmission. Data is sent
in blocks and at the end of each block, another value is added – this is the checksum.
Three main stages are involved in creating an algorithm for a given problem:
Data input
Data processing
Output
Consider a computer program that finds the average value of three numbers:
First, the program must prompt the user to enter the numbers (data input).
Next, the program calculates the average value of the numbers (data processing).
Finally, the program displays the result on the computer’s screen (results output).
The purpose is not to be able to classify an algorithm as one type or another, but to highlight the
various ways in which a problem can be solved.
~ 23 ~
Brute force algorithms
Randomized algorithms
It is called “simple” because several of the other algorithm types are inherently recursive.
More generally if a problem can be solved utilizing solutions to smaller versions of the same
problem, and the smaller versions reduce to easily solvable cases, then one can use a recursive
algorithm to solve that problem. For example, the elements of a recursively defined set, or the
value of a recursively defined function can be obtained by a recursive algorithm.
If a set or a function is defined recursively, then a recursive algorithm to compute its members or
values mirrors the definition. Initial steps of the recursive algorithm correspond to the basis
clause of the recursive definition and they identify the basic elements.
They are then followed by steps corresponding to the inductive clause, which reduce the
computation for an element of one generation to that of elements of the immediately preceding
generation.
In general, recursive computer programs require more memory and computation compared with
iterative algorithms, but they are simpler and for many cases a natural way of thinking about the
problem.
~ 24 ~
First, a base case is the condition that allows the algorithm to stop recursing. A base case is
typically a problem that is small enough to solve directly.
To obey the second law, we must arrange for a change of state that moves the algorithm toward
the base case. A change of state means that some data that the algorithm is using is modified.
Usually the data that represents our problem gets smaller in some way.
The final law is that the algorithm must call itself. This is the very definition of recursion.
Recursion is an elegant expression of solving a problem by breaking it down into a smaller and
easier problems.
Algorithm:
if k = 1, then return 0;
else return Even(k-1) + 2 .
Here the computation of Even(k) is reduced to that of Even for a smaller input value, that
is Even(k-1). Even(k) eventually becomes Even(1) which is 0 by the first line. For example, to
compute Even(3), Algorithm Even(k) is called with k = 2. In the computation
of Even(2), Algorithm Even(k) is called with k = 1. Since Even(1) = 0, 0 is returned for the
computation of Even(2), and Even(2) = Even(1) + 2 = 2 is obtained. This value 2 for Even(2) is
now returned to the computation of Even(3), and Even(3) = Even(2) + 2 = 4 is obtained.
n! = n(n-1)(n-2)...1
or in other words:
~ 25 ~
n! = n(n-1)!
The recursive version uses the second definition: n! = n * (n - 1)!, which is naturally
a recursive definition. It defines the factorial of n in terms of the factorial of a smaller version (n
- 1). We don't need to specify in advance, how many times the loop will run.
function factorialRecursive(n) {
let result = 0;
if (n == 1)
result = 1;
// recursive case
return result;
factorialRecursive(4);
Recursive algorithms can also be used to test objects for membership in a set.
Algorithm:
if x < 0, then return "No"
else
if x = 0, then return "Yes"
else return Natural( x - 1 )
~ 26 ~
Learning Outcome 1.5 Use Backtracking Algorithms Type
1.5.1 Description of backtracking algorithm
a. Definition
Backtracking is a very important concept in computer science and is used in many applications.
Generally, we use it when all possible solutions of a problem need to be explored. It is also often
employed to identify solutions that satisfy a given criterion also called a constraint.
A space state tree is a tree representing all the possible states (solution or nonsolution) of the
problem from the root as an initial state to the leaf as a terminal state.
c. Pseudocode
Backtrack(x)
if x is not a solution
return false
if x is a new solution
backtrack(expand x)
~ 27 ~
d. Usage considerations
In any backtracking algorithm, the algorithm seeks a path to a feasible solution that includes
some intermediate checkpoints. If the checkpoints do not lead to a viable solution, the problem
can return to the checkpoints and take another path to find a solution. Consider the following
scenario:
In this case, S represents the problem's starting point. You start at S and work your way to
solution S1 via the midway point M1. However, you discovered that solution S1 is not a viable
solution to our problem. As a result, you backtrack (return) from S1, return to M1, return to S,
and then look for the feasible solution S2. This process is repeated until you arrive at a workable
solution.
1. S1 and S2 are not viable options in this case. According to this example, only S3 is a viable
solution. When you look at this example, you can see that we go through all possible
~ 28 ~
combinations until you find a viable solution. As a result, you refer to backtracking as a brute-
force algorithmic technique.
2. A "space state tree" is the above tree representation of a problem. It represents all possible
states of a given problem (solution or non-solution).
Step 2: Otherwise, if all paths have been exhausted (i.e., the current point is an endpoint),
return failure because there is no feasible solution.
Step 3: If the current point is not an endpoint, backtrack and explore other points, then repeat
the preceding steps.
The pseudo-code above will call output for all candidates that are a solution to the given
instance. The algorithm can be modified to stop after finding the first solution, or a specified
number of solutions; or after testing a specified number of partial candidates, or after spending a
given amount of CPU time.
Backtracking is a rather typical recursive algorithm, and any recursive algorithm can be rewritten
as a stack algorithm. In fact, that is how your recursive algorithms are translated into machine or
assembly language.
boolean solve(Node n) {
put node n on the stack;
while the stack is not empty {
if the node at the top of the stack is a leaf {
if it is a goal node, return true
else pop it off the stack
}
~ 29 ~
else {
if the node at the top of the stack has untried children
push the next untried child onto the stack
else pop the node off the stack
}
return false
}
Starting from the root, the only nodes that can be pushed onto the stack are the children of the
node currently on the top of the stack, and these are only pushed on one child at a time; hence,
the nodes on the stack at all times describe a valid path in the tree.
Nodes are removed from the stack only when it is known that they have no goal nodes among
their descendants. Therefore, if the root node gets removed (making the stack empty), there must
have been no goal nodes at all, and no solution to the problem.
When the stack algorithm terminates successfully, the nodes on the stack form (in reverse order)
a path from the root to a goal node.
Similarly, when the recursive algorithm finds a goal node, the path information is embodied (in
reverse order) in the sequence of recursive calls. Thus as the recursion unwinds, the path can be
recovered one node at a time, by (for instance) printing the node at the current level, or storing it
in an array.
Here is the recursive backtracking algorithm, modified slightly to print (in reverse order) the
nodes along the successful path:
boolean solve(Node n) {
if n is a leaf node {
if the leaf is a goal node {
print n
return true
}
~ 30 ~
else return false
} else {
for each child c of n {
if solve(c) succeeds {
print n
return true
}
}
return false
}
}
1.5.3 Keeping backtracking simple
All of these versions of the backtracking algorithm are pretty simple, but when applied to a real
problem, they can get pretty cluttered up with details. Even determining whether the node is a
leaf can be complex: for example, if the path represents a series of moves in a chess endgame
problem, the leaves are the checkmate and stalemate solutions.
To keep the program clean, therefore, tests like this should be buried in methods. In a chess
game, for example, you could test whether a node is a leaf by writing a gameOver method (or
you could even call it isLeaf). This method would encapsulate all the ugly details of figuring out
whether any possible moves remain.
Notice that the backtracking altorithms require us to keep track, for each node on the current
path, which of its children have been tried already (so we don't have to try them again).
In the above code we made this look simple, by just saying for each child c of n. In reality, it
may be difficult to figure out what the possible children are, and there may be no obvious way to
step through them. In chess, for example, a node can represent one arrangement of pieces on a
chessboard, and each child of that node can represent the arrangement after some piece has made
a legal move. How do you find these children, and how do you keep track of which ones you've
already examined?
~ 31 ~
The most straightforward way to keep track of which children of the node have been tried is as
follows: Upon initial entry to the node (that is, when you first get there from above), make a list
of all its children. As you try each child, take it off the list. When the list is empty, there are no
remaining untried children, and you can return "failure." This is a simple approach, but it may
require quite a lot of additional work.
There is an easier way to keep track of which children have been tried, if you can define an
ordering on the children. If there is an ordering, and you know which child you just tried, you
can determine which child to try next.
For example, you might be able to number the children 1 through n, and try them in numerical
order. Then, if you have just tried child k, you know that you have already tried
children 1 through k-1, and you have not yet tried children k+1 through n. Or, if you are trying to
color a map with just four colors, you can always try red first, then yellow, then green, then blue.
If child yellow fails, you know to try child green next. If you are searching a maze, you can try
choices in the order left, straight, right (or perhaps north, east, south, west).
It isn't always easy to find a simple way to order the children of a node. In the chess game
example, you might number your pieces (or perhaps the squares of the board) and try them in
numerical order; but in addition each piece may also have several moves, and these must also be
ordered.
You can probably find some way to order the children of a node. If the ordering scheme is
simple enough, you should use it; but if it is too cumbersome, you are better off keeping a list of
untried children.
Solution: There are a total of 3! = 6 possibilities. We will try all the possibilities and get the
possible solutions. We recursively try all the possibilities.
~ 32 ~
All the possibilities
Example 2:
You need to arrange the three letters x, y, and z so that z cannot be next to x.
According to the backtracking, you will first construct a state-space tree. Look for all possible
solutions and compare them to the given constraint. You must only keep solutions that meet the
~ 33 ~
following constraint:
The following are possible solutions to the problems: (x,y,z), (x,z,y), (y,x,z), (y,z,x), (z,x,y)
(z,y,x).
Nonetheless, valid solutions to this problem are those that satisfy the constraint that keeps only
(x,y,z) and (z,y,x) in the final solution set.
– color(Country n)
~ 34 ~
» If successful, return success
1. Divide the actual problem into sub-problems (A subproblem is just a smaller instance of the
same problem).
3. Combine the solutions of the sub-problems to get the solution to the actual problem.
For example, Let there be a problem of size N and let us divide this problem into 4 sub-problems
say n1, n2, n3, and n4.
Let the time taken to complete the whole problem be T(N), time taken to complete n1, n2, n3, n4
respectively be T(n1), T(n2), T(n3) and T(n4), time taken in dividing the problem into sub-
problems be D(N), time taken to combine the solutions of sub-problems into one final solution
be C(N).
~ 35 ~
Then,
T(N) = T(n1) + T(n2) + T(n3) + T(n4) + D(N) + C(N) (If N is large)
T(N) = g(n) (If N is small)
Traditionally, an algorithm is only called divide and conquer if it contains two or more recursive
calls
1. Relational formula
a. It is the first thing required to solve a problem by divide and conquer effectively.
b. The relational formula is the formula that we generate for a given technique.
c. We need this formula to apply divide and conquer, break the problem into sub-problems and
then solve them recursively.
2. Stopping condition
a. It defines the point at which we need to stop dividing our main problem into sub-problems and
start combining the results that we get out of sub-problems.
b. We can also say that the stopping condition defines the base case of our recursive algorithm.
DAC(a, i, j){
if(small (i,j))
else{
m = Divide(i, j);
b = DAC(a, i, m);
~ 36 ~
c = DAC(a, m, j);
d = Combine(b,c);
Where T(n) is the time for Divide and Conquer of size n. g(n) is the computing time required for
solve small input. The F(n) is the time required in dividing the problem P and combining the
solutions to sub problems.
If we want to divide a problem of the size n into a size of n/b taking f(n) time to divide and
combine, then we can set up recurrence relation for obtaining size for n is:
The above equation is called divide and conquer recurrence. The order of growth of T(n)
depends upon the constants a, b and order of growth function f(n).
a. Binary search
b. Merge sort
c. Quicksort
~ 37 ~
d. Strassen’s matrix multiplication
e. Karatsuba algorithm
and many more.
a. Binary search
Binary search is also implemented by the divide and conquer strategy. This is used to find a
particular element in a sorted array.
While implementing binary search, we divide the array into 2 halves and check if the number to
be searched could be on the left half or right half. Then, we go to that half and again divide the
array into further two halves. This process goes on until the number to be searched is found.
Note: - There is another thought of school which says that binary search does not come directly
under the divide and conquer. Rather, it is implemented using an altogether different
methodology named ‘Decrease and Conquer’.
~ 38 ~
3. Find the middle element mid of the array ie. arr[(low + high)/2] = 6.
4. If x == mid, then return mid. Else, compare the element to be searched with m.
5. If x > mid, compare x with the middle element of the elements on the right side of mid. This is
done by setting low to low = mid + 1.
6. Else, compare x with the middle element of the elements on the left side of mid. This is done by
setting high to high=mid -1
8. x = 4 is found.
else
if x == arr[mid]
return mid
b. Merge sort
It divides the given array into halves which further divide themselves into two halves until
the array length becomes one.
It then starts merging the two sorted halves from bottom to top until we get the final sorted
array.
~ 40 ~
Merge sort example
Using the Divide and Conquer technique, we divide a problem into subproblems. When the
solution to each subproblem is ready, we 'combine' the results from the subproblems to solve the
main problem.
Suppose we had to sort an array A. A subproblem would be to sort a sub-section of this array
starting at index p and ending at index r, denoted as A[p..r].
Divide
~ 41 ~
If q is the half-way point between p and r, then we can split the subarray A[p..r] into two
arrays A[p..q] and A[q+1, r].
Conquer
In the conquer step, we try to sort both the subarrays A[p..q] and A[q+1, r]. If we haven't yet
reached the base case, we again divide both these subarrays and try to sort them.
Combine
When the conquer step reaches the base step and we get two sorted subarrays A[p..q] and A[q+1,
r] for array A[p..r], we combine the results by creating a sorted array A[p..r] from two sorted
subarrays A[p..q] and A[q+1, r].
MergeSort Algorithm
The MergeSort function repeatedly divides the array into two halves until we reach a stage where
we try to perform MergeSort on a subarray of size 1 i.e. p == r.
After that, the merge function comes into play and combines the sorted arrays into larger arrays
until the whole array is merged.
MergeSort(A, p, r):
if p > r
return
q = (p+r)/2
mergeSort(A, p, q)
mergeSort(A, q+1, r)
merge(A, p, q, r)
c. Quicksort
Quicksort is a sorting algorithm based on the divide and conquer approach where
~ 42 ~
An array is divided into subarrays by selecting a pivot element (element selected from the array).
While dividing the array, the pivot element should be positioned in such a way that elements less
than pivot are kept on the left side and elements greater than pivot are on the right side of the
pivot.
The left and right subarrays are also divided using the same approach. This process continues
until each subarray contains a single element.
At this point, elements are already sorted. Finally, elements are combined to form a sorted array.
There are many different versions of quickSort that pick pivot in different ways.
There are different variations of quicksort where the pivot element is selected from different
positions. Here, we will be selecting the rightmost element of the array as the pivot element.
Now the elements of the array are rearranged so that elements that are smaller than the pivot are
put on the left and the elements greater than the pivot are put on the right.
~ 43 ~
Here's how we rearrange the array:
A pointer is fixed at the pivot element. The pivot element is compared with the elements
beginning from the first index.
If the element is greater than the pivot element, a second pointer is set for that element.
Now, pivot is compared with other elements. If an element smaller than the pivot element is
reached, the smaller element is swapped with the greater element found earlier.
~ 44 ~
Again, the process is repeated to set the next greater element as the second pointer. And, swap it
with another smaller element.
3. Divide Subarrays
Pivot elements are again chosen for the left and the right sub-parts separately. And, step 2 is
repeated.
~ 45 ~
Select pivot element of in each half and put at correct place using recursion
The subarrays are divided until each subarray is formed of a single element. At this point, the
array is already sorted.
~ 46 ~
storeIndex++
return storeIndex + 1
The working of quicksort algorithm with the help of the illustrations below.
~ 47 ~
d. Strassen’s Matrix Multiplication
The main idea is to use the divide and conquer technique in this algorithm – divide matrix A &
matrix B into 8 submatrices and then recursively compute the submatrices of C.
|c d| |g h| |ce+dg cf+dh|
a*e
b*g
a*f
b*h
c*e
~ 48 ~
d*g
c*f
d*h
But Strassen came up with a solution where we don’t need 8 recursive calls but can be done in
only 7 calls and some extra addition and subtraction operations.
a * (f - h)
(a + b) * h
(c + d) * e
d * (g - e)
(a + d) * (e + h)
(b - d) * (g + h)
(a - c) * (e + f)
~ 49 ~
| p3+p4 p1+p5-p3-p7 |
Strassen’s Submatrix
= ae+bg
= (af-ah) + (ah+bh)
= af+bh
= (ce+de) + (dg-de)
= ce+dg
~ 50 ~
= cf+dh
Divide matrix A and matrix B in 4 sub-matrices of size N/2 x N/2 as shown above.
begin
If n = threshold then compute
C = a * b is a conventional matrix.
Else
Partition a into four sub matrices a11, a12, a21, a22.
Partition b into four sub matrices b11, b12, b21, b22.
Strassen ( n/2, a11 + a22, b11 + b22, d1)
Strassen ( n/2, a21 + a22, b11, d2)
Strassen ( n/2, a11, b12 – b22, d3)
Strassen ( n/2, a22, b21 – b11, d4)
Strassen ( n/2, a11 + a12, b22, d5)
Strassen (n/2, a21 – a11, b11 + b22, d6)
Strassen (n/2, a12 – a22, b21 + b22, d7)
C = d1+d4-d5+d7 d3+d5
d2+d4 d1+d3-d2-d6
end if
return (C)
end.
~ 51 ~
e. Karatsuba algorithm
The Karatsuba algorithm is a fast multiplication algorithm that uses a divide and conquer
approach to multiply two numbers. It was discovered by Anatoly Karatsuba in 1960 and
published in 1962.
It is faster than the naive algorithm for multiplying two numbers which requires n2n2 single-digit
products. As an example, the Karatsuba algorithm requires 3^10 = 59,049 single-digit
multiplications to multiply two 1024-digit numbers (n = 1024 = 2^10), whereas the classical
algorithm requires (2^10)^2 = 1,048,576 single-digit multiplications.
The key idea is to reduce the four sub-problems in multiplication to three unique problems.
Thus, on calculating the three unique sub-problems, the original four sub-problems are solved
using addition or subtraction operation. Hence, the speed-up.
Basically Karatsuba stated that if we have to multiply two n-digit numbers x and y, this can be
done with the following operations, assuming that B is the base of m and m < n (for instance: m
= n/2)
First both numbers x and y can be represented as x1,x2 and y1,y2 with the following formula.
x=x1∗Bm+x2x=x1∗Bm+x2
y=y1∗Bm+y2y=y1∗Bm+y2
The product x X y becomes the following product:
xy=(x1∗Bm+x2)(y1∗Bm+y2)xy=(x1∗Bm+x2)(y1∗Bm+y2)
=>xy=x1∗y1∗B(2m)+x1∗y2∗Bm+x2∗y1∗Bm+x2∗y2=>xy=x1∗y1∗B(2m)
+x1∗y2∗Bm+x2∗y1∗Bm+x2∗y2
Observe that there are 4 sub-problems: X1 * Y1, X1 * Y2, X2 * Y1 and X2 * Y2
With a clever insight, we can reduce this to 3 sub-problems and hence, the acceleration.
Let a=x1∗y1a=x1∗y1, b=x1∗y2+x2∗y1b=x1∗y2+x2∗y1 and c=x2∗y2c=x2∗y2
~ 52 ~
Finally, x X y becomes:
xy=a∗B(2m)+b∗Bm+cxy=a∗B(2m)+b∗Bm+c
That is why Karatsuba came up with the brilliant idea to calculate b with the following formula:
b=(x1+x2)(y1+y2)−a−c
Example:
x = 47
x = 4 * 10 + 7
x1 = 4
x2 = 7
y = 78
y = 7 * 10 + 8
y1 = 7
y2 = 8
a = x1 * y1 = 4 * 7 = 28
c = x2 * y2 = 7 * 8 = 56
~ 53 ~
11 * 15 can in turn be multiplied using Karatsuba Algorithm
Pseudocode
return num1*num2
m = max(size_base10(num1), size_base10(num2))
m2 = m/2
z0 = karatsuba(low1, low2)
z2 = karatsuba(high1, high2)
~ 54 ~
Learning Outcome 1.7 Use Dynamic programming
algorithms type
Dynamic Programming (DP) is an algorithmic technique for solving an optimization problem by
breaking it down into simpler subproblems and utilizing the fact that the optimal solution to the
overall problem depends upon the optimal solution to its subproblems.
Let’s take the example of the Fibonacci numbers. As we all know, Fibonacci numbers are a
series of numbers in which each number is the sum of the two preceding numbers. The first few
Fibonacci numbers are 0, 1, 1, 2, 3, 5, and 8, and they continue on from there.
If we are asked to calculate the nth Fibonacci number, we can do that with the following
equation,
As we can clearly see here, to solve the overall problem (i.e. Fib(n)), we broke it down into two
smaller subproblems (which are Fib(n-1) and Fib(n-2)). This shows that we can use DP to solve
this problem.
Like Divide and Conquer, Dynamic Programming combines solutions to sub-problems. Dynamic
Programming is mainly used when solutions of the same subproblems are needed again and
again i.e Dynamic Programming differs from Divide and Conquer, where subproblems generally
need not overlap.
a. Overlapping Subproblems
Subproblems are smaller versions of the original problem. Any problem has overlapping sub-
problems if finding its solution involves solving the same subproblem multiple times. Take the
example of the Fibonacci numbers; to find the fib(4), we need to break it down into the following
sub-problems:
~ 55 ~
Recursion tree for calculating Fibonacci numbers
We can clearly see the overlapping subproblem pattern here, as fib(2) has been evaluated twice
and fib(1) has been evaluated three times.
Any problem has optimal substructure property if its overall optimal solution can be constructed
from the optimal solutions of its subproblems. For Fibonacci numbers, as we know,
This clearly shows that a problem of size ‘n’ has been reduced to subproblems of size ‘n-1’ and
‘n-2’. Therefore, Fibonacci numbers have optimal substructure property.
In this approach, we try to solve the bigger problem by recursively finding the solution to smaller
sub-problems. Whenever we solve a sub-problem, we cache its result so that we don’t end up
~ 56 ~
solving it repeatedly if it’s called multiple times. Instead, we can just return the saved result. This
technique of storing the results of already solved subproblems is called Memoization.
Example: Write the code for the factorial problem in the top-down fashion
// of calculated states
// initialized to -1
int dp[MAXN]
// return fact x!
int solve(int x)
if (x==0)
return 1;
if (dp[x]!=-1)
return dp[x];
Tabulation is the opposite of the top-down approach and avoids recursion. In this approach, we
solve the problem “bottom-up” (i.e. by solving all the related sub-problems first). This is
typically done by filling up an n-dimensional table. Based on the results in the table, the solution
to the top/original problem is then computed.
~ 57 ~
Tabulation is the opposite of Memoization, as in Memoization we solve the problem and
maintain a map of already solved sub-problems. In other words, in memoization, we do it top-
down in the sense that we solve the top problem first (which typically recurses down to solve the
sub-problems).
int dp[MAXN];
// base case
int dp[0] = 1;
dp[i] = dp[i-1] * i;
~ 58 ~
1.7.3 Solving a dynamic programming problem
To solve any dynamic programming problem, we can use the FAST method.
'F' stands for Find the recursive solution: Whenever we find any DP problem, we have to find
the recursive solution.
'A' stands for Analyse the solution: Once we find the recursive solution then we have to analyse
the solution and look for the overlapping problems.
Typically, all the problems that require maximizing or minimize certain quantities or counting
problems that say to count the arrangements under certain conditions or certain probability
problems can be solved by using Dynamic Programming.
All dynamic programming problems satisfy the overlapping subproblems property and most of
the classic dynamic problems also satisfy the optimal substructure property. Once, we observe
these properties in a given problem, be sure that it can be solved using DP.
DP problems are all about state and their transition. This is the most basic step which must be
done very carefully because the state transition depends on the choice of state definition you
make. So, let’s see what do we mean by the term “state”.
State A state can be defined as the set of parameters that can uniquely identify a certain
position or standing in the given problem. This set of parameters should be as small as possible
to reduce state space.
'S' stands for Save the results for future use: Once we find the overlapping problems, we store
the solutions of these sub-problems. To store the solutions, we use the n-dimensional array for
caching purpose.
The above three steps are used for the top-down approach if we use 'F', 'A' and 'S', which means
that we are achieving the Top-down approach. Since it is not purely because we are using the
recursive technique.
~ 59 ~
'T' stands for Tweak the solution to make it more powerful by eliminating recursion overhead
which is known as a Bottom-up approach. Here we remove the recursion technique and use the
iterative approach to achieve the same results, so it's a pure approach. Recursion is always an
overhead as there are chances of getting a stack overflow error, so we should use the bottom-up
approach to avoid this problem.
0, 1, 1, 2, 3, 5, 8, 13, 21,...
Fib(n)
if(n<2)
return n;
The above recursive solution is also the solution for the above problem . So, dynamic
programming is used to reduce the time complexity from the exponential time to the linear time.
~ 60 ~
Fib(4)= fib(3) + fib(2)
As we can observe in the above figure that fib(2) is calculated two times while fib(1) is
calculated three times. So, here overlapping problem occurs. In this step, we have analysed the
solution.
The process of saving the result is known as memoization. In this step, we will follow the same
approach, i.e., recursive approach but with a small different that we have used the cache to store
the solutions so that it can be re-used whenever required.
Fib(n)
if(n<2)
return n;
if(cache[n]!= 0)
return cache[n];
In the above code, we have used a cache array of size n+1. If cache[n] is not equal to zero then
we return the result from the cache else we will calculate the value of cache and then return the
cache. The technique that we have used here is top-down approach as it follows the recursive
approach. Here, we always look for the cache so cache will be populated on the demand basis.
~ 61 ~
Suppose we want to calculate the fib(4), first we look into cache, and if the value is not in the
cache then the value is calculated and stored in the cache.
In this step, we will remove the recursion completely and make it an iterative approach. So, this
technique is known as a bottom-up approach.
Fib(n)
// base cases
cache[0] = 0;
cache[1] = 1;
~ 62 ~
{
return cache[n];
In the above code, we have followed the bottom-up approach. We have declared a cache array of
size n+1. The base cases are cache[0] and cache[1] with their values 0 and 1 respectively. In the
above code, we have removed the recursion completely. We have used an iterative approach. We
have defined a for loop in which we populate the cache with the values from the index i=2 to n,
and from the cache, we will return the result. Suppose we want to calculate f(4), first we will
calculate f(2), then we will calculate f(3) and finally, we we calculate the value of f(4). Here we
are going from down to up so this approach is known as a bottom-up approach.
As we can observe in the above figure that we are populating the cache from bottom to up so it is
known as bottom-up approach. This approach is much more efficient than the previous one as it
~ 63 ~
is not using recursion but both the approaches have the same time and space complexity, i.e.,
O(n).
In this case, we have used the FAST method to obtain the optimal solution. The above is the
optimal solution that we have got so far but this is not the purely an optimal solution.
Efficient solution:
fib(n)
if(n<2)
return 0;
first = second;
second = sum;
return sum;
The above solution is the efficient solution as we do not use the cache.
~ 64 ~
It simply means to pick up a choice/solution that seems the best at the moment (being
greedy). This technique is best suited when we want an immediate situation. It helps to solve
optimization problems i.e. which gives either minimum results or maximum results.
A solution satisfying the condition in the problem is a feasible solution. The solution having
minimum cost out of all possible feasible solutions is the optimal solution i.e. it is the best
solution.
The goal of the greedy algorithm is to find the optimal solution. There can be only 1 optimal
solution.
a. Greedy introduction
A. History of Greedy Algorithm
The greedy algorithm was first coined by the Dutch computer scientist and mathematician
Edsger W. Dijkstra when he wanted to calculate the minimum spanning tree. The major purpose
of many greedy algorithms was to solve graph-based problems.
The greedy algorithms first started coming into the picture in the 1950s. The then scientists, Prim
and Kruskal also achieved the optimization techniques for minimizing the costs of graphs during
that decade.
A few years later, in the 1970s, many American researchers proposed a recursive strategy for
solving greedy problems. In 2005, the NIST records registered the greedy paradigm as a separate
optimization strategy.
Since then, the greedy algorithm has been extensively in use in multiple fields including the web
protocols such as the open-shortest-path-first (OSPF) and many other network packet switching
protocols.
Logic in its easiest form was boiled down to “greedy” or “not greedy”. These statements were
defined by the approach taken to advance in each algorithm stage.
~ 65 ~
For example, Djikstra’s algorithm utilized a stepwise greedy strategy identifying hosts on the
Internet by calculating a cost function. The value returned by the cost function determined
whether the next path is “greedy” or “non-greedy”.
In short, an algorithm ceases to be greedy if at any stage it takes a step that is not locally greedy.
The Greedy problems halt with no further scope of greed.
There are many techniques to solve a problem. Out of these many techniques, we have
optimization techniques for a particular set of problems. Out of optimization techniques as well,
there are multiple choices.
So, how do we know that when to use and when not to use the greedy method as an optimization
algorithm.
For this purpose, we will check the properties of the algorithm. If the following two properties
hold, we will use the greedy approach or other approaches.
To reach the final optimal solution or the globally optimal solution, we find locally optimal
solutions(the best at that moment) for each sub-task.
2. Optimal sub-programs
1. In the greedy method, we divide the main problem into sub-problems and solve each of them
recursively.
~ 66 ~
E. Importance of Greedy algorithm
The greedy approach has a few tradeoffs, which may make it suitable for optimization.
One prominent reason is to achieve the most feasible solution immediately. In the activity
selection problem (Explained below), if more activities can be done before finishing the
current activity, these activities can be performed within the same time.
Another reason is to divide a problem recursively based on a condition, with no need to
combine all the solutions.
In the activity selection problem, the “recursive division” step is achieved by scanning a
list of items only once and considering certain activities.
2. This algorithm takes lesser time as compared to others because the best solution is
immediately reachable.
3. In the greedy method, multiple activities can execute in a given time frame.
1. The greedy method works by finding the best possible outcome at every step of the algorithm.
This sometimes leads to inaccurate results.
For example, suppose we wish to find the longest path in the following graph:
~ 67 ~
If we follow the greedy approach, we will make the best possible choice at every node. In that
case, the path will come out to be: 12→16→6→35 which comes out to be 69.
[Link] problematic part for a greedy algorithm is analyzing its accuracy. Even with the proper
solution, it is difficult to demonstrate why it is accurate.
3. Optimization problems (Dijkstra’s Algorithm) with negative graph edges cannot be solved
using a greedy algorithm.
The greedy algorithm proceeds by making feasible choices at each step of the whole process.
Feasible choices are those which satisfy all the algorithmic constraints.
Choose what is best at the given time i.e. make locally optimal choices while preceding through
the algorithm.
C. Unalterable
~ 68 ~
We cannot alter any sub-solution at any subsequent point of the algorithm while execution.
Problem: Alex is a very busy person. He has set aside time T to accomplish some interesting
tasks. He wants to do as many tasks as possible in this allotted time T. For that, he has created an
array A of timestamps to complete a list of items on his itinerary.
Now, here we need to figure out how many things Alex can complete in the T time he has.
Approach to Build a Solution: This given problem is a straightforward greedy problem. In each
iteration, we will have to pick the items from array A that will take the least amount of time to
accomplish a task while keeping two variables in mind: current_Time and number_Of_Things.
To generate a solution, we will have to carry out the following steps.
~ 69 ~
1.8.2 Use of greedy approach for solving the activity selection problem
The Activity Selection Problem is an optimization problem which deals with the selection of
non-conflicting activities that needs to be executed by a single person or machine in a given time
frame.
Each activity is marked by a start and finish time. Greedy technique is used for finding the
solution since this is an optimization problem.
Let's consider that you have n activities with their start and finish times, the objective is to find
solution set having maximum number of non-conflicting activities that can be executed in a
single time frame, assuming that only one person or machine is available for execution.
It might not be possible to complete all the activities, since their timings can collapse.
Greedy approach can be used to find the solution since we want to maximize the count
of activities that can be executed. This approach will greedily choose an activity with
earliest finish time at every step, thus yielding an optimal solution.
~ 70 ~
sol[] array refering to the solution set containing the maximum number of non-conflicting
activities.
Following are the steps we will be following to solve the activity selection problem,
Step 1: Sort the given activities in ascending order according to their finishing time.
Step 2: Select the first activity from sorted array act[] and add it to sol[] array.
Step 4: If the start time of the currently selected activity is greater than or equal to the finish time
of previously selected activity, then add it to the sol[] array.
5 9 a1
1 2 a2
3 4 a3
~ 71 ~
0 6 a4
5 7 a5
8 9 a6
Step 1: Sort the given activities in ascending order according to their finishing time.
1 2 a2
3 4 a3
0 6 a4
5 7 a5
~ 72 ~
5 9 a1
8 9 a6
Step 2: Select the first activity from sorted array act[] and add it to the sol[] array, thus sol =
{a2}.
Step 3: Repeat the steps 4 and 5 for the remaining activities in act[].
Step 4: If the start time of the currently selected activity is greater than or equal to the finish time
of the previously selected activity, then add it to sol[].
A. Select activity a3. Since the start time of a3 is greater than the finish time of a2 (i.e. s(a3)
> f(a2)), we add a3 to the solution set. Thus sol = {a2, a3}.
B. Select a4. Since s(a4) < f(a3), it is not added to the solution set.
C. Select a5. Since s(a5) > f(a3), a5 gets added to solution set. Thus sol = {a2, a3, a5}
D. Select a1. Since s(a1) < f(a5), a1 is not added to the solution set.
E. Select a6. a6 is added to the solution set since s(a6) > f(a5). Thus sol = {a2, a3, a5, a6}.
Hence, the execution schedule of maximum number of non-conflicting activities will be:
~ 73 ~
(1,2)
(3,4)
(5,7)
(8,9)
Branch and bound algorithms are used to find the optimal solution for combinatory, discrete, and
general mathematical optimization problems. In general, given a Hard problem, a branch and
bound algorithm explores the entire search space of possible solutions and provides an optimal
solution.
A branch and bound algorithm consist of stepwise enumeration of possible candidate solutions
by exploring the entire search space. With all the possible solutions, we first build a rooted
decision tree. The root node represents the entire search space:
~ 74 ~
Here, each child node is a partial solution and part of the solution set. Before constructing the
rooted decision tree, we set an upper and lower bound for a given problem based on the optimal
solution. At each level, we need to make a decision about which node to include in the solution
set. At each level, we explore the node with the best bound. In this way, we can find the best and
optimal solution fast.
Now it is crucial to find a good upper and lower bound in such cases. We can find an upper
bound by using any local optimization method or by picking any point in the search space. On
the other hand, we can obtain a lower bound from convex relaxation or duality.
In general, we want to partition the solution set into smaller subsets of solution. Then we
construct a rooted decision tree, and finally, we choose the best possible subset (node) at each
level to find the best possible solution set.
If the given problem is a discrete optimization problem, a branch and bound is a good
choice. Discrete optimization is a subsection of optimization where the variables in the problem
~ 75 ~
should belong to the discrete set. Examples of such problems are 0-1 Integer
Programming or Network Flow problem.
Branch and bound work efficiently on the combinatory optimization problems. Given an
objective function for an optimization problem, combinatory optimization is a process to find the
maxima or minima for the objective function. The domain of the objective function should be
discrete and large. Boolean Satisfiability, Integer Linear Programming are examples of the
combinatory optimization problems.
Example: define a job assignment problem. In a standard version of a job assignment problem,
there can be N jobs and N workers. To keep it simple, we’re taking 3 jobs and 3 workers in our
Example
We can assign any of the available jobs to any worker with the condition that if a job is assigned
to a worker, the other workers can’t take that particular job. We should also notice that each job
has some cost associated with it, and it differs from one worker to another.
Here the main aim is to complete all the jobs by assigning one job to each worker in such a way
that the sum of the cost of all the jobs should be minimized.
~ 76 ~
Initially, we’ve 3 jobs available. The worker A has the option to take any of the 3 available jobs.
So at level 1, we assigned all the available jobs to the worker A and calculated the cost. We can
see that when we assigned jobs 2 to the worker A, it gives the lowest cost in level 1 of the search
space tree. So we assign the job 2 to worker A and continue the algorithm. “Yes” indicates
that this is currently optimal cost.
After assigning the job 2 to worker A, we still have two open jobs. Let’s consider worker B now.
We’re trying to assign either job 1 or 3 to worker B to obtain optimal cost.
Either we can assign the job 1 or 3 to worker B. Again we check the cost and assign job 1 to
worker B as it is the lowest in level 2.
Finally, we assign the job 3 to worker C, and the optimal cost is 12.
~ 77 ~
a. Definition:
This is the most basic and simplest type of algorithm. A Brute Force Algorithm is the
straightforward approach to a problem i.e., the first approach that comes to our mind on seeing
the problem. More technically it is just like iterating every possibility available to solve that
problem.
A brute force algorithm simply tries all possibilities until a satisfactory solution is found.
For Example:
If there is a lock of 4-digit PIN. The digits to be chosen from 0-9 then the brute force will be
trying all possible combinations one by one like 0001, 0002, 0003, 0004, and so on until we
get the right PIN. In the worst case, it will take 10,000 tries to find the right combination.
Optimizing: Find the best solution. This may require finding all solutions, or if a value for the
best solution is known, it may stop when any best solution is found
Example: Finding the best path for a travelling salesman.
Often, brute force algorithms require exponential time. Various heuristics and optimizations
can be used
Heuristic: A “rule of thumb” that helps you decide which possibilities to look at first
Optimization: In this case, a way to eliminate certain possibilities without fully exploring
them
c. Advantages and disadvantages of brute force algorithm
Advantages of the brute-force algorithm
The advantages of the brute-force algorithms are:
~ 78 ~
If all the possible solutions are listed down, then you can be guaranteed that the brute-
force approach will find the correct solution.
It is applicable to problems that exist in a very wide range of domains.
It is useful for solving small and simple problems.
It is very simple, does not require any domain knowledge, and can be used as
a comparison benchmark.
The brute-force method is very inefficient. For real-time problems, algorithm analysis
quite frequently goes above the O(N!) order of growth.
Finding the right solution by this method consumes a lot of time. These algorithms run
rather slowly.
Instead of using a good algorithm design, these algorithms just rely on compromising the
power of the computer.
These algorithms are neither constructive nor creative in comparison to algorithms that
are constructed through the use of some other design paradigms.
The brute force algorithm searches all the positions in the text between 0 and n-m, whether the
occurrence of the pattern starts there or not. After each attempt, it shifts the pattern to the right by
exactly 1 position. The time complexity of this algorithm is O(m*n). If we are searching for n
characters in a string of m characters, then it will take n*m tries.
~ 79 ~
Example: Suppose a salesman needs to travel 10 different cities in a country, and he wants to
determine the shortest possible routes out of all the possible combinations. Here brute force
algorithm simply calculates the distance between all the cities and selects the shortest one.
b. Basic algorithm
In order candidate for P after the current one c.
The next procedure must also tell when there are no more candidates for the instance P, after the
current one c. A convenient way to do that is to return a "null candidate", some conventional data
value Λ that is distinct from any real candidate. Likewise the first procedure should return Λ if
there are no candidates at all for the instance P. The brute-force method is then expressed by the
algorithm
c ← first(P)
while c ≠ Λ do
if valid(P,c) then
output(P, c)
c ← next(P, c)
end while
For example, when looking for the divisors of an integer n, the instance data P is the number n.
The call first(n) should return the integer 1 if n ≥ 1, or Λ otherwise; the call next(n,c) should
return c + 1 if c < n, and Λ otherwise; and valid(n,c) should return true if and only if c is a
divisor of n. (In fact, if we choose Λ to be n + 1, the tests n ≥ 1 and c < n are unnecessary). The
brute-force search algorithm above will call output for every candidate that is a solution to the
given instance P. The algorithm is easily modified to stop after finding the first solution, or a
specified number of solutions; or after testing a specified number of candidates, or after spending
a given amount of CPU time.
~ 80 ~
Combinatorial explosions occur in some numeric problems when the complexity rapidly
increases, caused by the increasing the number of possible combinations of inputs. As an
example, if you have n constraints or factors for a decision, each with 10 possible options, then
you will have all together 10 to the power n combinations of results. The number of
combinations grows exponentially as n increases.
This explosion in complexity can make some mathematical problems intractable to brute force
solutions. The combinatorial explosion problem prevented computers from competing with
human world champions until suitable heuristic solutions were invented.
The following graph clearly visualizes the problem:
The naive way of solving combinatorial problems can be paraphrased as `generate and test': In a
first step one enumerates all combinations from which one selects all solutions in the second
step. In most cases however, `generate and test' is simply not feasible. This is obvious if the set
of combinations is infinite. But even if it is finite then it is usually very large, i.e. exponentially
large in size of the problem description. In this case, the generation step runs into a combinatorial
explosion (from which it usually returns only several billions of years later).
~ 81 ~
Let's consider the following example of 15 variables V1,...,V15, all taking values in the
domain {1,...,15} and for which we want to find all solutions that satisfy the constraints:
V1 < V2
V2 < V3
...
V14 < V15
Let's see how “Generate and Test” solving technique performs on this example.
The leaves which satisfy the problem's constraint are said to be solutions. Those which violate
this constraint are said to be failures. We will often display a search tree graphically as shown
below, where blue circles represent choice points, red squares failures, and green diamonds
solutions. For convenience, a subtree whose leaves are all failures (resp. solutions) is usually
abbreviated by a red (resp. green) triangle.
~ 82 ~
For our problem, there are 15 variables, each taking one of 15 possible values: this means there
are 15^15 = [Link].859.375 possible assignments. Let's be optimistic and suppose we
have a fast computer able to check 10^9 assignments per second to decide whether each is a
solution or a failure: checking all possibilities would still take approximately 14 years.
For a concrete example, let's consider only 6 variables V1,...,V6 taking values in {1,...,6} and
such that they must satisfy V1 < ... < Vn. The generate and test method produces the following
search tree with 93311 nodes:
~ 83 ~
1.10.4 Speeding up brute-force searches
One way to speed up a brute-force algorithm is to reduce the search space, that is, the set of
candidate solutions, by using heuristics specific to the problem class. For example, in the eight
queens problem the challenge is to place eight queens on a standard chessboard so that no queen
attacks any other. Since each queen can be placed in any of the 64 squares, in principle there are
648 = 281,474,976,710,656 possibilities to consider. However, because the queens are all alike,
and that no two queens can be placed on the same square, the candidates are all possible ways of
choosing of a set of 8 squares from the set all 64 squares; which means 64 choose 8 = 64!/(56!
*8!) = 4,426,165,368 candidate solutions – about 1/60,000 of the previous estimate. Further, no
arrangement with two queens on the same row or the same column can be a solution. Therefore,
we can further restrict the set of candidates to those arrangements.
As this example shows, a little bit of analysis will often lead to dramatic reductions in the
number of candidate solutions, and may turn an intractable problem into a trivial one.
In some cases, the analysis may reduce the candidates to the set of all valid solutions; that is, it
may yield an algorithm that directly enumerates all the desired solutions (or finds one solution,
as appropriate), without wasting time with tests and the generation of invalid candidates. For
example, for the problem "find all integers between 1 and 1,000,000 that are evenly divisible by
417" a naive brute-force solution would generate all integers in the range, testing each of them
for divisibility. However, that problem can be solved much more efficiently by starting with 417
and repeatedly adding 417 until the number exceeds 1,000,000 – which takes only 2398 (=
1,000,000 ÷ 417) steps, and no tests.
~ 84 ~
thereafter is equal to the previous one with 90% probability. If the candidates are enumerated in
increasing order, 1 to 1000, the number t of candidates examined before success will be about 6,
on the average. On the other hand, if the candidates are enumerated in the order
1,11,21,31...991,2,12,22,32 etc., the expected value of t will be only a little more than 2. More
generally, the search space should be enumerated in such a way that the next candidate is most
likely to be valid, given that the previous trials were not. So if the valid solutions are likely to be
"clustered" in some sense, then each new candidate should be as far as possible from the
previous ones, in that same sense. The converse holds, of course, if the solutions are likely to be
spread out more uniformly than expected by chance.
An algorithm that uses random numbers to decide what to do next anywhere in its logic is called
a Randomized Algorithm. For example, in Randomized Quick Sort, we use a random number to
pick the next pivot (or we randomly shuffle the array).
~ 85 ~
Examples:
There are two main types of randomized algorithms: Las Vegas algorithms and Monte-Carlo
algorithms.
Las Vegas algorithms, the algorithm may use the randomness to speed up the computation, but
the algorithm must always return the correct answer to the input.
Monte-Carlo algorithms do not have the former restriction, that is, they are allowed to
give wrong return values. However, returning a wrong return value must have a small
probability, otherwise that Monte-Carlo algorithm would not be of any use.
1.11.3 Derandomization
Derandomization is the process of taking a randomized algorithm and turning it into a
deterministic algorithm. This is useful both for practical reasons (deterministic algorithms are
more predictable, which makes them easier to debug and gives hard guarantees on running time)
and theoretical reasons (if we can derandomize any randomized algorithm we could show
results , which would reduce the number of complexity classes that complexity theorists
otherwise have to deal with).
~ 86 ~
The beginning of an algorithm
The instructions part
The end
1.12.1 Description of algorithm pseudocode
It can be understood as one of the methods that helps in the representation of an
algorithm.
It is a simpler version of coding in a programming language.
It is written in plain English, and uses short phrases to write the functionalities that
specific line of code would do.
There is no specific syntax which is actually present in other programming languages.
This means it can't be executed on a computer.
There are many formats that could be used to write pseudo-codes.
Most of these formats take the structure from languages such as C, LISP, FORTRAN,
and so on.
Pseudocode is not actually a programming language.
Control structures such as 'while', 'if-then-else', 'repeat-until', and so on can be used.
While algorithms are generally written in a natural language or plain English language,
pseudocode is written in a format that is similar to the structure of a high-level programming
language. Program on the other hand allows us to write a code in a particular programming
language.
Algorithm Flowchart
~ 87 ~
2. Algorithm is complex to understand. Flowchart is easy to understand.
6. Algorithm does not follow any rules. Flowchart follows rules to be constructed.
So, as depicted above you can clearly see how the algorithm is used to generate the pseudocode
which is further expanded by following a particular syntax of a programming language to create
the code of the program.
Example: An Algorithm and Pseudocode that add two numbers entered by the User
Algorithm
Step 1: Start
Step 4: Add num1 and num2 and assign the result to sum.
sum←num1+num2
Step 6: Stop
Pseudocode
~ 88 ~
BEGIN
INPUT s1
INPUT s2
sum=s1+s2
OUTPUT sum
END
a) Arithmetic operators
+ Addition
- Subtraction
* Multiplication
/ Division
↑or ̂ Power
b) Comparison operators
= Equal to
! = Not equal to
~ 89 ~
c) Logic operators
AND operator
OR operator
d) Assignment operator
To put a value in a variable we use an assignment operator which has the following symbol: ←
A read function is a function which is used for inputs. It helps to receive the value entered by a
user and assign it to a variable.
Read ()
Example:
Answer:
Var A as Integer
Start
read(A)
End
b) Write function
Write function is used for Outputs; it displays the content of a variable or displays messages.
write ()
Example:
~ 90 ~
Write an algorithm which displays a value stored in a variable.
Answer:
Var B as Integer
Start
B←5
write(B)
End
c. Tests
There are situations in which a set of instructions are executed in one situation and entirely
another set of instructions to be executed in a different situation.
In this kind of situations, a decision control instruction (test) is used. We can define a test as a
structure which controls the flow of instructions of a program during its execution. We can also
define it as a structure which helps us to evaluate a condition.
a) Structure of a test
The structure of a test is made of two main parts: the part which evaluate a condition, and a part
of one instruction or a block of instructions.
If statement
Syntax:
If (condition) then
Instructions
End if
~ 91 ~
The if statement is used to make a decision. The block of instructions following the if executes if
the decision is true, and the block does not execute otherwise.
Example 1:
start
Go
End if
End
Example 2:
start
Stop
End if
End
Each of these statements is conditional. If the condition is true, the instruction following the
condition go in the first example and stop in the second example are executed. In case the
condition evaluates to false nothing is done.
Example 3:
Write an algorithm which receives a number and informs the user when it is positive.
Answer:
Var A as integer
Start
~ 92 ~
Read(A)
If (A>0) then
End if
End
To this question when the condition evaluates true it displays the instruction: the number is
positive but when it evaluates for false it displays nothing.
If…else statement
Syntax
If (condition) then
Instructions
Else
Instructions
End if
End
The if…else statement is used to make a decision and gives the alternative when the condition
evaluates to false. The block of instructions following the if executes if the decision is true, and
the block after else when the condition evaluates to false.
Example 1:
start
Go
else
~ 93 ~
Stop
End if
End
If the condition is true, the instruction following the condition go is executed, when it evaluates
to false the instruction stop which follows else is executes.
Example 2:
Write an algorithm which receives a number and informs the user whether it is positive or
negative.
Answer:
Var A as integer
Start
Read(A)
If (A>0) then
else
End if
End
To this question when the condition evaluates true it displays the message the number is positive
when it evaluates to false it displays the message the number is negative.
Nested Ifs
If statement may be used inside another if statement, in such case we call it a nested if.
~ 94 ~
Example:
Write an algorithm which receives student note and it displays the grade as follows:
Answer:
Start
Read(Note)
If (Note>=16) then
Else
End if
end
~ 95 ~
A multiple choice using switch helps to solve the problem caused by nested if statement in case
there are many conditions to be tested. Switch receives a variable then it evaluates it using
several Cases.
Syntax:
Switch(variable)
Case 1
Instruction
Case 2
Instruction
….
Case n
Instruction
Default
Instruction
End switch
Example:
Write an algorithm which receives note and displays the student’s grade.
14-16: grade B
12-14: grade C
Answer:
~ 96 ~
Var Note as integer
Start
Read(Note)
Switch(Note)
Case 1
break
Case 2
break
Case 3
break
Case 4
Default
~ 97 ~
End switch
End
d. Loops
a) What is a loop?
A loop helps to repeat instruction or block of instructions. It assists in the algorithm where you
want to carry out an activity for a certain number of times.
b) Do while loop
The do-While loop execute its statements at least once even if the condition fails for the first
time.
Syntax
Variable=<start value>
Variable=variable+1
Loop
Example:
Write an algorithm which use do while loop and displays numbers from 1 to 10
Answer:
Var A as integer
Start
A=1
Do while A<=10
~ 98 ~
Write(A)
A=A+1
Loop
End
Do Until Loop means to do something until the condition becomes TRUE. It is like a logical
function that works based on TRUE or FALSE. This is the opposite of the Do While loop where
Do while runs the loops as long as the condition is TRUE.
Syntax:
<Variable>=<start value>
Do
Variable=variable+1
Example:
Write an algorithm which use do loop until and displays numbers from 1 to 10
Answer:
Var A as integer
Start
A=1
Do
Write(A)
A=A+1
~ 99 ~
Loop until A>10
End
d) For loop
The for loop is an iterative loop, it specifies some elements about the loop in one single line.
End which determine whether its value has reached the number of repetitions desired
The value of the loop counter will be increased each time (iteration), and segment within the
loop will be executed.
Syntax:
end for
Example:
Write an algorithm which ask a user to enter a number and it displays the 10 next numbers.
Var I, A as integer
Start
Read(I)
I<-I+1
Write(I)
End for
End
~ 100 ~
e) Loops in Loops
Loops in loops refer to what we call nested loops. These are loops that are such that when one
increment by one the other continues inside the first.
Example:
Var I, J as Integer
start
For I = 1 To 9 do
For J = 1 To I do
Write(J)
End for
End for
End
e. Arrays
a) Use of Arrays
An array is a variable with elements of the same data type. To access the array elements, we use
the array index.
For example, if you need to record notes of 20 students in a given course, you will have to
declare 20 variables as follows:
Var N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11, N12, N13, N14, N15, N16, N17, N18,
N19, N20 as integer
We can use one variable called an array to hold all these numbers.
~ 101 ~
b) Declaring an array
Syntax
Example:
Variable i is used in a loop to move from index 0 of the array to index 19 performing given
instructions on the array
Let us declare an array which will hold five numbers and assign to it 5 numbers of our choice.
Start
N (0) ←8
N (1) ← 10
N (2) ← 12
N (3) ← 6
N (4) ← 5
End
Var i as integer
For i=0 to 4
~ 102 ~
Write N(i)
End for
Var i as integer
Start
N (0) ←8
N (1) ←10
N (2) ←12
N (3) ←6
N (4) ←5
For i=0 to 4 do
Write N(i)
End for
End
In case we are told that the user we enter numbers in array we will use another array for input,
and our algorithm will change as follows:
Var I as integer
Start
For I=0 to 4 do
read N(I)
End for
~ 103 ~
For I=0 to 4 do
Write N(I)
End for
End
d) Arrays dimensions
A dimension is a direction in which you can vary the specification of an array's elements.
One-dimensional array
Two-dimensional array
Three-dimensional array
~ 104 ~
Suppose you want to track sales amounts for every day of the present month. You might declare
a one- dimensional array with 31 elements, one for each day of the month, as the following
example shows.
Now suppose you want to track the same information not only for every day of a month but also
for every month of the year. You might declare a two-dimensional array with 12 rows (for the
months) and 31 columns (for the days), as the following example shows.
Now suppose you decide to have your array hold information for more than one year. If you
want to track sales amounts for 5 years, you could declare a three-dimensional array with 5
layers, 12 rows, and 31 columns, as the following example shows.
a. Flowchart definition
A flowchart is simply a graphical representation of steps. It shows steps in sequential order and
is widely used in presenting the flow of algorithms, workflow or processes. Typically,
a flowchart shows the steps as boxes of various kinds, and their order by connecting them with
arrows.
The process of drawing a flowchart for an algorithm is known as “flowcharting”.
Flowchart is drawn according to defined rules. It is called a flowchart since it charts the flow of
the program. If the flow is in correct order, then program execution turns out to be in proper
order.
~ 105 ~
b. Advantages of using Flowcharts
As we discussed flow chart is used for representing algorithm in pictorial form. This pictorial
representation of a solution/system is having many advantages. These advantages are as follows:
1. Communication: A Flowchart can be used as a better way of communication of the logic of a
system and steps involve in the solution, to all concerned particularly to the client of system.
2. Effective analysis: A flowchart of a problem can be used for effective analysis of the
problem.
3. Documentation of Program/System: Program flowcharts are a vital part of a good program
documentation. Program document is used for various purposes like knowing the components in
the program, complexity of the program etc.
4. Efficient Program Maintenance: Once a program is developed and becomes operational it
needs time to time maintenance. With help of flowchart maintenance become easier.
5. Coding of the Program: Any design of solution of a problem is finally converted into
computer program. Writing code referring the flowchart of the solution become easy.
c. Flowchart Symbols
There are 6 basic symbols commonly used in flowcharting of assembly language Programs:
Terminal, Process, input/output, Decision, Connector and Predefined Process. This is not a
complete list of all the possible flowcharting symbols, it is the ones used most often in the
structure of Assembly language programming.
~ 106 ~
d. General Rules for flowcharting
1. All boxes of the flowchart are connected with Arrows. (Not lines)
2. Flowchart symbols have an entry point on the top of the symbol with no other entry points.
The exit point for all flowchart symbols is on the bottom except for the Decision symbol.
3. The Decision symbol has two exit points; these can be on the sides or the bottom and one side.
4. Generally a flowchart will flow from top to bottom. However, an upward flow can be shown
as long as it does not exceed 3 symbols.
5. Connectors are used to connect breaks in the flowchart. Examples are:
• From one page to another page.
• From the bottom of the page to the top of the same page.
• An upward flow of more than 3 symbols
6. Subroutines and Interrupt programs have their own and independent flowcharts.
7. All flow charts start with a Terminal or Predefined Process (for interrupt programs
or subroutines) symbol.
8. All flowcharts end with a terminal or a contentious loop.
~ 107 ~
Flowcharting uses symbols that have been in use for a number of years to represent the type of
operations and/or processes being performed. The standardized format provides a common
method for people to visualize problems together in the same manner. The use of standardized
symbols makes the flow charts easier to interpret, however, standardizing symbols is not as
important as the sequence of activities that make up the process.
Algorithm Flowchart
6. Algorithm does not follow any rules. Flowchart follows rules to be constructed.
~ 108 ~
Example 2: Draw a flowchart to find the greater number between two numbers and display the
message accordingly.
Example 3: Draw a flowchart to find the largest value of any three numbers
~ 109 ~
Example 4: A flowchart to display numbers from 1 to 10 using a loop
Exercises
~ 110 ~
1. An algorithm and Flowchart that add two numbers, calculate their average and display the
result.
2. Write algorithm and draw a flowchart to find the result of equation:
3. Write an algorithm and draw a flowchart which receives student’s marks and it displays the
grade as follows:
4. Write an algorithm to initialize the variable language by 10 and print the message C in case
of 1, C# in case of 2, C++ in case of 3 otherwise print “Other programming language”. Use
switch statement.
5. An algorithm and flowchart to find the sum of 5 numbers using for loop
6. Write an algorithm and draw a flowchart for program that display the Multiplication table
for a given number N.
~ 111 ~
memory than it is to buy time. If you need more memory — you can rent server space from
providers like Amazon, Google, or Microsoft. You could also buy more computers to add more
memory without renting server space.
How Do We Measure Time Complexity?
A new computer will usually be faster than an old computer, and desktops will usually be faster
than smartphones — so how do we really know the absolute time an algorithm takes?
To measure the absolute time, we consider the number of operations the algorithm performs. The
building blocks of any algorithm are if-statements and loops. They answer the questions: (1)
When should we do operations? (2) How many times should we do them? We want to write code
using as few if-statements and loops as possible for maximum efficiency on any machine.
For analyzing algorithms, we consider the input size n — the number of input items. We want to
make a good guess on how the algorithm’s running time relates to the input size n. This is
the order of growth: how the algorithm will scale and behave given the input size n.
In the above example, when we input 10 items, it takes 10 ms to run. When we input 100 items,
it takes 100 ms — which is good since the growth of our input increased proportionally to the
runtime.
However, on the next step, we input 1,000 items, and it takes 10,000 ms. We’re now taking 10
times longer to run relative to the increase in our input size n. Now we have exponential growth
of our runtime instead of linear growth. To better understand the different orders of growth, we’ll
cover the Big-O notation.
Linear Growth Rate
~ 112 ~
A linear growth rate is a growth rate where the resource needs and the amount of data is
directly proportional to each other. That is the growth rate can be described as a straight line
that is not horizontal.
Exponential Growth Rate
An exponential growth rate is one where each extra unit of data requires a doubling of
resource.
all exponential functions belong to the same order of growth regardless of the base of
the exponent. Exponential functions grow very quickly, so exponential algorithms are only
useful for small problems.
Big O notation
Big O notation is one of the most fundamental tools for computer scientists to analyze the cost
of an algorithm. It is a good practice for software engineers to understand in-depth as well.
Exercises
MAIN MENU
-------------------
PRESS 1 TO CHECK NUMBER
PRESS 2 TO DISPLAY THE SQUARE OF A NUMBER
~ 113 ~
PRESS 3 TO EXIT THE PROGRAM
• If a user presses 1 the program should display where a number inputted from the
keyboard is old or even number.
• If a user presses 2 the program should display the square of a number inputted from the
keyboard.
• If a user presses 3 the program should display “END OF PROGRAM”
• If a user presses any other key, the program should display “TRY AGAIN”
6. Write an Algorithm (using a loop of your choice) to print the numbers from 10 up 30.
7. Write short notes on one types of Algorithm of your choice.
8. Write an Algorithm to initialize an array of 6 integers by the values of your choice then
display the first, second and the third integer (number).
9. Write an algorithm and draw a flowchart for program that display the Multiplication table for
a given number N.
10. Write an algorithm and draw a flowchart to find the sum of 5 numbers using do while loop
~ 114 ~
2.10 Use Functions.
2.11 Manage memory using pointers
2.12 Use structure
2.13 Use file I/O
~ 115 ~
Natural language: spoken by human being or animals
e.g: Swahili, English, French, etc.
Artificial language: used to simplify communication especially in computer
programming.
There’re similarities between those languages. Each language has its own grammatical rules
which must be obeyed in order to write valid programs just as natural language has its own
grammatical rules for forming sentences.
A computer programming language therefore, can be defined as the process of writing and
maintaining step-by-step instructions which can be interpreted by a computing system to perform
a meaningful task.
Computer programming languages are mainly divided into two main categories: Low-level
programming languages and High-level programming language
Low-level programming languages are sometimes divided into two categories: first generation,
and second generation.
The word "low" does not imply that the language is inferior to high-level programming
languages but rather refers to the small or nonexistent amount of abstraction between the
language and machine language; because of this, low-level languages are sometimes described as
being "close to the hardware."
Currently, programmers almost never write programs directly in machine code, because not only
it requires attention to numerous details which a high-level language would handle
automatically, but also it requires memorizing or looking up numerical codes for every
instruction that is used.
For this reason, second generation programming languages abstract the machine code one level.
~ 116 ~
b) Second Generation Language(2GL) called assembly language
It is considered a second-generation language because while it is not a microprocessor's native
language, an assembly language programmer must still understand the microprocessor's unique
architecture (such as its registers and instructions).
These simple instructions are then compiled directly into machine code.
The term "high-level language" does not imply that the language is always superior to low-level
programming languages rather "high-level language" refers to the higher level of abstraction
from machine language.
High-level languages make complex programming simpler while low-level languages tend to
produce more efficient code.
A computer program's source code is the collection of files needed to convert from human-
readable form to some kind of computer-executable form. The source code may be converted
into an executable file (object code) by a compiler.
~ 117 ~
Example of these languages include Perl, PHP, Python, Ruby, SQL, MatLab (Matrix
Laboratory).
5. Fifth Generation Languages:
These are the programming languages that have visual tools to develop a program.
Examples of fifth generation language include Mercury, OPS5, and Prolog.
The first two generations are called low level languages. The next three generations are called
high level languages.
First of all, you must understand a problem, if you don’t know the problem, you can’t find a
solution.
Once you know what the problem is, you can analyze it and make a plan to resolve it.
This is a process whereby a set of instructions are used to produce a solution to a given problem.
-Input instructions: used for supplying data to a program inside the computer.
-Processing instructions: used for manipulating data inside the computer like addition,
multiplication, subtraction...
Another method used to specify small algorithm is called flowchart which consists of symbols
connected by arrows.
This is the next step where you write the codes for the program to make it work.
~ 118 ~
5. Compiling and debugging the program:
Once the program coding is completed, you compile your program means you translate the
source code to object code and if there are errors, you debug them.
6. Running a program:
To run an application means to execute it and check, if using some data, it is working with the
correctness of the program.
7. Documentation:
When those steps are achieved, you could implement your application. You may add the
explanation on how program works and how to use it.
Dev-C++ is a fully featured graphical IDE (Integrated Development Environment) that uses the
MinGw compiler system to create Windows as well as Console based C/C++ applications. It can
also be used with any other GCC-based compiler like Cygwin.
Dev-C++ is free software and is distributed under the GNU General Public License. Thus we can
distribute or modify the IDE freely. It was originally developed by “Bloodshed Software”. It has
been forked by Orwell after it was abandoned by Bloodshed in 2006.
#1) The first step while we start the installer is to select the language of our choice as shown in
the below screenshot
~ 119 ~
#2) Once you select the appropriate language, you have to agree to the license agreement that
pop-ups next.
#3) Next, we are asked to select the components that we need to install as a part of the Dev-C++
installation.
~ 120 ~
As shown in the above screenshot, we are provided with a list of components available for
installation and a checkbox against each component. We can check/uncheck each box to indicate
which components to install. Click next once the components are selected.
#4) Now the installer prompts the user for the destination folder where the Dev-C++
files/libraries etc. are to be copied.
~ 121 ~
Once we provide the destination folder path, click on Install.
#5) The following screenshot shows the progress of the installation.
~ 122 ~
Once the installation is over, a “finish” dialog that signals the end of the installation appears. We
click finish and then we can launch the dev-C++ IDE.
Now let’s see the working of this C++ IDE in detail.
Development Using Dev-C++ IDE
~ 123 ~
Set comment Ctrl + / Cancel comment and repeat again
Copy line Ctrl + E
delete line Ctrl + D
Shift one-tab position to the left overall shift + tab
Move one-tab position to the right as a whole tab
The overall code is indented and aligned Ctrl + Shift + A
Shortcut key setting and viewing: Click "Tools" in the top row, and then click
"Shortcut Key Options".
Every C program contains number of several building blocks known as functions. Each function
of it performs task independently.
~ 124 ~
Include header file section
/* comments */
/* comments*/
Declaration part
Executable part
User-defined functions
C program depends upon some header files for function definition that are used in program. Each
header file by default is extended with .h. The file should be included using # include directive
as given below.
In this example <stdio.h> file is included i.e. all definitions and prototypes of function defined in
this file are available in the current program.
b) Global declaration
This section declares some variables that are used in more than one function. These variables are
known as global variables. This section must be declared outside of all the functions.
~ 125 ~
c) Function main
Every program written in C language must contain main () function. Empty parentheses after
main are necessary. The function main () is a starting point of every ‘C’ program.
The execution of a program always begins with the function main ().
The program execution starts from the opening brace ({) and ends with the closing brace ( }).
Between these two braces the program should declare the declaration and the executable part.
d) Declaration part
The declaration part declares the entire variables that are used in executable part. The
initialization of variables are also done in this section. The initialization means providing initial
values to the variables.
e) Executable part
This part contains the statements following the declaration of the variables. This part part
contains a set of statements or a single statement. These statements are enclosed between the
braces.
f) User-defined function
The functions defined by the user are called user-defined functions. These functions are
generally defined after the main () function. They can also be defined before main () function.
g) Comments
Comments are not necessary in the program. However, to understand the flow of programs the
programmer can include comments in the program. Comments are used for documentation.
Comments are nothing but some kind of statements which are placed between delimiters /* and
*/.
The compiler does not execute comments. Thus, we can say that comments are not the part of
executable programs.
~ 126 ~
1. Using //……………... This is used to write a single line comment.
2. Using /*……………*/: The statements enclosed within /* and */, are used to write multi-
line comments.
Note: When we are working with the multiple line comments then nested comments are not
possible.
Example: First C Program
Let's walk through this program and start to see what different lines are doing:
#include <stdio.h>
main()
{
printf("Hello!");
}
When executed, this program instructs the computer to print out the line "Hello!"
A library is simply a package of code that someone else has written to make your life
easier. The line main() declares the main function. Every C program must have a
function named main somewhere in the code. At run time, program execution starts at
the first line of the main function.
In C, the {and} symbols mark the beginning and end of a block of code.
The printf statement in C allows you to send output to standard out (for us, the screen). The
portion in quotes “Hello!” is called the format string and describes how the data is to be
formatted when printed.
~ 127 ~
2.2.2 Programming rules
A programmer while writing a program should follow the following rules.
1. C is case-sensitive. That is, upper case and lower case characters are different. All statements
should be written in lower case letters. Upper case letters are only used for symbolic
constants.
2. Blank spaces may be inserted between words. This improves the readability of statements.
However, it is not used while declaring a variable, keyword, constant and function.
3. It is not necessary to fix the position of statement in the program i.e. the programmer can
write the statement anywhere between the two braces following the declaration part.
The user can also write one or more statements in one line separating them with a semicolon
(;). Hence, it is often called a free-form language. The following statements are valid.
a=b+c;
d=b*c;
or
a=b+c; d=b*c;
4. The opening and closing braces should be balanced i.e. for example, if opening braces are
four; then closing braces should also be four.
5. All C statements must end with semicolon.
The characters used to form words, numbers and expressions depend upon the computer on
which the program runs. The characters in C are classified in the following categories:
1) Letters
2) Digits
3) White spaces
~ 128 ~
4) Special characters
The whole character set is listed below:
Vertical tab
New line
Form feed
4) Special Characters
, Comma & Ampersand
; Semi-colon * Asterisk
: Colon - Minus
` Apostrophe + Plus
~ Tilde % Percent
$ Dollar = Equal to
~ 129 ~
2.2.4 Delimiters
Language pattern of C uses special kind of symbols, which are called as delimiters.
Delimiters Use
# Hash Preprocessor
However, few C compilers allow constructing variable names which exactly coincide with the
keywords. It is suggested not to mix up keywords with variable names.
~ 130 ~
default goto sizeof volatile
do if static while
2.2.6 Identifiers
Identifiers are names of variables, functions, and arrays. They are user-defined names, consisting
of sequence of letters and digits, with the letter as the first character. Lower case letters are
preferred.
However, the upper case letters are also permitted. The (_) under score symbol can be used as an
identifier. In general, under score is used as a link between two words in long identifiers.
a) #define N 10
b) #define a 15
Here, ‘N’ and ‘a’ are user-defined identifiers.
When a program needs data, it takes the data through the input functions and sends results
obtained through the output functions.
The printf( ) function is used to write information to standard output(normally monitor screen).
\n New line
\b Backspace
\f Form feed
\’ Single quote
\\ Backslash
\0 Null
\t Horizontal Tab
\r Carriage Return
\a Alert
\” Double quote
\v Vertical tab
\? Question Mark
The real power of a technical C program is its ability to interact with the program user. This
means that the program gets input values for variables from users.
The scanf( ) function is a built-in C function that allows a program to get user input from the
keyboard. The structure of this function is
Example of a program : A C program to calculate the average of three numbers supplied using
the keyboard.
~ 132 ~
#include<stdio.h> #include<conio.h>
main()
{
int a,b,c,average;
printf(“Enter the three numbers”);
scanf(“%d %d %d”,&a,&b,&c);
average= (a+ b +c)/3;
printf(“The average=%d”,average);
getch();
a. Definition
They are called Fundamental, primary, build-in or Primitive data type these are the data types
whose variable can hold maximum one value at a time
Example
int a; // valid
a = 10,20,30; // invalid
Following are the examples of some very common data types used in C:
char: The most basic data type in C. It stores a single character and requires a single byte
of memory in almost all compilers.
int: As the name suggests, an int variable is used to store an integer.
float: It is used to store decimal numbers (numbers with floating point value) with single
precision.
double: It is used to store decimal numbers (numbers with floating point value) with
double precision.
b. Derived Data Type
These data type are derived from fundamental data type. Variables of derived data type allow
us to store multiple values of same type in one variable but never allows to store multiple
values of different types. These are the data type whose variable can hold more than one value
of similar type. In C language it can be achieve by array.
Example
~ 134 ~
int a[] = {10,20,30}; // valid
int b[] = {100, 'A', "ABC"}; // invalid
Enumeration is a user defined datatype in C language. It is used to assign names to the integral
constants which makes a program easy to read and maintain. The keyword “enum” is used to
declare an enumeration.
Here is the syntax of enum in C language,
The enum keyword is also used to define the variables of enum type. There are two ways to
define the variables of enum type as follows.
// In both of the below cases, "day" is defined as the variable of type week.
// Or
enum week {Mon, Tue, Wed} day;
The void data type is an empty data type that refers to an object that does not have a value of
any type. Here are the common uses of void data type.
When it is used as a function return type.
Void return type specifies that the function does not return a value.
When it is used as a function's parameter list:
int myFunction(void);
~ 135 ~
Void parameter specifies that the function takes no parameters.
When it is used in the declaration of a pointer variable:
void *ptr;
It specifies that the pointer is "universal" and it can point to anything. When we want to access
data pointed by a void pointer, first we have to type cast it.
2.3.3 Data type modifiers in C
In c language Data Type Modifiers are keywords used to change the properties of current
properties of data type. Data type modifiers are classified into following types.
long
short
unsigned
signed
Modifiers are prefixed with basic data types to modify (either increase or decrease) the amount
of storage space allocated to a variable.
For example, storage space for int data type is 4 bytes for 32-bit processor. We can increase the
range by using long int which is 8 bytes. We can decrease the range by using short int which is
2 bytes.
long:
This can be used to increased size of the current data type to 2 more bytes, which can be
applied on int or double data types. For example, int occupy 2 byte of memory if we use long
with integer variable then it occupies 4 byte of memory.
~ 136 ~
Syntax
short
In general int data type occupies different memory spaces for a different operating system; to
allocate fixed memory space short keyword can be used.
Syntax
short int a; --> occupies 2 bytes of memory space in every operating system.
unsigned
This keyword can be used to make the accepting values of a data type is positive data type.
Syntax
Signed
This keyword accepts both negative or positive value and this is default properties or data type
modifiers for every data type.
Example
Note:
~ 137 ~
1. In real time no need to write signed keyword explicitly for any data type.
2. The storage representation and machine instructions differ from machine to
machine. sizeof operator can use to get the exact size of a type or a variable on a
particular Platform.
~ 138 ~
long e = -21556; // long -ve integer data type
int f = -185; // -ve integer data type
short g = 130; // short +ve integer data type
short h = -130; // short -ve integer data type
double i = 4.1234567890; // double float data type
float j = -3.55; // float data type
}
Example 2:
#include <stdio.h>
#include <limits.h>
int main()
{
printf("Storage size for int is: %d \n", sizeof(int));
printf("Storage size for char is: %d \n", sizeof(char));
return 0;
}
A variable is a data name used for storing a data value. Its value may be changed during the
program execution. The variables value keeps on changing during the execution of a program.
In other words, a variable can be assigned different values at different times during the execution
of a program.
A variable name may be declared based on the meaning of the operation. Some meaningful
variable names are as follows.
1) They must begin with a letter without spaces but underscore is permitted.
~ 139 ~
2) The length of the variable varies from compiler to compiler. Generally most of the compilers
support 8 characters excluding extension. However, the ANSI standard recognizes the
maximum length of a variable up to 31 characters.
3) The variable should not be a C keyword
4) The variable names may be a combination of upper and lower characters. For example suM
and sum are not the same.
5) The variable name should not start with a digit.
c. Declaring variables
The declaration of variables should be done in the declaration part of the program. The variables
must be declared before they are used in the program.
Syntax
Data_typevariable_name;
Example:
int age;
char m;
float s;
double k;
int a,b,c;
d. Initializing variables
~ 140 ~
Variables declared can be assigned or initialized using an assignment operator ‘=’. The
declaration and initialization can also be done in the same line.
Syntax:
Variable_name = constant;
or
Example:
int y=2;
int x=y=z=1; more variables have been initialized with one value.
Note: If no input values are assigned by the user than system will gives a default value called
garbage value.
Garbage value can be any value given by system and that is no way related to correct
programs. This is a disadvantage of C programming language and in C programming it can
overcome using variable initialization.
2.4.2 Key Differences between Identifier and Variable
1. Both an identifier and a variable are the names allotted by users to a particular entity in a
program. The identifier is only used to identify an entity uniquely in a program at the time of
execution whereas, a variable is a name given to a memory location, that is used to hold a
value.
2. Variable is only a kind of identifier, other kinds of identifiers are function names, class
names, structure names, etc. So it can be said that all variables are identifiers whereas, vice
versa is not true.
~ 141 ~
As identifier and variable names are user-defined names, it should be taken care that no two
identifiers or no two variable names in a program should be the same. It will create a problem of
ambiguity in a program.
void function1(){
int x=10;//local variable
static int y=10;//static variable
~ 142 ~
x=x+1;
y=y+1;
printf("%d,%d",x,y);
}
If you call this function many times, the local variable will print the same value for each
function call, e.g, 11,11,11 and so on. But the static variable will print the incremented
value in each function call, e.g. 11, 12, 13 and so on.
d. Automatic Variable
All variables in C that are declared inside the block, are automatic variables by default. We can
explicitly declare an automatic variable using auto keyword.
void main(){
int x=10;//local variable (also automatic)
auto int y=20;//automatic variable
}
e. External Variable
We can share a variable in multiple C source files by using an external variable. To declare an
external variable, you need to use extern keyword.
myfile.h
extern int x=10;//external variable (also global)
program1.c
#include "myfile.h"
#include <stdio.h>
void printValue(){
printf("Global variable: %d", x);
}
~ 143 ~
during the execution of a program. It means that once we assign value to the constant, then we
can’t change it throughout the execution of a program- it stays fixed.
There are several types of constants in C.
Constants
Character Constants
Numeric Constants
1) Integer constants
These are the sequence of numbers from 0 to 9 without decimal points or fractional part or any
other symbols.
Integer constants could either be positive or negative or may be zero. The number without a sign
is assumed as positive.
2) Real constants
Real constants are often known as floating point constants. Integer constants are unfit to
represent many quantities. Many parameters or quantities are defined not only in integers but
also in real numbers. For example, length, height, prize etc. are measured in real numbers.
c. Character Constant
~ 144 ~
1) Single character constants
A character constant is a single character. They are also represented with a single digit or a single
special symbol or white space enclosed within a pair of single quote marks.
Character constants have integer values known as ASCII (American Standard Code for
Information Interchange) values. For example, the statement printf( “%c %d”, 65, ‘B’) will
display the characters ‘A’ and 66.
2) String constants
String constants are sequence of characters enclosed within a double quote marks. The string
may be a combination of all kinds of symbols.
The ‘const’ keyword is used to create a constant of any given datatype in a program. For creating
a constant, we have to prefix the declaration of the variable with the ‘const’ keyword. Here is the
general syntax that we follow when using the ‘const’ keyword:
OR
Example:
~ 145 ~
const int a = 10 ;
#include<stdio.h>
#include<conio.h>
void main(){
int q = 9 ;
const int a = 10 ;
q = 15 ;
a = 100 ; // creates an error
printf(“q = %d\n a = %d”, q, a ) ;
}
The program given above creates an error. It is because we are trying to change the value of the
constant variable (a = 100).
One can also use the ‘#define’ preprocessor directive to create the constants. And when we
create the constants by making use of the preprocessor directive, we must define it in the very
beginning of the program. It is because we must write all the preprocessor directives before the
global declaration.
Here is the syntax that we must use for creating a constant by making use of the ‘#define’
preprocessor directive:
Example:
#define PI 3.14
~ 146 ~
#include<stdio.h>
#include<conio.h>
#define PI 3.14
void main(){
int a, area ;
printf(“Enter the radius of the given circle here : “) ;
scanf(“%d”, &a) ;
area = PI * (a * a) ;
printf(“The area of the circle is = %d”, area) ;
}
An operator indicates an operation to be performed on data that yields a value. Using various
operators in C one can link the variables and constants.
Some operators require two operands, while others act upon only one operand. The operators are
classified into unary, binary and ternary depending on whether they operate on one, two or three
operands respectively.
Types of operators
~ 147 ~
Increment and decrement ++ and --
Assignment operator =
Comma operator ,
Conditional operator ?:
a. Arithmetic operators
These operators are commonly used in most of the computer languages. These arithmetic
operators are used for numerical calculations between two values. They are so called Binary
Arithmetic Operators.
+ Addition 4+8=12
- Subtraction 8-7=1
* Multiplication 6*5=30
/ Division -6/3=-2
b. Relational operators
These operators are used to distinguish between two variables depending on their relations.
These operators provide the relationship between the two expressions. If the relation is true then
it returns a value 1 otherwise 0 for false relation.
~ 148 ~
Relational operators
== Equal to 2= = 3 0
A program to use various relational operators and display their return values
#include <stdio.h>
#include <conio.h>
main()
getch();
Output:
~ 149 ~
Condition: Return Values
10! =10 :0
10==10 :1
10>=10 :1
10<=100 :1
10! =9 :1
c. Logical Operators
Logical operators are used to join two expressions. After checking the conditions, logical true (1)
or false (0) is provided. The operands could be constants, variables and expressions.
The logical AND (&&) operator provides true result when both expressions are true otherwise 0.
The logical OR (||) operator provides true result when one of the expressions is true otherwise 0.
The logical NOT (!) operator provides 0 if the condition is true otherwise 1.
#include <stdio.h>
#include <conio.h>
main()
~ 150 ~
printf(“\n 8>5 || 8<2 : %5d”, 8>5 || 8<2);
getch();
Output:
8>5 || 8<2 : 1
!(8= = 8) : 0
The operator ++ adds one to its operand whereas the operator -- subtracts one from its operand.
Both these operators may either follow or precede the operand. That is, x=x+1; can be
represented as x++ or ++x.
If ++ or -- are used as a suffix to the variable name then the post increased/ decreased operations
take place.
Example: x=20;
y=10;
z=x*y++;
In the above equation the current value of y is used for the product. The
increased by one.
~ 151 ~
If ++ or -- are used as a prefix to the variable name then the pre increased/ decreased operations
take place.
Example: x=20;
y=10;
z=x*++y;
In the above equation the value of y is increased and then used for multiplication. The result is
220 which is assigned to ‘z’.
#include <stdio.h>
#include <conio.h>
main()
int a,z,x=10,y=20;
z=x*y++ ;
a=x*y ;
printf(“\n %d %d”,z,a);
Output:
200 210
#include <stdio.h>
#include <conio.h>
main()
~ 152 ~
{
int a,z,x=10,y=20;
z=x*++y ;
a=x*y ;
printf(“\n %d %d”,z,a);
Output:
210 210
e. Assignment operators
In addition to usual assignment operator =, C has a set of shorthand operators, that simplifies the
coding of a certain type of assignment statement.
It is of the form:
var op = exp;
f. Bitwise operators
~ 153 ~
C supports a set of bitwise operators. The lowest logical element in the memory is bit. C allows
the programmer to interact directly with the hardware of a particular system through bitwise
operators and expression.
These operators work only with int and char datatypes and cannot be used with float and double
type.
The following table shows the bitwise operators that are available in C.
Operator Meaning
~ One’s complement
| Bitwise OR
#include <stdio.h>
#include <conio.h>
main()
int x,y;
scanf(“%d”,&x);
x>>=2;
~ 154 ~
y=x;
Output:
The comma operator is used to separate two or more expressions. The comma operator has the
lowest priority among all the operators.
# include <conio.h>
main()
Output:
Addition =5
Subtraction =1
The conditional operator contains a condition followed by two statements or values. If the
condition is true the first statement is executed otherwise the second statement.
~ 155 ~
The conditional operator ( ? ) and ( : ) are sometimes called ternary operators because they take
three arguments.
Two expressions are separated by a colon. If the condition is true expression1 gets evaluated
otherwise expression2. The condition is always written before question mark ( ?).
#include <stdio.h>
#include <conio.h>
main()
Output:
Result as a value= 5
#include <stdio.h>
#include <conio.h>
main()
clrscr();
3>2?printf(“True”):printf(“False);
Output:
~ 156 ~
True
The size of () gives the bytes occupied by a variable. The number of bytes occupied by a variable
to variable depends upon its data type.
Write a program to use ‘&’ and ‘size of’ operator and determine the size of integer and float
variables.
#include <stdio.h>
#include <conio.h>
main()
float y=4 ;
printf(“\n Sizeof(x)=%d”,sizeof(x));
printf(“\n Sizeof(y)=%d”,sizeof(y));
Output:
Sizeof(x)=2
Sizeof(y)=4
~ 157 ~
2.6.2 Priority of Operations and their Clubbing
Various relational operations have different priorities or precedence. If an arithmetic expression
contains more operators, then the execution will be performed according to their priorities. The
precedence is set for different operators in C.
[] Array expression
Or Square bracket
. Structure operator
- Unary minus
++ Increment
-- Decrement
! Not operator
~ Ones complement
* Pointer operator
/ Division
~ 158 ~
-
Subtraction
!= Inequality
When two operators of the same priority are found in the expression, precedence is given to the
extreme left operator.
Example x= 5 * 4 + 8 / 2;
~ 159 ~
1 2
Here, 5*4 is solved first, * and / have the same priority and the operator * occurs before /.
If there are more sets of parenthesis in the expression, the innermost parenthesis will be solved
first, followed by the second and so on.
Example y= ( 8 / (2 *( 2 * 2) ) );
3 2
~ 160 ~
if (condition) /*no semi-colon*/
statement;
Suppose if it is required to include more than one statement, then a compound statement is used,
in place of single statement.
~ 161 ~
Two numbers are equal
b. The if…..else Statement
The if….else statement takes care of true as well as false conditions. It has two blocks. One
block is for if and it is executed when the condition is true and the other block of else is executed
when the condition is false.
It’s syntax:
if (condition is true)
execute the statement1;
else
execute the statement2;
Example:
Write a program to check whether the year is Leap or Not. Use if…else statement.
#include<stdio.h>
#include<conio.h>
main()
{
clrscr();
int year;
printf("\n Enter year :");
scanf("%d “,&year);
if(year % 4= =0)
printf("\n Leap year.");
else
printf(“\n Not Leap Year.”);
getch();
}
Output
Enter year: 2020
~ 162 ~
Leap year.
~ 163 ~
scanf("%d %d",&initial,&final);
consumed=final-initial;
if(consumed>=200&&consumed<=500)
total=consumed*3.50;
else if(consumed>=100&&consumed<200)
total=consumed*2.50;
else
total=consumed*1.50;
printf("\n Total bill for %d units is %f",consumed,total);
getch();
}
Output:
Enter the initial and final readings: 800 850
Total bill for 50 units is 75.000000
d. The switch statement
The switch statement is a multi-way branch statement. In the program if there is a possibility to
make a choice from a number of options, this structure is useful.
The switch statement requires only one argument of any data type, which is checked with
number of case options. The switch statement evaluates expression and then looks for its value
among the case constants. If the value matches with case constant, this particular case statement
is executed; if not, default is executed.
Switch, case and default are reserved keywords. Every case statement terminates with a colon’:’,
the break statement is used to exit from the current case structure.
~ 164 ~
break;
case constant B:
statement;
break;
…
default:
statement;
}
Example:
Write a program to provide multiple functions such as 1. Addition [Link]
[Link] 4. Division [Link] by using switch() statement.
#include<stdio.h>
#include<conio.h>
main()
{
clrscr();
int a,b,c,ch;
printf("\t ============");
printf("\n\t MENU");
printf("\n\t ============");
printf("\n\t[1] Addition");
printf("\n\t[2] Subtraction");
printf("\n\t[3] Multiplication");
printf("\n\t[4] Division");
printf("\n\t[5] Reminder");
printf("\n\t[0] Exit");
printf("\n\t ============");
printf("\n Enter two numbers:");
~ 165 ~
scanf("%d %d",&a,&b);
printf("\n\n\t Enter your choice:");
scanf("%d",&ch);
if(ch<=5&ch>=0)
switch(ch)
{
case 1:
c=a+b;
printf("\n Addition:%d",c);
break;
case 2:
c=a-b;
printf("\n Subtraction:%d",c);
break;
case 3:
c=a*b;
printf("\n Multiplication:%d",c);
break;
case 4:
c=a/b;
printf("\n Division:%d",c);
break;
case 5:
c=a%b;
printf("\n Reminder:%d",c);
break;
case 0:
printf("\n Terminated by choice");
~ 166 ~
default:
printf("\n Invalid Choice");
}
getch();
}
Output:
============
MENU
============
[1] Addition
[2] Subtraction
[3] Multiplication
[4] Division
[5] Reminder
[0] Exit
============
Enter two numbers: 8 3
Enter your choice: 5
Reminder: 2
A loop is defined as a block of statements which are repeatedly executed for a certain number of
times.
~ 167 ~
for loop
while loop
do-while loop
The for loop is used to repeat the execution statement for some fixed number of times.
statement;
condition is the test expression, which evaluated before each iteration of the loop, which
determines when the loop will exist.
Increment/decrement is the modifier expression, which changes the value of loop control
variable. This expression is executed at the end of each loop.
~ 168 ~
printf(“%d”,a); from 10 to 0
#include <stdio.h>
#include <conio.h>
void main()
int i;
for(i=0;i<=15;i+=2)
printf("%5d",i);
getch();
Output:
0 2 4 6 8 10 12 14
b. While loop.
The while loop is best suited to repeat a statement or a set of statements as long as some
condition is satisfied.
initial expression;
while(conditional-expression)
statement;
~ 169 ~
increment/decrement; }
where the statement (body of the loop) may be a single statement or a compound statements.
The conditional-expression (test condition) must results zero or non-zero.
#include <stdio.h>
#include <conio.h>
void main()
clrscr();
int i=0;
while(i<=15)
printf("%5d",i);
i+=2;
getch();
Output:
2 4 6 8 10 12 14
c. Do while loop.
The while loop is best suited to repeat a statement or a set of statements as long as some
condition is satisfied.
~ 170 ~
initial expression;
do
statement;
increment/decrement;
} while(conditional-expression);
where the statement (body of the loop) may be a single statement or a compound statements.
The conditional-expression (test condition) must results zero or non-zero.
#include <stdio.h>
#include <conio.h>
void main()
int i=0;
do
printf("%5d",i);
i+=2;
while(i<=15);
getch();
Output:
~ 171 ~
0 2 4 6 8 10 12 14
d. Nested loop
C programming allows to use one loop inside another loop. The following shows a few examples
to illustrate the concept.
Syntax:
statement(s);
statement(s);
The syntax for a nested while loop statement in C programming language is as follows
while(condition) {
while(condition) {
statement(s);
} statement(s);
The syntax for a nested do...while loop statement in C programming language is as follows
do {
statement(s);
do {
statement(s);
~ 172 ~
}while( condition );
}while( condition );
A final note on loop nesting is that you can put any type of loop inside any other type of loop.
For example, a 'for' loop can be inside a 'while' loop or vice versa.
Example:
**
***
****
*****
#include<stdio.h>
#include<conio.h>
main()
int x,i,j;
scanf("%d",&x);
for(i=1;i<=x;i++)
for(j=1;j<=i;j++)
~ 173 ~
printf("*");
printf("\n");
getch();
Output:
**
***
****
*****
a) Break statement
~ 174 ~
When a break statement is encountered inside a loop, the loop is immediately
terminated and the program control resumes at the next statement following the
loop.
It can be used to terminate a case in the switch statement (covered in the preceding
chapter).
If you are using nested loops, the break statement will stop the execution of the innermost loop
and start executing the next line of code after the block.
break;
Example:
#include <stdio.h>
main ()
{
int a = 10;
while( a < 20 )
{
printf("value of a: %d\n", a);
a++;
if( a > 15)
{
/* terminate the loop using break statement */
break;
}
}
}
b) Continue statement
~ 175 ~
The continue statement in C programming works somewhat like the break statement. Instead of
forcing termination, it forces the next iteration of the loop to take place, skipping any code in
between.
For the for loop, continue statement causes the conditional test and increment portions of the
loop to execute. For the while and do...while loops, continue statement causes the program
control to pass to the conditional tests.
continue;
Example:
#include <stdio.h>
main () {
int a = 10;
do
{
if( a == 15)
{
/* skip the iteration */
a = a + 1;
continue;
}
printf("value of a: %d\n", a);
a++;
}while( a < 20 ); }
c) Goto statement
A goto statement in C programming provides an unconditional jump from the ‘goto’ to a labeled
statement in the same function.
~ 176 ~
goto label;
.. ..
label:
statement;
Here label can be any plain text except C keyword and it can be set anywhere in the C program
above or below to goto statement.
NOTE: Use of goto statement is highly discouraged in any programming language because it
makes difficult to trace the control flow of a program, making the program hard to understand
and hard to modify. Any program that uses a goto can be rewritten to avoid them.
#include <stdio.h>
main ()
{
int a = 10;
LOOP:
do
{
if( a == 15)
{
a = a + 1;
goto LOOP;
}
printf("value of a: %d\n", a);
a++;
}while( a < 20 ); }
~ 177 ~
An Array is a collection of same data type. The elements of an array are referred by a common
name and are differentiate from one another by their position within an array. The elements of an
array can be of any data type but all elements in an array must be of the same type.
b. Characteristic of an array
The declaration int a[5] is nothing but creation of 5 variables of integer types in the memory.
All the elements of an array share the same name and they are distinguished from one
another with the help of an element number.
The element number in an array plays major role for calling each element.
Any particular element of an array can be modified separately without disturbing other
elements.
int a[5]={1,2,3,4,8};
If a programmer needs to replace 8 with 10, he/she doesn’t require to change all other
numbers expect 8. To carry out this task, the statement a[4]=10 can be used. Here all other
three elements are not disturbed.
Any element of an array a[] can be assigned/equated to another ordinary variable or array of
its type.
For example
b=a[2];
a[2]=a[3];
In the statement b=a[2] or vice versa, value of a[2] is assigned to ‘b’ where b is an
integer.
In the statement a[2]=a[3] or vice versa, value of a[3] is assigned to a[2] where both
statements are of the same array.
The array elements are stored in continuous memory locations. The amount of storage
required for holding elements of the array depends on its type and size. The total size in
bytes for a single dimensional array is computed as shown below.
Single or One Dimensional array is used to represent and store data in a linear form.
Array having more than one subscript variable is called Multi-Dimensional array.
type array_name[size];
where type is a valid datatype, array_name is the name of the array and size is the number of
elements that array_name contains.
Example:
int A[100];
A - name of array
b. Initializing Arrays
Similar to other datatypes, the array also can be initialized at the time of declaration.
~ 179 ~
int num[5] ={3,2,1,5,4};
The individual elements of an array can be referenced by means of its subscript (or index).
In C subscript starts from 0. That is, if we declare an array of size n, then we can refer the
elements from 0 to (n-1)th element.
Example:
A program to compute and display addition of even numbers and product of odd numbers for an
array of 5 numbersof your choice
#include <stdio.h>
#include <conio.h>
main()
{
int n,m[5]={6,8,9,10,12},i,a=0,s=1;
~ 180 ~
for(i=0;i<5;i++)
{
if(m[i]%2= =0)
{
printf("\n Even number:%d",m[i]);
a=a+m[i];
}
else
{
printf("\n Odd number :%d",m[i]);
s=s*m[i];
}
}
printf("\n\n\n Addition of even numbers:%d",a);
printf("\n Product of odd numbers:%d",s);}
2.9.3 Multi Dimensional Array
C programming language allows multidimensional arrays.
type name[size1][size2]...[sizeN];
For example, the following declaration creates a three dimensional integer array
int threedim[5][10][4];
b. Two-dimensional Arrays
~ 181 ~
Type arrayName [ x ][ y ];
Where type can be any valid C data type and arrayName will be a valid C identifier. A two-
dimensional array can be considered as a table which will have x number of rows and y number
of columns. A two-dimensional array a, which contains three rows and four columns can be
shown as follows
Thus, every element in the array a is identified by an element name of the form a[ i ][ j ], where
'a' is the name of the array, and 'i' and 'j' are the subscripts that uniquely identify each element in
'a'.
Multidimensional arrays may be initialized by specifying bracketed values for each row.
Following is an array with 3 rows and each row has 4 columns.
The nested braces, which indicate the intended row, are optional. The following initialization is
equivalent to the previous example −
An element in a two-dimensional array is accessed by using the subscripts, i.e., row index and
column index of the array. For example −
~ 182 ~
The above statement will take the 4th element from the 3rd row of the array. You can verify it in
the above figure. Let us check the following program where we have used a nested loop to
handle a two-dimensional array
#include <stdio.h>
int main () {
int i, j;
return 0;
When the above code is compiled and executed, it produces the following result
a[0][0]: 0
a[0][1]: 0
a[1][0]: 1
a[1][1]: 2
a[2][0]: 2
a[2][1]: 4
~ 183 ~
a[3][0]: 3
a[3][1]: 6
a[4][0]: 4
a[4][1]: 8
As explained above, you can have arrays with any number of dimensions, although it is likely
that most of the arrays you create will be of one or two dimensions.
a. Definition of a function
b. Types of functions
Library functions
User defined functions
A) Library functions
The library functions are pre-defined set of functions. Their task is limited. A user can not
understand the internal working of these functions. The user can only use the functions but can’t
change or modify them.
For example sqrt (81) gives result 9. Here, the user doesn’t need to worry about its source code
but the result should be provided by the function.
~ 184 ~
Example:
#include <stdio.h>
#include <conio.h>
#include <math.h>
main()
{
float x,y;
printf("\n Enter a number:");
scanf("%f",&x);
y=sqrt(x);
printf("\n square root %f is %f ",x,y);
}
Output:
The library functions are created by the designers of compilers. All C standard library functions
are defined inside the different header files saved with the extension .h. We need to include these
header files in our program to make use of the library functions defined in such header files.
For example, to use the library functions such as printf/scanf we need to include stdio.h in our
program which is a header file that contains all the library functions regarding standard
input/output.
The list of mostly used header files is given in the following table.
S Header Description
N file
1 stdio.h This is a standard input/output header file. It contains all the library functions
regarding standard input/output.
~ 185 ~
2 conio.h This is a console input/output header file.
3 string.h It contains all string related library functions like gets(), puts(),etc.
4 stdlib.h This header file contains all the general library functions like malloc(),
calloc(), exit(), etc.
5 math.h This header file contains all the math operations related functions like sqrt(),
pow(), etc.
9 signal.h All the signal handling functions are defined in this header file.
#include <math.h>
~ 186 ~
FunctionName( argument );
Calls function sqrt, which returns the square root of its argument
All math functions return data type double e.g the sqrt function takes an argument of type double
and returns a result of type double
Forgetting to include the math header file when using math library functions can cause strange
results
Example:
#include <stdio.h>
#include <math.h>
int main()
~ 187 ~
printf( "sqrt(%.1f) = %.1f\n", 900.0, sqrt( 900.0 ) );
return 0;
Output:
~ 188 ~
B) User defined functions
These are the functions defined by the user according to his/her requirement. The user can
modify the function according to the requirement. The user can only understand the internal
working of the function. The user has full scope to implement his/her own ideas in the function.
Thus the set of such user-defined functions can be useful to another programmer. One should
include the file in which user-defined functions are stored to call function in the program.
For example: square (9) is a user-defined function that gives the result 81. Here the user knows
the internal working of the square () function as its source code is visible, this is the major
difference between the two types of functions.
argument declaration;
statement1;
~ 189 ~
statement2;
return(value);
b. Working of function:
main()
…………………..
…………………..
abc(x,y,z) Function Call
…………………..
………………….. Actual argument
}
……………………
…………………… Formal argument
return() ; Return value
}
A) Actual argument
The elements of calling functions are actual arguments. The variables ‘x’,’y’ and ‘z’ are actual
arguments.
B) Formal argument
The arguments of called function are formal arguments. The variables ‘l’, ’k’, ’j’ are formal
arguments.
C) Function name
~ 190 ~
A function must follow the same rule as we use for variable naming.
Where sum () is a user defined function and ‘a’ and ‘b’ are integer variable arguments. The
function name must be ended by a semi-colon (;).
D) Argument/parameter list
The argument list means variable names enclosed within the parenthesis. They must be separated
by a comma (,). The formal arguments receive values from the actual argument.
E) Function call.
A compiler executes the function when a semi-colon (;) is followed by function name. A
function can be called simply using its name like other C statement, terminated by semicolon (;).
Example: Write a program to show how user-defined function is called, the function returns one
value.
#include <stdio.h>
#include <conio.h>
main()
{
int add(int a,int b),a=5,b=1,s;
s=add(a,b);
printf("\n Result in calling function");
printf("\n =========================");
printf("\n Sum=%d",s);
printf("\n In calling function;a=%d and b=%d",a,b);
}
add(int x,int y)
{
return (x+y);
}
~ 191 ~
Output:
Recursion is a technique to be used to call itself. In C, it is possible for the functions to call
themselves. A function is called recursive if a statement with in the body of a function calls the
same function itself.
Examples:
#include<stdio.h>
#include<conio.h>
//Function Declaration
void printline();
void main()
{
printline(); // function declaration
printf(“This illustrate the use of C functions”);
printline();
getch();
}
~ 192 ~
void printline() //return type and function name
{
int i;
for (i=1; i<=40;i++)
printf(“- \n”);
}
2. Arguments but no Return values
#include<stdio.h>
#include<conio.h>
void swap(int,int);
void main()
{
int a,b;
printf(“Enter two number”);
scanf(“%d %d”,&a,&b);
swap(a,b);
getch();
}
void swap(int x, int y)
{
int sup ;
sup=x ;
x=y ;
y=sup ;
printf(“After swapping %d and %”, x, y ) ;
}
#include<stdio.h>
~ 193 ~
#include<conio.h>
{
int a,b,max;
printf (“Enter numbers: “) ;
scanf(“ %d %d “,&a,&b);
max = big(a,b);
printf(“\n The biggest number is:%d”,max);
getch();
}
int big(int x, int y)
{
if (x>y)
return x;
else
return y;
}
4. Recursion
#include<stdio.h>
#include<conio.h>
void main()
int n;
scanf %d ,&n);
res = fact(n);
~ 194 ~
printf The factorial is:%ld ,res);
getch();
}
long int fact (int n)
{
long int f;
if(n==1)
return 1;
else
f = n*fact(n-1);
return f;
}
2. 10.4 Important points about functions
All C programs must contain at least one function. [The main() function serves this rule]
A function can return only one value. Thus we should not specify two values to return.
The return type in function declaration is optional. If no return type is specified it is
assumed to be an integer which is default.
When a function is not returning any value, void type can be used as return type.
Parameter list is optional.
C provides a statement return
return expression
Return statement is used in function definition to communicate the return value to the
calling function. Return statement indicates exit from the function and return to the point
from where the function was invoked.
There may be any number of return statements in function definition, but only one return
statement will activate in a function call.
The variable declarations within the function (between braces { }) are local to the
function and are not available outside the function.
~ 195 ~
If there is no return statement, the program will return to the calling point after it reaches
the end of the function body (}).
A function call can be used wherever a variable of same type is used (except the left side
of an assignment statement).
There should be one to one correspondence between the actual and formal parameters in
type, order and number.
C allows recursion. That is a function can call itself.
A C function cannot be defined in another function.
Call by value
Call by reference
a. Call by value
In this type, values of actual arguments are passed to the formal arguments and the operation is
done on formal arguments. Any change made on the formal argument doesn’t affect the actual
arguments because formal arguments are photocopy of actual arguments.
Hence, when function is called by value method, the contents of actual arguments are not being
affected. Changes made on formal arguments are local to the block of the called function. Once
the control returns back to the calling function, the changes made vanish.
A program in which arguments are passed to the function by call by value that returns “No
value”.
#include <stdio.h>
#include <conio.h>
main()
{
int x,y,change(int ,int);
printf("Enter two numbers x and y:");
scanf("%d %d",&x,&y);
change(x,y);
~ 196 ~
printf("\n In Main() X=%d Y=%d",x,y);
return 0;
}
change(int a,int b)
{
int k;
k=a;
a=b;
b=k;
printf("\n In Change() X=%d Y=%d",a,b);
}
Output:
B) Call by reference
In this type, instead of passing values, addresses (reference) are passed. Function operates on
address rather than values. Here, the formal arguments are pointers to the actual arguments. In
this type, formal arguments point to the actual argument; hence, changes made in the arguments
are permanent.
A program in which arguments are passed to the function by call by reference that returns “more
than one value”.
#include <stdio.h>
#include <conio.h>
main()
{
int x,y,add,sub,change(int *,int*, int*,int*);
clrscr();
~ 197 ~
printf("Enter two numbers x and y:");
scanf("%d %d",&x,&y);
change(&x,&y,&add,&sub);
printf("\n Addition :%d",add);
printf("\n Subtraction :%d",sub);
return 0;
}
change(int *a,int *b,int *c,int *d)
{
*c=*a+*b;
*d=*a-*b;
}
Output:
A program in which both call by value and call by reference have been used.
#include <stdio.h>
#include <conio.h>
main()
{
int k,m,other(int,int*);
clrscr();
printf("\n Address of k & m in main():%u %u",&k,&m);
other(k,&m);
return 0;
}
other(int k,int *m)
{
~ 198 ~
printf("\n Address of k & m in other():%u %u",&k,&m);
}
Output:
This method copy original value into function This method copy address of arguments into
as arguments. function as arguments.
Actual and formal arguments will be created Actual and formal arguments will be created
in different memory location in same memory location
The area or block of the C program from where the variable can be accessed is known as the
scope of variable.
The area or scope of the variable depends on its storage class. ie where and how the variable is
declared.
~ 199 ~
Storage area of a variable
Initial value of a variable if not initialized
Scope of a variable
Life of a variable ie how long the variable would be active in the program.
Auto variables are defined inside the function. A variable declared inside the function without
storage class name by default is an auto variable.
The scope of a variable is local to the block in which they are defined.
Once the execution of the function takes place and return turns off the function, the contents and
existence of the auto or local variables vanish.
Auto variables are safety i.e they can not be accessed directly by other functions.
Example:
#include <stdio.h>
#include <conio.h>
main()
{
int add(int a,int b),a=5,b=1,s;
s=add(a,b);
printf("\n Result in calling function");
printf("\n =========================");
printf("\n Sum=%d",s);
printf("\n In calling function;a=%d and b=%d",a,b);
~ 200 ~
}
add(int x,int y)
{
int a=20,b=10;
printf("\n In Called function;a=%d and b=%d",a,b);
return (x+y);
}
Output:
The variable is available to all functions. The variable is called external or global variable and is
declared outside the function body.
In case both external and auto variables are declared with the same name, the first priority is
given to auto variables and the compiler doesn’t allocate memory for these variables.
Example:
#include <stdio.h>
#include <conio.h>
int a=5,b=1;
main()
{
int add(int a,int b);
int subtra(int a,int b);
int s,t;
~ 201 ~
s=add(a,b);
t=subtra(a,b);
printf("\n Result in calling function");
printf("\n =========================");
printf("\n Sum=%d \n Subtraction=%d",s,t);
printf("\n In calling function;a=%d and b=%d",a,b);
}
add(int x,int y)
{
printf("\n In ADDITION Called function;a=%d and b=%d",a,b);
return (x+y);
}
subtra(int x,int y)
{
int a=20,b=10;
printf("\n In SUBTRACTION Called function;a=%d and b=%d",a,b);
return (x-y);
}
Output:
c. Static variable
The static variable may be of an internal or external type, depending upon where it is declared. If
it is declared outside the function of the body it will be static global. In case, it is declared in the
body or block it will be auto variable. When a variable is declared as static its garbage value is
~ 202 ~
removed and initialized to NULL value. The contents stored in these variables remain constant
throughout the program execution.
A static variable is initialized only once; it is never reinitialized. The value of static variable
persists at each call and the last change made in the value of static variable remains throughout
the program execution.
Example:
#include <stdio.h>
#include <conio.h>
main()
{
int x;
static int y;
printf("x=%d & Y=%d",x,y);
}
Output:
d. Register variables
Keyword register tells the compiler that the variable list followed by it, is kept on CPU registers
since register access is faster than the memory access.
If the CPU fails to keep the variables in the CPU registers, in that case the variables are assumed
as auto and stored in the memory. CPU registers are limited in numbers; hence, we can not
declare more variables with register variables. However, compiler automatically converts register
variables to non-variables once the limit is reached. The user can not determine the success or
failure of register variables.
We cannot use register class for all types of variables. The CPU registers in microprocessor are
16 bit registers. The data type float and double need space of more than 16 bits. If we define
~ 203 ~
variable of these data type with register class, no errors will be shown. The compiler treats them
as variable of auto class.
Example:
#include <stdio.h>
#include <conio.h>
main()
{
register int x;
for(x=0;x<=5;x++)
printf("%2d",x);
}
Output:
The inline function can be substituted at the place where the function call is happening. Function
substitution is always compiler choice.
Most of the Inline functions are used for small computations. They are not suitable for large
computing.
An inline function is similar to a normal function. The only difference is that we place a keyword
inline before the function name.
b. Syntax
~ 204 ~
//function definition
c. Example
#include<stdio.h>
return(a*b);
int main(){
int c;
printf("Multiplication:%d\n",c);
return 0;
Output:
Another Example:
#include <stdio.h>
// Inline function in C
static inline int foo()
{
return 2;
~ 205 ~
}
// Driver code
int main()
{
int ret;
// inline function call
ret = foo();
printf("Output is: %d\n", ret);
return 0;}
When a variable gets assigned in a memory in one program, that memory location cannot be
used by another variable or another program. So, C language gives us a technique of allocating
memory to different variables and programs.
There are two types used for allocating memory. These are: static memory allocations and
dynamic memory allocations.
In the static memory allocation technique, allocation of memory is done at compilation time, and
it stays the same throughout the entire run of your program. Neither any changes will be there in
the amount of memory nor any change in the location of memory.
~ 206 ~
In dynamic memory allocation technique, allocation of memory is done at the time of running
the program, and it also has the facility to increase/decrease the memory quantity allocated and
can also release or free the memory as and when not required or used. Reallocation of memory
can also be done when required. So, it is more advantageous, and memory can be managed
efficiently.
In C, it is possible to access and display the address of the memory location variable using &
operator with variable name. The pointer variable is needed to store the memory address of any
variable. The pointer is denoted by (*) asterisk symbol.
a. Definition of a pointer
A pointer is a memory variable that stores a memory address. Pointer can have any name that is
legal for other variable and it is declared in the same fashion like other variables but it is always
denoted by ‘*’ operator.
b. Features of pointers
Execution time with pointer is faster because data is manipulated with the address i.e direct
access to memory location.
The memory is accessed efficiently with pointers. The pointer assigns the memory space and it
also releases. Dynamically memory is allocated.
Pointers are used with data structures. They are useful for representing two-dimensional and
multi-dimensional arrays.
c. Usage of pointer
~ 207 ~
In c language, we can dynamically allocate memory using malloc() and calloc() functions where
the pointer is used.
Pointers in c language are widely used in arrays, functions, and structures. It reduces the code
and improves the performance.
In C, every variable must be declared for its type. Since pointer variables contain addresses that
belong to a separate data type, they must be declared as pointers before we use them.
datatype *pt_name;
This tells the compiler three things about the variable pt_name.
For example:
int *p;
Remember that the type int refers to the data type of the variable being pointed to by p and not
the type of the value of the pointer.
float *x;
b. Initializing pointers
~ 208 ~
Once a pointer variable has been declared, it can be made to point to a variable using an
assignment statement such as
p = &quantity;
which causes p to point to quantity. That is, p now contains the address of quantity.
int x, *p=&x;
is perfectly valid. It declares x as an integer variable and p as a pointer variable and then
initializes p to the address of x. Note carefully that this is an initialization of p, not *p. And
int *p=&x, x;
is not valid.
Once a pointer has been assigned the address of a variable, the question remains as to how to
access the value of the variable using the pointer. This is done by using another unary operator *
(asterisk), usually known as the indirection operator.
quantity = 179;
p = &quantity;
n = *p;
~ 209 ~
The first line declares quantity and n as integer variables and p as a pointer variable pointing to
an integer.
The third line assigns the address of quantity to the pointer variable p.
When the operator * is placed before a pointer variable in an expression, the pointer returns the
value of the variable of which the pointer value is the address.
In this case, *p returns the value of the variable quantity, because p is the address of quantity.
The * can be remembered as value of address. Thus the value of n would be 179.
Example:
int *x;
float *f;
char *y;
In the first statement ‘x’ is an integer pointer and it tells to the compiler that it holds the address
of any integer variable. In the same way ‘f’ is a float pointer that stores the address of any
character variable.
The indirection operator (*) indicates that the value at the memory location stored in the pointer
is to be accessed rather than the address itself.
The’ &’ is the address operator and it represents the address of variable. The %u is used with
printf() function for printing the address of a variable. The address of any variable is a whole
number. The operator ‘&’ immediately preceding the variable returns the address of a variable.
A program to display the value of variable and its location using pointer
#include <stdio.h>
#include <conio.h>
~ 210 ~
main()
int v,*p;
scanf("%d",&v);
p=&v;
getch();
Output:
Explanation
In the above program ‘v’ is an integer variable and its value is 10. The variable ‘p’ is declared as
a pointer variable.
The statement p=&v assigns address of ‘v’ to ‘p’ i.e ‘p’ is the pointer to variable ‘v’. To access
the address and value of ‘v’ pointer ‘p’ can be used. The value of ‘p’ is nothing but the address
of the variable ‘v’.
The pointer variables also have an address and are displayed using ‘&’ operator. The statement
used is (“\n Address of p=%u”,&p);
~ 211 ~
A program to print value of variable using different operator notations.
#include <stdio.h>
#include <conio.h>
main()
clrscr();
int v=10,*p;
p=&v;
getch();
Output:
Arithmetic operations on pointer variables are also possible. Increase, decrease, prefix & postfix
operations can be performed with the help of pointers.
~ 212 ~
A program to show the effect of increment on pointers variables. Display the memory locations
of integer, character, and floating point numbers before and after increment of pointers.
#include <stdio.h>
#include <conio.h>
main()
int x, *x1;
char y, *y1;
float z, *z1;
scanf("%d %c %f",&x,&y,&z);
x1=&x;
y1=&y;
z1=&z;
x1++;
y1++;
z1++;
printf("\n=============================");
~ 213 ~
printf("\n Now address of x=%u",x1);
printf("\n=========");
printf("\n Integer:%d",sizeof(*x1));
printf("\n Character:%d",sizeof(*y1));
printf("\n Float:%d",sizeof(*z1));}
Output:
As we know that, a pointer is used to store the address of a variable in C. Pointer reduces the
access time of a variable. However, In C, we can also define a pointer to store the address of
another pointer. Such pointer is known as a double pointer (pointer to pointer). The first pointer
is used to store the address of a variable whereas the second pointer is used to store the address
of the first pointer. Let's understand it by the diagram given below.
~ 214 ~
The syntax of declaring a double pointer is given below.
#include<stdio.h>
void main ()
int a = 10;
int *p;
int **pp;
printf("value stored at p: %d\n",*p); // value stoted at the address contained by p i.e. 10 will be pr
inted
printf("value stored at pp: %d\n",**pp); // value stored at the address contained by the pointer sto
yred at pp
Output:
address of a: d26a8734
~ 215 ~
address of p: d26a8738
value stored at p: 10
Let's see an example where one pointer points to the address of another pointer.
As you can see in the above figure, p2 contains the address of p (fff2), and p contains the address
of number variable (fff4).
#include<stdio.h>
int main(){
int number=50;
p2=&p;
~ 216 ~
printf("Value of **p2 variable is %d \n",*p);
return 0;
Output:
Value of *p variable is 50
Just like any other argument, pointers can also be passed to a function as an argument. Let’s take
an example to understand how this is done.
In this example, we are passing a pointer to a function. When we pass a pointer as an argument
instead of a variable then the address of the variable is passed instead of the value. So any
change made by the function using the pointer is permanently made at the address of passed
variable. This technique is known as call by reference in C.
Try this same program without pointer, you would find that the bonus amount will not reflect in
the salary, this is because the change made by the function would be done to the local variables
of the function. When we use pointers, the value is changed at the address of variable
#include <stdio.h>
~ 217 ~
*var = *var+b;
int main()
scanf("%d", &salary);
printf("Enter bonus:");
scanf("%d", &bonus);
salaryhike(&salary, bonus);
return 0;
Output:
Enter bonus:2000
When an array is declared, the compiler allocates a base address and sufficient amount of storage
to contain all the elements of the array in contiguous memory locations.
The base address is the location of the first element (index 0) of the array. The compiler also
defines the array name as a constant pointer to the first element.
~ 218 ~
If we declare p as an integer pointer, then we can make the pointer p to point to the array x by the
following assignment:
p = x;
This is equivalent to
p = &x[0];
When handling arrays, instead of using array indexing, we can use pointers to access array
elements.
Note that *(p+3) gives the value of x[3]. The pointer accessing method ismuch faster than array
indexing.
Array name by itself is an address or pointer. It points to the address of the first element (0th
element of an array). The elements of the array together with their addresses can be displayed by
using array name itself. Array elements are always stored in contiguous memory locations.
Write a program to display array element with their addresses using array name as pointer
#include <stdio.h>
#include <conio.h>
main()
intn,i,x[5];
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&x[i]);
~ 219 ~
for(i=0;i<n;i++)
Output:
Dynamic Memory Allocation: Memory allocation done at the time of execution (run time) is
known as dynamic memory allocation. Functions calloc() and malloc() support allocating
dynamic memory. In the Dynamic allocation of memory space is allocated by using these
functions when the value is returned by functions and assigned to pointer variables.
~ 220 ~
before program execution. program execution.
3 It uses stack for managing the static It uses heap for managing the dynamic
allocation of memory allocation of memory
7 In this memory allocation scheme, we This allows reusing the memory. The user
cannot reuse the unused memory. can allocate more memory when required.
Also, the user can release the memory when
the user needs it.
10 In this allocated memory remains from In this allocated memory can be released at
start to end of the program. any time during the program.
Stack memory is allocated during compilation time execution. This is known as static memory
allocation.
Whereas, heap memory is allocated at run-time compilation. This is known as dynamic memory
allocation.
~ 221 ~
2.11.6 Use of Dynamic Memory Allocation
a. Overview
Since C is a structured language, it has some fixed rules for programming. One of them includes
changing the size of an array. An array is a collection of items stored at contiguous memory
locations.
As it can be seen that the length (size) of the array above made is 9. But what if there is a
requirement to change this length (size). For Example,
If there is a situation where only 5 elements are needed to be entered in this array. In this case,
the remaining 4 indices are just wasting memory in this array. So there is a requirement to lessen
the length (size) of the array from 9 to 5.
Take another situation. In this, there is an array of 9 elements with all 9 indices filled. But there
is a need to enter 3 more elements in this array. In this case, 3 indices more are required. So the
length (size) of the array needs to be changed from 9 to 12.
malloc()
calloc()
~ 222 ~
realloc()
free()
Then, the concept of dynamic memory allocation in c language enables the C programmer to
allocate memory at runtime. Dynamic memory allocation in c language is possible by 4
functions of stdlib.h header file.
Now let's have a quick look at the methods used for dynamic memory allocation.
b. malloc() function in C
The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large
block of memory with the specified size. It returns a pointer of type void which can be cast into a
pointer of any form. It doesn’t Initialize memory at execution time so that it has initializes each
block with the default garbage value initially.
ptr=(cast-type*)malloc(byte-size)
For Example:
Example 1:
#include <stdio.h>
#include <stdlib.h>
int main()
int* ptr;
int n, i;
scanf("%d",&n);
~ 224 ~
// Dynamically allocate memory using malloc()
if (ptr == NULL) {
exit(0);
else {
ptr[i] = i + 1;
return 0;
~ 225 ~
Example 2:
#include<stdio.h>
#include<stdlib.h>
int main(){
int n,i,*ptr,sum=0;
scanf("%d",&n);
if(ptr==NULL)
exit(0);
for(i=0;i<n;++i)
scanf("%d",ptr+i);
sum+=*(ptr+i);
~ 226 ~
printf("Sum=%d",sum);
free(ptr);
return 0;
Output:
c. calloc () function in C
Syntax:
here, n is the no. of elements and element-size is the size of each element.
For Example:
float.
~ 227 ~
Example 1:
#include <stdio.h>
#include <stdlib.h>
int main()
int* ptr;
int n, i;
n = 5;
~ 228 ~
if (ptr == NULL) {
exit(0);
else {
ptr[i] = i + 1;
return 0;
~ 229 ~
Example 2:
#include<stdio.h>
#include<stdlib.h>
int main(){
int n,i,*ptr,sum=0;
scanf("%d",&n);
if(ptr==NULL)
exit(0);
for(i=0;i<n;++i)
scanf("%d",ptr+i);
sum+=*(ptr+i);
}
~ 230 ~
printf("Sum=%d",sum);
free(ptr);
return 0;
Output:
d. C free () method
“free” method in C is used to dynamically de-allocate the memory. The memory allocated using
functions malloc() and calloc() is not de-allocated on their own. Hence the free () method is used,
whenever the dynamic memory allocation takes place. It helps to reduce wastage of memory by
freeing it.
Syntax:
free(ptr);
~ 231 ~
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
int n, i;
n = 5;
~ 232 ~
// Dynamically allocate memory using calloc()
exit(0);
else {
free(ptr);
free(ptr1);
return 0;
~ 233 ~
e. C realloc() method
If memory is not sufficient for malloc() or calloc(), you can reallocate the memory by realloc()
function. In short, it changes the memory size.
Syntax:
~ 234 ~
If space is insufficient, allocation fails and returns a NULL pointer.
Example:
#include <stdio.h>
#include <stdlib.h>
int main()
int* ptr;
int n, i;
~ 235 ~
// Get the number of elements for the array
n = 5;
if (ptr == NULL) {
exit(0);
else {
ptr[i] = i + 1;
~ 236 ~
// Print the elements of the array
n = 10;
ptr[i] = i + 1;
~ 237 ~
printf("The elements of the array are: ");
free(ptr);
return 0;
#include <stdio.h>
#include <stdlib.h>
int main()
{
~ 238 ~
int index = 0, i = 0, n,
int ans;
marks = (int*)malloc(sizeof(
// malloc or not?
if (marks == NULL) {
else {
"using malloc\n");
do {
scanf("%d", &ans);
~ 239 ~
if (ans == 1) {
index++;
marks = (int*)realloc(
marks,
(index + 1)
* sizeof(
if (marks == NULL) {
else {
printf(
///beginning address of
///allocated memory
~ 240 ~
}
marks[i]);
free(marks);
return 0;
~ 241 ~
Learning Outcome 2.12: Use structure
2.12.1 Description of structure in C
A variable stores a single value of a data type. Arrays can store many values of similar data type.
In real life, different data types are required for example to maintain employees’ information
such as name, age, salary and so on, all these data types cannot be expressed in a single array.
a. Defining a structure:
Is a collection of one or more variables of different data types, grouped together under a single
name. By using structures, we can make a group of variables, arrays, pointers.
b. Advantages of structures
To copy elements of one array to another array of same data type, elements are copied
one by one whereas in structure, it is possible to copy the contents of all structure
elements.
Nesting of structures is possible.
It is also possible to pass structure elements to a function.
It is also possible to create structure pointers.
struct struct_type
type variable1;
type variable2;
};
Structure declaration starts with struct keyword, struct-type is known as tag or name of
structure. The struct declaration is enclosed within a pair of curly braces. Variable1 and variable2
are variables known as structure members.
The closing brace in the structure type declaration must be followed by a semicolon.
~ 242 ~
It is important to understand that a structure type declaration does not tell the compiler to reserve
any space in memory. All a structure declaration does is, it defines the form of the structure.
Usually structure type declaration appears at the top of the source code file, before any variables
or functions are defined.
Example of a structure:
struct book
char book[30];
int pages;
float price;
};
e. Initialization:
#include <stdio.h>
#include <conio.h>
main()
struct book1
~ 243 ~
char book[30];
int pages;
float price;
};
printf("\n No of pages:%d",[Link]);
Output:
Write a program to display the details of a book by entering the name of a book, the number of
pages and the price.
#include <stdio.h>
#include <conio.h>
main()
struct book1
char book[30];
int pages;
float price;
~ 244 ~
};
scanf("%s",&[Link]);
scanf("%d",&[Link]);
scanf("%f",&[Link]);
printf("\n No of pages:%d",[Link]);
Output:
a. Definition
~ 245 ~
An array of structures in C can be defined as the collection of multiple structures variables where
each variable contains information about different entities. The array of structures in C are used
to store information about multiple entities of different data types. The array of structures is also
known as the collection of structures.
Array Structure
Array element access takes less time Structure elements takes more time than
4
than structures. Array.
Example:
#include <stdio.h>
#include<conio.h>
#include <string.h>
struct student
int id;
char name[30];
float percentage;
~ 246 ~
};
main()
int i;
record[0].id=1;
strcpy(record[0].name, “Gaju");
record[0].percentage = 86.5;
record[1].id=2;
strcpy(record[1].name, “Cyuzuzo");
record[1].percentage = 90.5;
record[2].id=3;
strcpy(record[2].name, “UWITONZE");
record[2].percentage = 81.5;
~ 247 ~
getch();
#include<stdio.h>
struct address
char city[20];
int pin;
char phone[14];
};
struct employee
char name[20];
};
void main ()
~ 248 ~
printf("Enter employee information?\n");
Output:
Arun
Delhi
110001
1234567890
name: Arun
City: Delhi
Pincode: 110001
Phone: 1234567890
a. By separate structure
b. By Embedded structure
a. By Separate structure
Here, we create two structures, but the dependent structure should be used inside the main
structure as a member. Consider the following example.
~ 249 ~
struct Date
int dd;
int mm;
int yyyy;
};
struct Employee
int id;
char name[20];
}emp1;
As you can see, doj (date of joining) is the variable of type Date. Here doj is used as a member in
Employee structure. In this way, we can use Date structure in many structures.
b. By Embedded structure
The embedded structure enables us to declare the structure inside the structure. Hence, it requires
less line of codes but it cannot be used in multiple data structures. Consider the following
example.
struct Employee
int id;
char name[20];
struct Date
~ 250 ~
{
int dd;
int mm;
int yyyy;
}doj;
}emp1;
[Link]
[Link]
[Link]
Example:
#include <stdio.h>
#include <string.h>
struct Employee
int id;
char name[20];
struct Date
int dd;
int mm;
~ 251 ~
int yyyy;
}doj;
}e1;
int main( )
[Link]=101;
[Link]=10;
[Link]=11;
[Link]=2014;
return 0;
Output:
employee id : 101
~ 252 ~
2.12.4 Passing Structure to Function
Just like other variables, a structure can also be passed to a function. We may pass the structure
members into the function or pass the structure variable at once. Consider the following example
to pass the structure variable employee to a function display () which is used to display the
details of an employee.
#include<stdio.h>
struct address
char city[20];
int pin;
char phone[14];
};
struct employee
char name[20];
};
void main ()
~ 253 ~
display(emp);
printf("%s %s %d %s",[Link],[Link],[Link],[Link]);
Output:
It is used to create complex data structures such as linked lists, trees, graphs and so on.
The members of the structure can be accessed using a special operator called as an arrow
operator ( -> )
Structure in C refer to a collection of various data types for example you create a structure
named "Student" which contains his name, roll no, DOB etc. Name is string, Roll no is int.
While pointer refer to address in C & symbol is used to point some particular place in C
memory.
b. Declaration
~ 254 ~
struct tagname *ptr;
c. Accessing
Ptr-> membername;
d. Example in Program
Example 1:
#include<stdio.h>
struct student{
int sno;
char sname[30];
float marks;
};
main ( ){
struct student s;
st = &s;
getch ( );
Output:
Let us run the above program that will produce the following result:
Number = 1
name = Lucky
marks =98.000000
Example 2:
#include<stdio.h>
struct person{
int age;
float weight;
};
int main(){
personPtr = &person1;
scanf("%d", &personPtr->age);
~ 256 ~
printf("Enter weight: ");
scanf("%f", &personPtr->weight);
printf("Displaying:\n");
return 0;
Output:
Let us run the above program that will produce the following result:
Enter age: 45
Enter weight: 60
Displaying:
Age: 45
weight: 60.000000
2.12.6 typedef in C
a. Definition
The typedef is a keyword used in C programming to provide some meaningful names to the
already existing variable in the C program. It behaves similarly as we define the alias for the
commands. In short, we can say that this keyword is used to redefine the name of an already
existing variable.
Syntax of typedef
In the above syntax, 'existing_name' is the name of an already existing variable while 'alias
name' is another name given to the existing variable.
~ 257 ~
For example, suppose we want to create a variable of type unsigned int, then it becomes a
tedious task if we want to declare multiple variables of this type. To overcome the problem, we
use a typedef keyword.
In the above statements, we have declared the unit variable of type unsigned int by using a
typedef keyword.
Now, we can create the variables of type unsigned int by writing the following statement:
unit a, b;
instead of writing:
unsigned int a, b;
Till now, we have observed that the typedef keyword provides a nice shortcut by providing an
alternative name for an already existing variable. This keyword is useful when we are dealing
with the long data type especially, structure declarations.
#include <stdio.h>
int main()
unit i,j;
i=10;
j=20;
printf("Value of i is :%d",i);
printf("\nValue of j is :%d",j);
return 0;
~ 258 ~
}
Output:
Value of i is :10
Value of j is :20
struct student
char name[20];
int age;
};
In the above structure declaration, we have created the variable of student type by writing the
following statement:
The above statement shows the creation of a variable, i.e., s1, but the statement is quite big. To
avoid such a big statement, we use the typedef keyword to create the variable of type student.
struct student
char name[20];
int age;
};
~ 259 ~
stud s1, s2;
In the above statement, we have declared the variable stud of type struct student. Now, we can
use the stud variable in a program to create the variables of type struct student.
char name[20];
int age;
} stud;
stud s1,s2;
From the above declarations, we conclude that typedef keyword reduces the length of the code
and complexity of data types. It also helps in understanding the program.
#include <stdio.h>
char name[20];
int age;
}stud;
int main()
stud s1;
~ 260 ~
printf("\nEnter the name of the student:");
scanf("%s",&[Link]);
scanf("%d",&[Link]);
return 0;
Output:
Applications generally involve tremendous amount of data to be read and written to an auxiliary
[Link] save such information on the disk we use data files.
Files allow us to store information permanently in the disk; access and further it can be altered depending
upon the needs.
a. Definition of a file:
File is a set of records that can be accessed through the set of library functions.
b. Types of files
~ 261 ~
Sequential File Random Access File
Data are kept sequentially Data can be read and modified randomly
For reading the last record, we need to read all For reading the last record, we can directly
the records before that record (the last one). access it.
a. Opening of file
c. Closing file
Opening of a file creates a link between the operating system and the file functions.
FILE *fp;
fp=fopen(“[Link]”,”r”);
The first statement declares the variable fpas a pointer to the data typeFILE. As stated earlier, FILE is a
structure that is defined in the I/O library.
The second statement opens the file named filename and assigns asidentifier to the FILE the pointer fp.
This pointer which contains all theinformation about the file is subsequently used as a communication
link between the system and the program.
~ 262 ~
The second statement also specifies the purpose of opening this file. Themode does this job. Mode can be
one of the following:
r Open for reading. If the file does not exist, fopen() returns NULL.
Open for both reading and If the file does not exist, fopen() returns NULL.
r+
writing.
Open for both reading and If the file exists, its contents are overwritten. If the
w+
writing. file does not exist, it will be created.
Once a file is opened, reading out of or writing to it is accomplished using the standard I/O routines that
are listed.
The functions fprintf() and fscanf() are the file version of printf() and fscanf(). The only difference while
using fprintf() and fscanf() is that, the first argument is a pointer to the structure FILE
#include<stdio.h>
int main()
~ 263 ~
int n;
FILE *fptr;
fptr=fopen("C:\\[Link]","w");
if(fptr==NULL){
printf("Error!");
printf("Enter n: ");
scanf("%d",&n);
fprintf(fptr,"%d",n);
fclose(fptr);
getch();
return0;
This program takes the number from user and stores in file. After you compile and run this program, you
can see a text file [Link] created in C drive of your computer. When you open that file, you can see
the integer you entered.
#include<stdio.h>
int main()
int n;
FILE *fptr;
if((fptr=fopen("C:\\[Link]","r"))==NULL){
~ 264 ~
}
fscanf(fptr,"%d",&n);
printf("Value of n=%d",n);
fclose(fptr);
If you have run program above to write in file successfully, you can get the integer back entered in that
program using this program.
Other functions like fgetchar(), fputc() etc. can be used in similar way.
d. Closing a file
fclose(fptr);
#include <stdio.h>
#include <conio.h>
main()
FILE *fp;
char text[30];
fp=fopen("[Link]","w");
gets(text);
fprintf(fp,"%s",text);
~ 265 ~
fclose(fp);
#include <stdio.h>
#include <conio.h>
main()
FILE *fp;
char text[30];
fp=fopen("[Link]","w+");
gets(text);
fprintf(fp,"%s",text);
printf("\n Name:");
fscanf(fp,"%s",text);
printf("\n%s",text);
fclose(fp);
Exercises
~ 266 ~
1. Write a C program to calculate the Water bill. As per the following rules
Meters Rate
Bellow 100 5%
~ 268 ~
Non-Primitive Data structure
Non-Primitive Data structure is classified into two categories:
Linear data structure: Data structures where data elements are arranged sequentially or
linearly where the elements are attached to its previous and next adjacent in what is called
a linear data structure. In linear data structure, single level is involved. Therefore, we can
traverse all the elements in single run only. Linear data structures are easy to implement
because computer memory is arranged in a linear way. Its examples
are array, stack, queue, linked list, etc.
Non-linear data structure: Data structures where data elements are not arranged sequentially
or linearly are called non-linear data structures. In a non-linear data structure, single level
is not involved. Therefore, we can’t traverse all the elements in single run only. Non-linear
data structures are not easy to implement in comparison to linear data structure. It utilizes
computer memory efficiently in comparison to a linear data structure. Its examples
are trees and graphs.
Example: You might want to store data in
Linear fashion – Array/ Linked List
One on the other – Stacks
Hierarchical Fashion – Trees
Connect nodes – Graph
~ 269 ~
Linear Data structure Non-Linear Data structure
Basic In this structure, the elements are In this structure, the elements are arranged
arranged sequentially or linearly hierarchically or non-linear manner.
and attached to one another.
Types Arrays, linked list, stack, queue Trees and graphs are the types of a non-linear
are the types of a linear data data structure.
structure.
implementation Due to the linear organization, Due to the non-linear organization, they are
they are easy to implement. difficult to implement.
Traversal As linear data structure is a The data items in a non-linear data structure
single level, so it requires a cannot be accessed in a single run. It requires
single run to traverse each data multiple runs to be traversed.
item.
Arrangement Each data item is attached to the Each item is attached to many other items.
previous and next items.
Levels This data structure does not In this, the data elements are arranged in
~ 270 ~
contain any hierarchy, and all the multiple levels.
data elements are organized in a
single level.
Memory In this, the memory utilization is In this, memory is utilized in a very efficient
utilization not efficient. manner.
Time The time complexity of linear The time complexity of non-linear data
complexity data structure increases with the structure often remains same with the increase
increase in the input size. in the input size.
Applications Linear data structures are mainly Non-linear data structures are used in image
used for developing the software. processing and Artificial Intelligence.
c. Major Operations
The major or the common operations that can be performed on the data structures are:
Searching: We can search for any element in a data structure.
Sorting: We can sort the elements of a data structure either in an ascending or
descending order.
Insertion: We can also insert the new element in a data structure.
Updating: We can also update the element, i.e., we can replace the element with another
element.
Deletion: We can also perform the delete operation to remove the element from the data
structure.
d. Factors for Choosing Data Structure
~ 271 ~
sorted order to perform the binary search. The binary search works very fast for the
simple list as it divides the search space into half.
Memory usage: Sometimes, we want a data structure that utilizes less memory.
3.1.2 Searching
Searching is the process of finding some particular element in the list. If the element is present in
the list, then the process is called successful and the process returns the location of that element,
otherwise the search is called unsuccessful.
There are two popular search methods that are widely used in order to search some item into the
list. However, choice of the algorithm depends upon the arrangement of the list.
Linear Search
Binary Search
a. What is a linear search?
A linear search is also known as a sequential search that simply scans each element at a time.
Suppose we want to search an element in an array or list; we simply calculate its length and do
not jump at any item.
Let's consider a simple example.
Suppose we have an array of 10 elements as shown in the below figure:
~ 272 ~
The above figure shows an array of character type having 10 values. If we want to search 'E',
then the searching begins from the 0th element and scans each element until the element, i.e., 'E'
is not found. We cannot directly jump from the 0 th element to the 4th element, i.e., each element is
scanned one by one till the element is not found.
We access list elements, starting with the first, and compare each element with search key. If we
find a match, the search is successful. In this case we return the index of the list element and still
no match, and then the search is unsuccessful.
~ 273 ~
b. Complexity of Linear search
As linear search scans each element one by one until the element is not found. If the number of
elements increases, the number of elements to be scanned is also increased. We can say that
the time taken to search the elements is proportional to the number of elements. Therefore, the
worst-case complexity is O(n)
c. What is a Binary search?
A binary search is a search in which the middle element is calculated to check whether it is
smaller or larger than the element which is to be searched. The main advantage of using binary
search is that it does not scan each element in the list. Instead of scanning each element, it
performs the searching to the half of the list. So, the binary search takes less time to search an
element as compared to a linear search.
The one pre-requisite of binary search is that an array should be in sorted order, whereas the
linear search works on both sorted and unsorted array. The binary search algorithm is based on
the divide and conquer technique, which means that it will divide the array recursively.
There are three cases used in the binary search:
~ 274 ~
Case 1: data<a[mid] then left = mid+1.
Case 2: data>a[mid] then right=mid-1
Case 3: data = a[mid] // element is found
In the above case, 'a' is the name of the array, mid is the index of the element calculated
recursively, data is the element that is to be searched, left denotes the left element of the array
and right denotes the element that occur on the right side of the array.
Let's understand the working of binary search through an example.
Suppose we have an array of 10 size which is indexed from 0 to 9 as shown in the below figure:
We want to search for 70 element from the above array.
Step 1: First, we calculate the middle element of an array. We consider two variables, i.e., left
and right. Initially, left =0 and right=9 as shown in the below figure:
Therefore, mid = 4 and a[mid] = 50. The element to be searched is 70, so a[mid] is not equal to
data. The case 2 is satisfied, i.e., data>a[mid].
~ 275 ~
Step 2: As data>a[mid], so the value of left is incremented by mid+1, i.e., left=mid+1. The value
of mid is 4, so the value of left becomes 5. Now, we have got a subarray as shown in the below
figure:
Now again, the mid-value is calculated by using the above formula, and the value of mid
becomes 7. Now, the mid can be represented as:
In the above figure, we can observe that a[mid]>data, so again, the value of mid will be
calculated in the next step.
Step 3: As a[mid]>data, the value of right is decremented by mid-1. The value of mid is 7, so the
value of right becomes 6. The array can be represented as:
The value of mid will be calculated again. The values of left and right are 5 and 6, respectively.
Therefore, the value of mid is 5. Now the mid can be represented in an array as shown below:
~ 276 ~
In the above figure, we can observe that a[mid]<data.
Step 4: As a[mid]<data, the left value is incremented by mid+1. The value of mid is 5, so the
value of left becomes 6.
Now the value of mid is calculated again by using the formula which we have already discussed.
The values of left and right are 6 and 6 respectively, so the value of mid becomes 6 as shown in
the below figure:
We can observe in the above figure that a[mid]=data. Therefore, the search is completed, and the
element is found successfully.
d. Differences between Linear search and Binary search
~ 277 ~
The following are the differences between linear search and binary search:
Definition The linear search starts searching from It finds the position of the searched
the first element and compares each element by finding the middle element
element with a searched element till the of the array.
element is not found.
Sorted data In a linear search, the elements don't The pre-condition for the binary search
need to be arranged in sorted order. is that the elements must be arranged in
a sorted order.
Implementation The linear search can be implemented The implementation of binary search is
on any linear data structure such as an limited as it can be implemented only
array, linked list, etc. on those data structures that have two-
way traversal.
Approach It is based on the sequential approach. It is based on the divide and conquer
approach.
Size It is preferable for the small-sized data It is preferable for the large-size data
sets. sets.
Efficiency It is less efficient in the case of large- It is more efficient in the case of large-
size data sets. size data sets.
Worst-case In a linear search, the worst- case In a binary search, the worst-case
scenario scenario for finding the element is O(n). scenario for finding the element is
O(log2n).
Best-case In a linear search, the best-case scenario In a binary search, the best-case
scenario for finding the first element in the list is scenario for finding the first element in
~ 278 ~
O(1). the list is O(1).
~ 279 ~
f. Implementation of Binary Search using Recursion
#include<stdio.h>
int binarySearch(int[], int, int, int);
void main ()
{
int arr[10] = {16, 19, 20, 23, 45, 56, 78, 90, 96, 100};
int item, location=-1;
printf("Enter the item which you want to search ");
scanf("%d",&item);
location = binarySearch(arr, 0, 9, item);
if(location != -1)
{
printf("Item found at location %d",location);
}
else
{
printf("Item not found");
}
}
int binarySearch(int a[], int beg, int end, int item)
{
int mid;
if(end >= beg)
{
mid = (beg + end)/2;
if(a[mid] == item)
{
return mid+1;
}
~ 280 ~
else if(a[mid] < item)
{
return binarySearch(a,mid+1,end,item);
}
else
{
return binarySearch(a,beg,mid-1,item);
}
}
return -1;
}
g. Interpolation Search
Interpolation search is an improved variant of binary search. This search algorithm works on the
probing position of the required value. For this algorithm to work properly, the data collection
should be in a sorted form and equally distributed.
Binary search has a huge advantage of time complexity over linear search. Linear search has
worst-case complexity of Ο(n) whereas binary search has Ο(log n).
There are cases where the location of target data may be known in advance. For example, in
case of a telephone directory, if we want to search the telephone number of Mark. Here,
linear search and even binary search will seem slow as we can directly jump to memory
space where the names start from 'M' are stored.
Positioning in Binary Search
In binary search, if the desired data is not found then the rest of the list is divided in two parts,
lower and higher. The search is carried out in either of them.
~ 281 ~
Even when the data is sorted, binary search does not take advantage to probe the position of the
desired data.
Position Probing in Interpolation Search
Interpolation search finds a particular item by computing the probe position. Initially, the probe
position is the position of the middle most item of the collection.
If a match occurs, then the index of the item is returned. To split the list into two parts, we use
the following method −
mid = Lo + ((Hi - Lo) / (A[Hi] - A[Lo])) * (X - A[Lo])
where −
A = list
Lo = Lowest index of the list
Hi = Highest index of the list
A[n] = Value stored at index n in the list
If the middle item is greater than the item, then the probe position is again calculated in the sub-
array to the right of the middle item. Otherwise, the item is searched in the subarray to the left of
the middle item. This process continues on the sub-array as well until the size of subarray
reduces to zero.
Runtime complexity of interpolation search algorithm is Ο(log (log n)) as compared to Ο(log
n) of BST in favorable situations.
~ 282 ~
Algorithm
As it is an improvisation of the existing BST algorithm, we are mentioning the steps to search
the 'target' data value index, using position probing −
Step 1 − Start searching data from middle of the list.
Step 2 − If it is a match, return the index of the item, and exit.
Step 3 − If it is not a match, probe position.
Step 4 − Divide the list using probing formula and find the new middle.
Step 5 − If data is greater than middle, search in higher sub-list.
Step 6 − If data is smaller than middle, search in lower sub-list.
Step 7 − Repeat until match.
Pseudocode
A → Array list
N → Size of A
X → Target Value
Procedure Interpolation_Search()
Set Lo → 0
Set Mid → -1
Set Hi → N-1
if A[Mid] = X
EXIT: Success, Target found at Mid
else
if A[Mid] < X
Set Lo to Mid+1
else if A[Mid] > X
Set Hi to Mid-1
end if
end if
End While
End Procedure
[Link] of interpolation search
~ 283 ~
#include<stdio.h>
#define MAX 10
comparisons++;
// data found
if(list[mid] == data) {
index = mid;
break;
} else {
if(list[mid] < data) {
// if data is larger, data is in upper half
lo = mid + 1;
} else {
// if data is smaller, data is in lower half
hi = mid - 1;
}
}
}
int main() {
//find location of 33
int location = find(33);
~ 284 ~
// if element was found
if(location != -1)
printf("\nElement found at location: %d" ,(location+1));
else
printf("Element not found.");
return 0;
}
You can change the search value and execute the program to test it.
3.1.3 List of Data Structures using C
Array
Linked List
Stack
Queue
Tree
Heap
Hashing
Graph
~ 285 ~
Can access elements randomly using index
Stores homogeneous elements i.e, similar elements
a. Advantages
Random access
Easy sorting and iteration
Replacement of multiple variables
b. Disadvantages
Size is fixed
Difficult to insert and delete
If capacity is more and occupancy less, most of the array gets wasted
Needs contiguous memory to get allocated
#include <stdio.h>
int main() {
//array declaration
int rollNo[10];
//taking inputs
for(int i=0;i<10;i++)
~ 286 ~
scanf("%d",&rollNo[i]);
//printing
for(int i=0;i<10;i++)
printf("%d ",rollNo[i]);
return 0;
}
Input:
12 13 34 56 12 87 56 78 23 10
Output:
12 13 34 56 12 87 56 78 23 10
~ 287 ~
a. Advantages
Dynamic in size.
No wastage as capacity and size is always equal.
Easy insertion and deletion as 1 link manipulation is required.
Efficient memory allocation.
The list is not required to be contiguously present in the memory. The node can reside
anywhere in the memory and linked together to make a list. This achieves optimized
utilization of space.
b. Disadvantages
If the head node is lost, the linked list is lost
No random access possible
c. Why use linked list over array?
Till now, we were using array data structure to organize the group of elements that are to be
stored individually in the memory. However, Array has several advantages and disadvantages
which must be known in order to decide the data structure which will be used throughout the
program.
Array contains following limitations:
1. The size of array must be known in advance before using it in the program.
2. Increasing size of the array is a time taking process. It is almost impossible to expand the
size of the array at run time.
3. All the elements in the array need to be contiguously stored in the memory. Inserting any
element in the array needs shifting of all its predecessors.
Linked list is the data structure which can overcome all the limitations of an array. Using
linked list is useful because,
~ 288 ~
1. It allocates the memory dynamically. All the nodes of linked list are non-contiguously
stored in the memory and linked together with the help of pointers.
2. Sizing is no longer a problem since we do not need to define its size at the time of
declaration. List grows as per the program's demand and limited to the available memory
space.
3.3.2 Applications of stack
Suitable where memory is limited
We can observe in the above figure that there are three different nodes having address 100, 200
and 300 respectively. The first node contains the address of the next node, i.e., 200, the second
node contains the address of the last node, i.e., 300, and the third node contains the NULL value
~ 289 ~
in its address part as it does not point to any node. The pointer that holds the address of the initial
node is known as a head pointer.
The linked list, which is shown in the above diagram, is known as a singly linked list as it
contains only a single link. In this list, only forward traversal is possible; we cannot traverse in
the backward direction as it has only one link in the list.
Representation of the node in a singly linked list
struct node
{
int data;
struct node *next;
}
In the above representation, we have defined a user-defined structure named a node containing
two members, the first one is data of integer type, and the other one is the pointer (next) of the
node type.
To know more about a singly linked list, click on the link given below:
[Link]
b. Doubly linked list
As the name suggests, the doubly linked list contains two pointers. We can define the doubly
linked list as a linear data structure with three parts: the data part and the other two address part.
In other words, a doubly linked list is a list that has three parts in a single node, includes one data
part, a pointer to its previous node, and a pointer to the next node.
Suppose we have three nodes, and the address of these nodes are 100, 200 and 300, respectively.
The representation of these nodes in a doubly-linked list is shown below:
~ 290 ~
As we can observe in the above figure, the node in a doubly-linked list has two address parts;
one part stores the address of the next while the other part of the node stores the previous node's
address. The initial node in the doubly linked list has the NULL value in the address part, which
provides the address of the previous node.
Representation of the node in a doubly linked list
struct node
{
int data;
struct node *next;
struct node *prev;
}
In the above representation, we have defined a user-defined structure named a node with three
members, one is data of integer type, and the other two are the pointers, i.e., next and prev of
the node type. The next pointer variable holds the address of the next node, and the prev
pointer holds the address of the previous node. The type of both the pointers, i.e., next and
prev is struct node as both the pointers are storing the address of the node of the struct
node type.
To know more about doubly linked list, click on the link given below:
[Link]
c. Circular linked list
A circular linked list is a variation of a singly linked list. The only difference between the singly
linked list and a circular linked list is that the last node does not point to any node in a singly
linked list, so its link part contains a NULL value. On the other hand, the circular linked list is a
list in which the last node connects to the first node, so the link part of the last node holds the
first node's address. The circular linked list has no starting and ending node. We can traverse in
any direction, i.e., either backward or forward. The diagrammatic representation of the circular
linked list is shown below:
struct node
{
int data;
struct node *next;
~ 291 ~
}
A circular linked list is a sequence of elements in which each node has a link to the next node,
and the last node is having a link to the first node. The representation of the circular linked list
will be similar to the singly linked list, as shown below:
To know more about the circular linked list, click on the link given below:
[Link]
Doubly Circular linked list
The doubly circular linked list has the features of both the circular linked list and doubly linked
list.
The above figure shows the representation of the doubly circular linked list in which the last
node is attached to the first node and thus creates a circle. It is a doubly linked list also because
each node holds the address of the previous node also. The main difference between the doubly
linked list and doubly circular linked list is that the doubly circular linked list does not contain
the NULL value in the previous field of the node. As the doubly circular linked contains three
~ 292 ~
parts, i.e., two address parts and one data part so its representation is similar to the doubly linked
list.
struct node
{
int data;
struct node *next;
struct node *prev;
}
To know more about the doubly circular linked list, click on the link given below:
[Link]
3.3.4 Basic Operations on Linked List
Traversal: To traverse all the nodes one after another.
~ 293 ~
new_node->data = new_data;
new_node->next = (*head_ref);
// Move head to new node
(*head_ref) = new_node;
}
// Insert a node after a node
void insertAfter(struct Node* prev_node, int new_data) {
if (prev_node == NULL) {
printf("the given previous node cannot be NULL");
return;
}
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = prev_node->next;
prev_node->next = new_node;
}
// Insert the the end
void insertAtEnd(struct Node** head_ref, int new_data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = *head_ref; /* used in step 5*/
new_node->data = new_data;
new_node->next = NULL;
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
while (last->next != NULL) last = last->next;
last->next = new_node;
return;
}
// Delete a node
~ 294 ~
void deleteNode(struct Node** head_ref, int key) {
struct Node *temp = *head_ref, *prev;
if (temp != NULL && temp->data == key) {
*head_ref = temp->next;
free(temp);
return;
}
// Find the key to be deleted
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
// If the key is not present
if (temp == NULL) return;
// Remove the node
prev->next = temp->next;
free(temp);
}
// Search a node
int searchNode(struct Node** head_ref, int key) {
struct Node* current = *head_ref;
while (current != NULL) {
if (current->data == key) return 1;
current = current->next;
}
return 0;
}
// Sort the linked list
void sortLinkedList(struct Node** head_ref) {
struct Node *current = *head_ref, *index = NULL;
int temp;
~ 295 ~
if (head_ref == NULL) {
return;
} else {
while (current != NULL) {
// index points to the node next to current
index = current->next;
while (index != NULL) {
if (current->data > index->data) {
temp = current->data;
current->data = index->data;
index->data = temp;
}
index = index->next;
}
current = current->next;
}
}
}
// Print the linked list
void printList(struct Node* node) {
while (node != NULL) {
printf(" %d ", node->data);
node = node->next;
}
}
// Driver program
int main() {
struct Node* head = NULL;
insertAtEnd(&head, 1);
insertAtBeginning(&head, 2);
insertAtBeginning(&head, 3);
~ 296 ~
insertAtEnd(&head, 4);
insertAfter(head->next, 5);
int item_to_find = 3;
if (searchNode(&head, item_to_find)) {
printf("\n%d is found", item_to_find);
} else {
printf("\n%d is not found", item_to_find);
}
sortLinkedList(&head);
printf("\nSorted List: ");
printList(head);
}
3.3.5 Demonstration of Linked List in a C program
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct node
{int data;
struct node *next;
}*p,*tmp,*tmp1;
void insert_end(int);
void insert_beg(int);
void ldelete(int);
~ 297 ~
void display();
void main()
{
int val,n;
p=NULL;
do
{printf("\n************************* MENU ************************");
printf("\[Link] AT END");
printf("\[Link] AT BEG");
printf("\[Link] A PARTICULAR ELE");
printf("\[Link] FROM BEG");
printf("\[Link] FROM END");
printf("\[Link]");
printf("\[Link]");
printf("\n enter ur choice : ");
scanf("%d",&n);
switch(n)
{case 1: printf("\nenter the value ");
scanf("%d",&val);
insert_end(val);
break;
case 2: printf("\nenter the value");
scanf("%d",&val);
insert_beg(val);
break;
case 3: printf("\nenter the value");
scanf("%d",&val);
l_delete(val);
break;
case 4:
delete_beg();
~ 298 ~
break;
case 5:
delete_end();
break;
case 6: display();
break;
case 7: exit(0);
break;
default: printf("\n Wrong Choice!");
break;
}
printf("\n do u want to cont... ");
}while('y'==getch());
}
void insert_end(int ele)
{
tmp=p;
tmp1=(struct node*)malloc(sizeof(struct node));
tmp1->data=ele;
tmp1->next=NULL;
if(p==NULL)
p=tmp1;
else
{
while(tmp->next!=NULL)
tmp=tmp->next;
tmp->next=tmp1;
}
}
void insert_beg(int ele)
{
~ 299 ~
tmp=p;
tmp1=(struct node*)malloc(sizeof(struct node));
tmp1->data=ele;
tmp1->next=p;
p=tmp1;
}
~ 300 ~
void delete_beg()
{
tmp=p;
if(p==NULL)
printf("\n no element to be deleted!! ");
else
{
printf("\nelement deleted - %d", p->data);
p=p->next;
}
void delete_end()
{
tmp=p;
struct node* pre;
if(p==NULL)
printf("\n no element to be deleted!! ");
else if(p->next==NULL)
{
printf("\nelement deleted - %d", p->data);
p=NULL;
}
else
{
while(tmp->next!=NULL){
pre=tmp;
tmp=tmp->next;
}
~ 301 ~
pre->next=NULL;
printf("\nelement deleted - %d", tmp->data);
}
}
void display()
{
tmp=p;
while(tmp!=NULL)
{printf("\n %d",tmp->data);
tmp=tmp->next;
}
}
Output:
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter your choice : 1
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
~ 302 ~
[Link] FROM END
[Link]
[Link]
enter ur choice :
1
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice :
2
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
~ 303 ~
enter ur choice :
2
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice :
6
90
67
23
12
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice : 3
~ 304 ~
enter the value67
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice :
6
90
23
12
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice :
4
element deleted - 90
~ 305 ~
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice : 5
element deleted - 12
do u want to cont...
************************* MENU ************************
[Link] AT END
[Link] AT BEG
[Link] A PARTICULAR ELE
[Link] FROM BEG
[Link] FROM END
[Link]
[Link]
enter ur choice : 6
23
do u want to cont...
~ 306 ~
It is a type of Linear Data Structures using C
Follows LIFO: Last In First Out
Only the top elements are available to be accessed
Insertion and deletion takes place from the top
Eg: a stack of plates, chairs, Undo/Redo stacks in Excel or Word, etc.
a. Working of Stack in C
A stack is a container of objects that are inserted and removed according to the Last-in First-
Out (LIFO) principle. Both operation insert and deletion perform in stack only from TOP.
b. Real life example of stack
A most popular example of stack is plates in marriage party. Fresh plates are pushed onto to
the top and popped from the top.
c. Stack Representation
~ 307 ~
A stack can be implemented by means of Array, Structure, Pointer, and Linked List. Stack can
either be a fixed size one or it may have a sense of dynamic resizing. Here, we are going to
implement stack using arrays, which makes it a fixed size stack implementation.
d. Advantages
Parsing
Editors
Expression evaluation
Backtracking (game playing, finding paths, exhaustive searching)
Memory management, run-time environment for nested language features.
~ 308 ~
To reverse a word. You push a given word to stack - letter by letter - and then pop
letters from the stack.
An "undo" mechanism in text editors; this operation is accomplished by keeping all
text changes in a stack.
space for parameters and local variables is created internally using a stack.
compiler's syntax check for matching braces is implemented by using stack.
Back/Forward on browsers are perform using stacks.
3.4.3 Operations on Stack
push() – used to insert element at top
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct stackk {
int top;
unsigned size;
int* array;
};
struct stackk* create(unsigned size)
{
struct stackk* stackk = (struct stackk*)malloc(sizeof(struct stackk));
stackk->size = size;
stackk->top = -1;
stackk->array = (int*)malloc(stackk->size * sizeof(int));
~ 309 ~
return stackk;
}
~ 310 ~
int main()
{
int val,n;
struct stackk* stackk = create(100);
do
{printf("\n************************* MENU ************************");
printf("\[Link]");
printf("\[Link]");
printf("\[Link]");
printf("\n4 IS EMPTY");
printf("\[Link]");
printf("\n enter ur choice : ");
scanf("%d",&n);
switch(n)
{
case 1:
printf("\nenter the value ");
scanf("%d",&val);
push(stackk , val);
break;
case 2:
printf("\n popped element : %d",pop(stackk));
break;
case 3:
printf("\n top element : %d",peek(stackk));
break;
case 4: printf("\n is empty : %d",isEmpty(stackk));
break;
case 5: exit(0);
break;
~ 311 ~
default: printf("\n Wrong Choice!");
break;
}
printf("\n do u want to cont... ");
}while('y'==getch());
}
Output:
************************* MENU ************************
[Link]
[Link]
[Link]
4 IS EMPTY
[Link]
enter ur choice :
1
enter the value
45
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link]
4 IS EMPTY
[Link]
enter ur choice :
1
enter the value
56
do u want to cont...
************************* MENU ************************
~ 312 ~
[Link]
[Link]
[Link]
4 IS EMPTY
[Link]
enter ur choice :
3
top element : 56
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link]
4 IS EMPTY
[Link]
enter ur choice :
4
is empty : 0
do u want to cont...
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct node
{int data;
struct node *next;
}*p,*tmp,*tmp1,*end;
void insert_end(int);
void display();
~ 313 ~
void delete_end();
void isEmpty();
int main()
{
int val,n;
p=NULL;
do
{printf("\n************************* MENU ************************");
printf("\[Link]");
printf("\[Link]");
printf("\n3 IS EMPTY");
printf("\[Link]");
printf("\[Link]");
printf("\nenter ur choice : ");
scanf("%d",&n);
switch(n)
{
case 1:
printf("\nenter the value ");
scanf("%d",&val);
insert_end(val);
break;
case 2:
delete_end();
break;
case 3:
isEmpty();
break;
case 4: display();
break;
case 5: exit(0);
~ 314 ~
break;
default: printf("\n Wrong Choice!");
break;
}
printf("\ndo u want to cont... ");
}while('y'==getch());
}
void insert_end(int ele)
{
tmp=p;
tmp1=(struct node*)malloc(sizeof(struct node));
tmp1->data=ele;
tmp1->next=NULL;
if(p==NULL)
p=tmp1;
else
{
while(tmp->next!=NULL)
tmp=tmp->next;
tmp->next=tmp1;
}
end=tmp1;
}
void delete_end()
{
tmp=p;
struct node* pre;
if(p==NULL)
printf("\n no element to be deleted!! ");
else if(p->next==NULL)
{
~ 315 ~
printf("\nelement deleted - %d", p->data);
p=NULL;
end=NULL;
}
else
{
while(tmp->next!=NULL){
pre=tmp;
tmp=tmp->next;
}
pre->next=NULL;
end=pre;
printf("\nelement deleted - %d", tmp->data);
}
}
void isEmpty(){
if(p==NULL)
printf("Stack is Empty");
else
{
printf("Stack is Not Empty");
}
}
void display()
{
tmp=p;
while(tmp!=NULL)
{printf("\n %d",tmp->data);
tmp=tmp->next;
}
}
~ 316 ~
Output
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
enter ur choice : 1
do u want to cont...
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
enter ur choice :
1
do u want to cont...
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
enter ur choice :
~ 317 ~
3
Stack is Not Empty
do u want to cont...
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
enter ur choice :
4
56
67
do u want to cont...
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
enter ur choice : 2
element deleted - 67
do u want to cont...
************************* MENU ************************
[Link]
[Link]
3 IS EMPTY
[Link]
[Link]
~ 318 ~
enter ur choice : 4
56
do u want to cont...
Queue is work on the principal of First-In-First-Out (FIFO), it means first entered item remove
first. Queue have two end front and rear, from front you can insert element and from rear you can
delete element.
~ 319 ~
b. Real life example of stack
A common example of queue is movie theater ticket counter, there first person who stand in
front of ticket window take ticket first and remove from line and new person always stand in line
from end.
Ticket Counter: First person get ticket first and go out first
~ 320 ~
Queue of packets in data communication.
Queue of air planes waiting for landing instructions.
d. Queue Representation
As we now understand that in queue, we access both ends for different reasons. The following
diagram given below tries to explain queue representation as data structure −
As in stacks, a queue can also be implemented using Arrays, Linked-lists, Pointers and
Structures. For the sake of simplicity, we shall implement queues using one-dimensional array.
e. Advantages
Scheduling
Maintaining playlist
Interrupt handling
3.5.2 Operations on Queue
enqueue() – used to insert element at top
~ 321 ~
struct que
{
int front, rear, size;
unsigned actualSize;
int* arr;
};
struct que* createque(unsigned actualSize)
{
struct que* que = (struct que*) malloc(sizeof(struct que));
que->actualSize = actualSize;
que->front = que->size = 0;
que->rear = actualSize - 1;
que->arr = (int*) malloc(que->actualSize * sizeof(int));
return que;
}
int isFull(struct que* que)
{ return (que->size == que->actualSize); }
~ 322 ~
{
if (isEmpty(que))
return INT_MIN;
int item = que->arr[que->front];
que->front = (que->front + 1)%que->actualSize;
que->size = que->size - 1;
return item;
}
int front(struct que* que)
{
if (isEmpty(que))
return INT_MIN;
return que->arr[que->front];
}
int rear(struct que* que)
{
if (isEmpty(que))
return INT_MIN;
return que->arr[que->rear];
}
int main()
{
int val,n;
struct que* que = createque(1000);
do
{printf("\n************************* MENU ************************");
printf("\[Link]");
printf("\[Link]");
printf("\[Link] EMPTY");
printf("\[Link] FULL");
~ 323 ~
printf("\[Link] ELE");
printf("\[Link] ELE");
printf("\[Link]");
printf("\nenter ur choice : ");
scanf("%d",&n);
switch(n)
{case 1: printf("\nenter the value ");
scanf("%d",&val);
enqueue(que,val);
break;
case 2:
dequeue(que);
break;
case 3:
printf("\nIsEmpty : %d",isEmpty(que));
break;
case 4:
printf("\nIsFull : %d",isFull(que));
break;
case 5:
printf("\nFront element: %d",front(que));
break;
case 6:
printf("\nLast element : %d", rear(que));
break;
case 7: exit(0);
break;
default: printf("\n Wrong Choice!");
break;
}
printf("\ndo u want to cont... ");
~ 324 ~
}while('y'==getch());
}
Output:
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
[Link] ELE
[Link] ELE
[Link]
enter ur choice : 1
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
[Link] ELE
[Link] ELE
[Link]
enter ur choice :
1
~ 325 ~
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
[Link] ELE
[Link] ELE
[Link]
enter ur choice : 3
IsEmpty : 0
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
[Link] ELE
[Link] ELE
[Link]
enter ur choice :
4
IsFull : 0
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
~ 326 ~
[Link] ELE
[Link] ELE
[Link]
enter ur choice : 5
Front element: 23
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link] FULL
[Link] ELE
[Link] ELE
[Link]
enter ur choice : 6
Last element : 45
do u want to cont...
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct node
{int data;
struct node *next;
}*p,*tmp,*tmp1;
void insert_end(int);
void delete_beg();
~ 327 ~
void display();
void isEmpty();
int main()
{
int val,n;
p=NULL;
do
{printf("\n************************* MENU ************************");
printf("\[Link]");
printf("\[Link]");
printf("\[Link] EMPTY");
printf("\[Link]");
printf("\[Link]");
printf("\nenter ur choice : ");
scanf("%d",&n);
switch(n)
{case 1: printf("\nenter the value ");
scanf("%d",&val);
insert_end(val);
break;
case 2:
delete_beg();
break;
case 3:
isEmpty();
break;
case 4: display();
break;
case 5: exit(0);
break;
default: printf("\n Wrong Choice!");
~ 328 ~
break;
}
printf("\ndo u want to cont... ");
}while('y'==getch());
~ 329 ~
void isEmpty(){
if(p==NULL)
printf("Queue is Empty");
else
{
printf("Queue is Not Empty");
}
}
~ 330 ~
}
}
printf("\n no match found!! ");
}
void delete_beg()
{
tmp=p;
if(p==NULL)
printf("\n no element to be deleted!! ");
else
{
printf("\nelement deleted - %d", p->data);
p=p->next;
}
void delete_end()
{
tmp=p;
struct node* pre;
if(p==NULL)
printf("\n no element to be deleted!! ");
else if(p->next==NULL)
{
printf("\nelement deleted - %d", p->data);
p=NULL;
~ 331 ~
else
{
while(tmp->next!=NULL){
pre=tmp;
tmp=tmp->next;
}
pre->next=NULL;
printf("\nelement deleted - %d", tmp->data);
}
}
void display()
{
tmp=p;
while(tmp!=NULL)
{printf("\n %d",tmp->data);
tmp=tmp->next;
}
}
Output
~ 332 ~
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link]
[Link]
enter ur choice :
1
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link]
[Link]
enter ur choice : 4
45
67
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link]
[Link]
~ 333 ~
enter ur choice : 3
Queue is Not Empty
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link]
[Link]
enter ur choice : 2
element deleted - 45
do u want to cont...
************************* MENU ************************
[Link]
[Link]
[Link] EMPTY
[Link]
[Link]
enter ur choice : 4
67
do u want to cont...
~ 334 ~
Recursively, we find that a tree is a collection of n nodes, one of which is the root, and n-1
edges. That there are n-1 and every node except the root has one parent.
A tree is a collection of nodes. A tree consists of a distinguished node r, called the root, and
zero or more (sub) trees T1, T2… TK, each of whose roots are connected by a directed edge to r.
the root of each subtree is said to be a child of r, and r is the parent of each subtree root. Every
node except the root has one parent. Nodes with no children are known as leaves. Nodes
with the same parent are siblings. Grandparents are grandchild relations can be defined in a
similar manner.
A tree is also one of the data structures that represent hierarchical data. Suppose we want to show
the employees and their positions in the hierarchical form then it can be represented as shown
below:
A Tree is a non-linear data structure that consists of various linked nodes. It has a hierarchical
tree structure that forms a parent-child relationship. The diagrammatic representation of
a tree data structure is shown below:
~ 335 ~
For example, the posts of employees are arranged in a tree data structure like managers, officers,
clerk. In the above figure, A represents a manager, B and C represent the officers, and other
nodes represent the clerks.
3.6.2 General tree
The general tree is one of the types of tree data structure. In the general tree, a node can have
either 0 or maximum n number of nodes. There is no restriction imposed on the degree of the
node (the number of nodes that a node can contain). The topmost node in a general tree is known
as a root node. The children of the parent node are known as subtrees.
~ 336 ~
The above tree shows the organization hierarchy of some company. In the above
structure, john is the CEO of the company, and John has two direct reports named
as Steve and Rohan. Steve has three direct reports named Lee, Bob, Ella where Steve is a
manager. Bob has two direct reports named Sal and Emma. Emma has two direct reports
named Tom and Raj. Tom has one direct report named Bill. This particular logical structure is
known as a Tree. Its structure is similar to the real tree, so it is named a Tree. In this structure,
~ 337 ~
the root is at the top, and its branches are moving in a downward direction. Therefore, we can
say that the Tree data structure is an efficient way of storing the data in a hierarchical way.
3.6.3 Important Terms
~ 338 ~
keys − Key represents a value of a node based on which a search operation is to be
carried out for a node.
Internal nodes: A node has at least one child node known as an internal
Ancestor node: An ancestor of a node is any predecessor node on a path from the root to
that node. The root node doesn't have any ancestors. In the tree shown in the above
image, nodes 1, 2, and 5 are the ancestors of node 10.
Descendant: The immediate successor of the given node is known as a descendant of a
node. In the above figure, 10 is the descendant of node 5.
3.6.4 Applications of trees
~ 339 ~
a. Types of Binary Tree
The full binary tree is also known as a strict binary tree. The tree can only be considered as the
full binary tree if each node must contain either 0 or 2 children. The full binary tree can also be
defined as the tree in which each node must contain 2 children except the leaf nodes.
Let's look at the simple example of the Full Binary tree
In the above tree, we can observe that each node is either containing zero or two children;
therefore, it is a Full Binary tree.
B. Complete Binary Tree
The complete binary tree is a tree in which all the nodes are completely filled except the last
level. In the last level, all the nodes must be as left as possible. In a complete binary tree, the
nodes should be added from the left.
Let's create a complete binary tree.
~ 340 ~
The above tree is a complete binary tree because all the nodes are completely filled, and all the
nodes in the last level are added at the left first.
C. Perfect Binary Tree
A tree is a perfect binary tree if all the internal nodes have 2 children, and all the leaf nodes are at
the same level.
Let's look at a simple example of a perfect binary tree.
The below tree is not a perfect binary tree because all the leaf nodes are not at the same
level.
~ 341 ~
D. Degenerate Binary Tree
The degenerate binary tree is a tree in which all the internal nodes have only one children.
Let's understand the Degenerate binary tree through examples.
The above tree is a degenerate binary tree because all the nodes have only one child. It is also
known as a right-skewed tree as all the nodes have a right child only.
~ 342 ~
The above tree is also a degenerate binary tree because all the nodes have only one child. It is
also known as a left-skewed tree as all the nodes have a left child only.
E. Balanced Binary Tree
The balanced binary tree is a tree in which both the left and right trees differ by atmost 1. For
example, AVL and Red-Black trees are balanced binary tree.
The above tree is a balanced binary tree because the difference between the left subtree and right
subtree is zero.
~ 343 ~
The above tree is not a balanced binary tree because the difference between the left subtree and
the right subtree is greater than 1.
For more Info, use this link: [Link]
preorder: root-left-right (A B D G H C E F)
Traverses a tree in a pre-order manner.
postorder: left-right-root (G H D B C E F A)
~ 344 ~
Traverses a tree in a post-order manner.
inorder: left-root-right (G D H B A C E F)
Traverses a tree in an in-order manner.
B. Breadth First or Level Order Traversal: Traverses a tree Level by Level
~ 345 ~
void printCurrentLevel(struct node* root, int level)
{
if (root == NULL)
return;
if (level == 1)
printf("%d ", root->data);
else if (level > 1) {
printCurrentLevel(root->left, level - 1);
printCurrentLevel(root->right, level - 1);
}
}
/* Compute the "height" of a tree -- the number of nodes along the longest path from the
root node down to the farthest leaf node.*/
int height(struct node* node)
{
if (node == NULL)
return 0;
else {
/* compute the height of each subtree */
int lheight = height(node->left);
int rheight = height(node->right);
/* use the larger one */
if (lheight > rheight)
return (lheight + 1);
else
return (rheight + 1);
}
}
/* Helper function that allocates a new node with the given data and NULL left and right
pointers. */
struct node* newNode(int data)
{
struct node* node
= (struct node*)malloc(sizeof(struct node));
node->data = data;
node->left = NULL;
node->right = NULL;
return (node);
}
/* Driver program to test above functions*/
~ 346 ~
int main()
{
struct node* root = newNode(1);
root->left = newNode(2);
root->right = newNode(3);
root->left->left = newNode(4);
root->left->right = newNode(5);
return 0;
}
~ 347 ~
return (node);
}
/* Given a binary tree, print its nodes according to the "bottom-up" postorder traversal.
*/
void printPostorder(struct node* node)
{
if (node == NULL)
return;
// first recur on left subtree
printPostorder(node->left);
// then recur on right subtree
printPostorder(node->right);
~ 348 ~
root->right = newNode(3);
root->left->left = newNode(4);
root->left->right = newNode(5);
getchar();
return 0;
}
Advantages
Can represent data with some relationship
Insertion and search are much efficient
Disadvantages
Sorting is difficult
Not much flexible
Applications
File system hierarchy
Multiple variations of the binary tree have a wide variety of applications
c. Demonstration of Binary Tree
#include<stdio.h>
#include<conio.h>
~ 349 ~
#include<stdlib.h>
struct bst
{
int data;
struct bst *left;
struct bst *right;
};
struct bst * insert(struct bst *,int);
void inorder(struct bst *);
void preorder(struct bst *);
void postorder(struct bst *);
int main ()
{
struct bst *r=NULL;
r=insert(r,30);
r=insert(r,15);
r=insert(r,10);
r=insert(r,20);
r=insert(r,40);
r=insert(r,5);
r=insert(r,45);
r=insert(r,35);
printf("\n display element in inorder:-");
inorder(r);
printf("\n display element in preorder:-");
preorder(r);
printf("\n display element in postorder:-");
postorder(r);
return 1;
~ 350 ~
}
struct bst * insert(struct bst *q,int val)
{
struct bst *tmp;
tmp=(struct bst *)malloc(sizeof(struct bst));
if(q==NULL)
{
tmp->data=val;
tmp->left=tmp->right=NULL;
return tmp;
}
else
{
if(val<(tmp->data))
{
q->left=insert(q->left,val);
}
else
{
q->right=insert(q->right,val);
}
}
return q;
}
void inorder(struct bst *q)
{
if(q==NULL)
{
return;
}
~ 351 ~
inorder(q->left);
printf(" %d ",q->data);
inorder(q->right);
}
void preorder(struct bst *q)
{
if(q!=NULL)
{
printf(" %d ",q->data);
preorder(q->left);
preorder(q->right);
}
if(q!=NULL)
{
postorder(q->left);
postorder(q->right);
printf(" %d ",q->data);
}
Output
~ 352 ~
display element in inorder:- 35 45 5 40 20 10 15 30
display element in preorder:- 30 15 10 20 40 5 45 35
display element in postorder:- 35 45 5 40 20 10 15 30
--------------------------------
Process exited after 0.04906 seconds with return value 1
Press any key to continue . . .
Restriction:
o The left child must always be less than the root node
o The right child must always be greater than the root node
Insertion, Deletion, Search is much more efficient than a binary tree
a. Advantages
~ 353 ~
Random access not possible
Ordering adds complexity
c. Applications
#include<stdio.h>
#include<stdlib.h>
struct bst
{
int data;
struct bst *left;
struct bst *right;
};
~ 354 ~
else
{
q->right=insert(q->right,val);
}
}
return q;
}
void inorder(struct bst *q)
{
if(q==NULL)
{
return;
}
inorder(q->left);
printf("%d\t" , q->data);
inorder(q->right);
}
struct bst *search(struct bst *p, int key, struct bst **y)
{
struct bst *temp;
if( p == NULL)
return(NULL);
temp=p;
*y = NULL;
while( temp != NULL)
{
if(temp->data == key)
return(temp);
else
{
~ 355 ~
*y = temp; /*store this pointer as root */
if(temp->data > key)
temp = temp->left;
else
temp = temp->right;
}
}
return(NULL);
}
~ 356 ~
free(x);
return(p);
}
/* this code is for deleting node having both children */
if( x->left!=NULL && x->right!=NULL)
{
if(y->left==x)
{
temp=x->left;
y->left=x->left;
while(temp->right != NULL)
temp = temp->right;
temp->right=x->right;
x->left=NULL;
x->right=NULL;
}
else
{
temp = x->right;
y->right = x->right;
while(temp->left!= NULL)
temp = temp->left;
temp->left=x->left;
x->left=NULL;
x->right=NULL;
}
free(x);
return(p);
}
~ 357 ~
/* this code is for deleting a node with one child*/
if(x->left== NULL && x->right!= NULL)
{
if(y->left== x)
y->left=x->right;
else
y->right= x->right;
x->right= NULL;
free(x);
return(p);
}
~ 358 ~
}
}
}
int main()
{
struct bst *root;
root=NULL; int n,val,num;
printf("\n enter no. of term:- ");
scanf("%d",&n);
while(n!=0)
{
printf("\n enter element:- ");
scanf("%d",&val);
root=insert(root,val);
n--;
}
printf("\n display element:-.......");
inorder(root);
printf("\n enter element to be deleted:- ");
scanf("%d",&num);
del(root,num);
printf("\n display element after deleted:-.......");
inorder(root);
return 1;
}
Output:
enter no. of term:- 5
enter element:- 12
enter element:- 34
enter element:- 56
~ 359 ~
enter element:- 10
enter element:- 23
display element:-.......10 12 23 34 56
enter element to be deleted:- 23
display element after deleted:-.......10 12 34 56
3.6.7 AVL Tree
AVL tree is a binary search tree in which the difference of heights of left and right subtrees of
any node is less than or equal to one. The technique of balancing the height of binary trees was
developed by Adelson, Velskii, and Landi and hence given the short form as AVL tree or
Balanced Binary Tree.
It is one of the types of the binary tree, or we can say that it is a variant of the binary search tree.
AVL tree satisfies the property of the binary tree as well as of the binary search tree.
An AVL tree can be defined as follows:
Let T be a non-empty binary tree with TL and TR as its left and right subtrees. The tree is height
balanced if:
TL and TR are height balanced
hL - hR <= 1, where hL - hR are the heights of TL and TR
The Balance factor of a node in a binary tree can have value 1, -1, 0, depending on whether the
height of its left subtree is greater, less than or equal to the height of the right subtree.
a. Advantages of AVL Tree
Since AVL trees are height balance trees, operations like insertion and deletion have low time
complexity. Let us consider an example:
If you have the following tree having keys 1, 2, 3, 4, 5, 6, 7 and then the binary tree will be like
the second figure:
~ 360 ~
To insert a node with a key Q in the binary tree, the algorithm requires seven comparisons, but if
you insert the same key in AVL tree, from the above 1st figure, you can see that the algorithm
will require three comparisons.
Struct AVLNode
{
int data;
struct AVLNode *left, *right;
int balfactor;
};
c. Algorithm for different Operations on AVL
A. For Insertion
Step 1: First, insert a new element into the tree using BST's (Binary Search Tree) insertion logic.
Step 2: After inserting the elements you have to check the Balance Factor of each node.
~ 361 ~
Step 3: When the Balance Factor of every node will be found like 0 or 1 or -1 then the algorithm
will proceed for the next operation.
Step 4: When the balance factor of any node comes other than the above three values then the
tree is said to be imbalanced. Then perform the suitable Rotation to make it balanced and then
the algorithm will proceed for the next operation.
B. For Deletion:
To balance itself, an AVL tree may perform the following four kinds of rotations −
Left rotation
Right rotation
Left-Right rotation
Right-Left rotation
The first two rotations are single rotations and the next two rotations are double rotations. To
have an unbalanced tree, we at least need a tree of height 2. With this simple tree, let's
understand them one by one.
~ 362 ~
A. Left Rotation
If a tree becomes unbalanced, when a node is inserted into the right subtree of the right subtree,
then we perform a single left rotation −
In our example, node A has become unbalanced as a node is inserted in the right subtree of A's
right subtree. We perform the left rotation by making A the left-subtree of B.
B. Right Rotation
AVL tree may become unbalanced, if a node is inserted in the left subtree of the left subtree.
The tree then needs a right rotation.
As depicted, the unbalanced node becomes the right child of its left child by performing a right
rotation.
C. Left-Right Rotation
Double rotations are slightly complex version of already explained versions of rotations. To
understand them better, we should take note of each action performed while rotation. Let's first
~ 363 ~
check how to perform Left-Right rotation. A left-right rotation is a combination of left rotation
followed by right rotation.
State Action
A node has been inserted into the right subtree of the left subtree. This
makes C an unbalanced node. These scenarios cause AVL tree to
perform left-right rotation.
We first perform the left rotation on the left subtree of C. This makes A,
the left subtree of B.
~ 364 ~
We shall now right-rotate the tree, making B the new root node of this
subtree. C now becomes the right subtree of its own left subtree.
D. Right-Left Rotation
The second type of double rotation is Right-Left Rotation. It is a combination of right rotation
followed by left rotation.
State Action
A node has been inserted into the left subtree of the right subtree. This
makes A, an unbalanced node with balance factor 2.
~ 365 ~
First, we perform the right rotation along C node, making C the right
subtree of its own left subtree B. Now, B becomes the right subtree
of A.
~ 366 ~
Arr[0] element will be treated as root
length(A) – size of array
heapSize(A) – size of heap
Generally used when we are dealing with minimum and maximum elements
For ith node
(i-1)/2 Parent
(2*i)+1 Left child
(2*i)+2 Right Child
a. Advantages
#include<stdio.h>
#define MAX_SIZE 15
~ 367 ~
return 2*i + 2;
}
~ 368 ~
if (largest != i) {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest, n);
}
int main() {
int n = 10;
int a[MAX_SIZE];
a[1] = 10; a[2] = 12; a[3] = 9; a[4] = 78; a[5] = 33; a[6] = 21; a[7] =
35; a[8] = 29; a[9] = 5; a[10] = 66;
build_max_heap(a, n);
insert(a, 55, &n);
insert(a, 56, &n);
~ 369 ~
insert(a, 57, &n);
insert(a, 58, &n);
insert(a, 100, &n);
print_heap(a, n);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct set
~ 370 ~
{
int key;
int data;
};
struct set *array;
int capacity = 10;
int size = 0;
int i;
int hashFunction(int key)
{
return (key % capacity);
}
int checkPrime(int n)
{
int i;
if (n == 1 || n == 0)
{
return 0;
}
for (i = 2; i < n / 2; i++)
{
if (n % i == 0)
{
return 0;
}
}
return 1;
}
int getPrime(int n)
{
if (n % 2 == 0)
{
n++;
}
while (!checkPrime(n))
{
n += 2;
}
return n;
}
void init_array()
{
capacity = getPrime(capacity);
array = (struct set *)malloc(capacity * sizeof(struct set));
for ( i = 0; i < capacity; i++)
{
~ 371 ~
array[i].key = 0;
array[i].data = 0;
}
}
~ 372 ~
{
printf("\n array[%d]: / ", i);
}
else
{
printf("\n key: %d array[%d]: %d \t", array[i].key, i, array[i].data);
}
}
}
int size_of_hashtable()
{
return size;
}
int main()
{
int choice, key, data, n;
int c = 0;
init_array();
do
{
printf("[Link] item in the Hash Table"
"\[Link] item from the Hash Table"
"\[Link] the size of Hash Table"
"\[Link] a Hash Table"
"\n\n Please enter your choice: ");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("Enter key -:\t");
scanf("%d", &key);
printf("Enter data -:\t");
scanf("%d", &data);
insert(key, data);
break;
case 2:
printf("Enter the key to delete-:");
scanf("%d", &key);
remove_element(key);
break;
case 3:
n = size_of_hashtable();
~ 373 ~
printf("Size of Hash Table is-:%d\n", n);
break;
case 4:
display();
break;
default:
printf("Invalid Input\n");
}
} while (c == 1);
}
Output:
~ 374 ~
~ 375 ~
~ 376 ~
Learning outcome 3.8 Use Graph Data Structure Using C
Programming Techniques
Graphs are those types of non-linear data structures which consist of a definite
quantity of vertices and edges. The vertices or the nodes are involved in storing data
and the edges show the vertices relationship. The difference between a graph to a
tree is that in a graph there are no specific rules for the connection of nodes. Real-
life problems like social networks, telephone networks, etc. can be represented
through the graphs.
a. Advantages
finding connectivity
Shortest path
~ 377 ~
min cost to reach from 1 pt to other
Min spanning tree
b. Disadvantages
Storing graph (Adjacency list and Adjacency matrix) can lead to complexities
c. Graph representation
G(V, E): where V(G) represents a set of vertices and E(G) represents a set of
edges
A graph also might be undirected, meaning that line segments join point from either
direction.
~ 378 ~
When we see edge with no arrows, you may presume the graph is undirected, or
bidirectional, when an edge connects two vertices or nodes, we say that they are
‘adjacent’.
E is adjacent to F, A, B and D, but not C. Sure, you can still get to C from E, but not
directly.
Unlike trees, which are special types of graphs, graphs may have cycles, meaning
that children can be the parents of their ‘ancestor’.
A collection of individual points (or edges) in a graph may represent a path. A path
is just a way of getting from one node to another node.
We can also assign ‘weights’ or values to edges.
~ 379 ~
In such case, we have a weighted graph, the weights can represent cost, distance….
3.8.3 Graph to a Tree
the tree is generated by traversing the graph
the same node in the graph may appear repeatedly in the tree
the arrangement of the tree depends on the traversal strategy (search method)
the initial state becomes the root node of the tree
in the fully expanded tree, the goal states are the leaf nodes
cycles in graphs may result in infinite branches
Example:
~ 380 ~
3.8.4 Graph Traversal
Graph traversal is a technique to visit each nodes of a graph G. It is also use to calculate the
order of vertices in traverse process. We visit all the nodes starting from one node which is
connected to each other without going into loop.
Basically in graph it may happen sometime visitors can visit one node more than once. So,
this may cause the going the visitors into infinite loop. So, to protect from this infinite loop
condition we keep record of each vertex. Like if visitors visited vertex then the value will be
zero if not, then one.
There are two graph traversal techniques
Breadth-first search
Depth-first search
a. Breadth First Search (BFS) Traversal in Data Structure
Breadth-first search graph traversal techniques use a queue data structure as an auxiliary
data structure to store nodes for further processing. The size of the queue will be the
maximum total number of vertices in the graph.
Steps to implement BFS traversal
Step 1 – First define a Queue of size n. Where n is the total number of vertices in the graph.
Step 2 – Select any vertex which is a starting point from where traversal will start.
~ 381 ~
Step 3 – Visit starting vertex and insert it into the Queue.
Step 4 – Visit all the non-visited adjacent vertices which is connected to it and insert all
non-visited vertices into the Queue.
Step 5 – When there is no new vertex to be visited from the element which is top in a queue,
Remove the top element which is the vertex from the queue.
Step 6 – Repeat steps 4 and 6 until the queue becomes empty.
Step 7 – When all elements removed from the queue, then produce the final spanning tree
by removing unused edges from the graph.
Example of BFS Graph Traversal
Initialize the queue
We start visiting from vertex 12 that can be considered as starting node, and
mark it as visited.
~ 382 ~
Now we will see an unvisited adjacent node from 12. In this example, we have
three nodes and we can visit anyone. here we are traversing from left to right.
We choose 5 and mark it as visited and enqueue it.
Next, visit the unvisited adjacent node from 12 to 23 . We mark it as visited and
enqueue it.
~ 383 ~
Now, all the connected adjacent node from 12 is traversed and no unvisited
adjacent nodes left. So, we dequeue and find all connect vertex from 5.
From 5 we have 25 as unvisited adjacent node. We mark it as visited and enqueue it.
Now if all nodes visited, we will dequeue all nodes from queue.
So BFS Traversal output is: 12, 5, 23, 3, 25
Complexity Analysis of BFS
Time complexity: O(V + E), where V is the number of vertices and E is the number of
edges in the graph.
Space Complexity: O(V).
Since, an extra visited array is needed of size V.
Applications of BFS
Finding the Shortest path in an unweighted graph
~ 384 ~
Find a solution to a game with the least number of moves. In such a scenario each
state of the game can be represented by a node and state transitions as edges
Finding Connected Components in an unweighted graph
Level Order Traversal in Tree
Find the shortest paths in graphs with weights 0/1
b. Depth First Search(DFS) Traversal in Data Structure
DFS stands for Depth First Search, is one of the graph traversal algorithms that uses Stack
data structure. In DFS Traversal go as deep as possible of the graph and then backtrack once
reached a vertex that has all its adjacent vertices already visited.
Depth First Search (DFS) algorithm traverses a graph in a depth ward motion and uses a
stack data structure to remember to get the next vertex to start a search, when a dead end
occurs in any iteration.
DFS traversal for tree and graph is similar but the only difference is that a graph can
have a cycle but the tree does not have any cycle. So in the graph we have additional
array which keeps the record of visited array to protect from infinite loop and not visited
again the visited node.
Steps to implement Depth First Traversal
Step 1 – Visit all adjacent unvisited vertex. Mark it as visited. Print it and Push it in a stack.
Step 2 − If no adjacent vertex is found, pop up a vertex from the stack.
Step 3 − Repeat Step 1 and Step 2 until the stack is empty.
Graph Traversal using DFS Technique
Initialize the stack
~ 385 ~
Mark 12 as visited and push into stack. Now Explore any unvisited adjacent
node from 12. In our example We have three nodes. We can pick any of them.
Here we are going to pick 5.
Mark 5 as visited and put it onto the stack. Explore any unvisited adjacent node
from 5. Both 12 and 25 are adjacent to 5 but we are concerned for unvisited
nodes only.
Visit 25 and mark it as visited and put onto the stack. Here, we
have 23 and 3 nodes, which are adjacent to 25 and both are unvisited.
~ 386 ~
We choose 3, mark it as visited and put onto the stack. Here 3 does not have any
unvisited adjacent node. So, we pop 3 from the stack.
We check the stack top for return to the previous node and check if it has any
unvisited nodes. Here, we find 25 to be on the top of the stack.
~ 387 ~
Only unvisited adjacent node is from D is 23 now. So we visit 23, mark it as
visited and put it onto the stack.
~ 388 ~
Exercises
1) How can we describe an array in the best possible way?
A. The Array shows a hierarchical structure.
B. Arrays are immutable.
C. Container that stores the elements of similar types
D. The Array is not a data structure
2) Which of the following is the correct way of declaring an array?
A. int javatpoint[10];
B. int javatpoint;
C. javatpoint{20};
D. array javatpoint[10];
3) How can we initialize an array in C language?
A. int arr[2]=(10, 20)
B. int arr(2)={10, 20}
C. int arr[2] = {10, 20}
D. int arr(2) = (10, 20)
4) Which of the following is the advantage of the array data structure?
A. Elements of mixed data types can be stored.
B. Easier to access the elements in an array
C. Index of the first element starts from 1.
D. Elements of an array cannot be sorted
5) Which of the following highly uses the concept of an array?
A. Binary Search tree
B. Caching
C. Spatial locality
D. Scheduling of Processes
6) Which of the following is the disadvantage of the array?
A. Stack and Queue data structures can be implemented through an array.
B. Index of the first element in an array can be negative
C. Wastage of memory if the elements inserted in an array are lesser than the allocated size
~ 389 ~
D. Elements can be accessed sequentially.
7) What is the output of the below code?
#include <stdio.h>
int main()
{
int arr[5]={10,20,30,40,50};
printf("%d", arr[5]);
return 0;
}
A. Garbage value
B. 10
C. 50
D. None of the above
8) Which one of the following is the size of int arr[9] assuming that int is of 4 bytes?
A. 9
B. 36
C. 35
D. None of the above
9) Which one of the following is the process of inserting an element in the stack?
A. Insert
B. Add
C. Push
D. None of the above
10) When the user tries to delete the element from the empty stack then the condition is
said to be a ____
A. Underflow
B. Garbage collection
C. Overflow
D. None of the above
11) If the size of the stack is 10 and we try to add the 11th element in the stack then the
condition is known as___
~ 390 ~
A. Underflow
B. Garbage collection
C. Overflow
D. None of the above
12) Which one of the following is not the application of the stack data structure
A. String reversal
B. Recursion
C. Backtracking
D. Asynchronous data transfer
13) Which data structure is mainly used for implementing the recursive algorithm?
A. Queue
B. Stack
C. Binary tree
D. Linked list
14) Which data structure is required to convert the infix to prefix notation?
A. Stack
B. Linked list
C. Binary tree
D. Queue
15) Which of the following is not the correct statement for a stack data structure?
A. Arrays can be used to implement the stack
B. Stack follows FIFO
C. Elements are stored in a sequential manner
D. Top of the stack contains the last inserted element
16) If the elements '1', '2', '3' and '4' are added in a stack, so what would be the order for
the removal?
A. 1234
B. 2134
~ 391 ~
C. 4321
D. None of the above
17) The minimum number of stacks required to implement a stack is __
A. 1
B. 3
C. 2
D. 5
18) Which one of the following node is considered the top of the stack if the stack is
implemented using the linked list?
A. First node
B. Second node
C. Last node
D. None of the above
19) Consider the following stack implemented using stack.
#define SIZE 11
struct STACK
{
int arr[SIZE];
int top=-1;
}
What would be the maximum value of the top that does not cause the overflow of the stack?
A. 8
B. 9
C. 11
D. 10
20) If the elements '1', '2', '3' and '4' are inserted in a queue, what would be order for the
removal?
A. 1234
B. 4321
C. 3241
D. None of the above
~ 392 ~
21) A list of elements in which enqueue operation takes place from one end, and dequeue
operation takes place from one end is__
A. Binary tree
B. Stack
C. Queue
D. Linked list
22) Which of the following principle does Queue use?
A. LIFO principle
B. FIFO principle
C. Linear tree
D. Ordered array
23) Which one of the following is not the type of the Queue?
A. Linear Queue
B. Circular Queue
C. Double ended Queue
D. Single ended Queue
24) Which of the following that determines the need for the Circular Queue?
A. Avoid wastage of memory
B. Access the Queue using priority
C. Follows the FIFO principle
D. None of the above
25) Consider the following code.
int fun()
{
if(isEmpty())
{
return -10;
}
else
{
int n;
~ 393 ~
n= q[front];
front++;
return n;
}
}
Which operation does the above code perform?
A. Enqueue
B. Dequeue
C. Return the front element
D. Both B and C
26) In the linked list implementation of queue, where will the new element be inserted?
A. At the middle position of the linked list
B. At the head position of the linked list
C. At the tail position of the linked list
D. None of the above
27) How many Queues are required to implement a Stack?
A. 3
B. 2
C. 1
D. 4
28) Which one of the following is not the application of the Queue data structure?
A. Resource shared between various systems
B. Data is transferred asynchronously
C. Load balancing
D. Balancing of symbols
29) Which of the following option is true if implementation of Queue is from the linked list?
A. In enqueue operation, new nodes are inserted from the beginning and in dequeue
operation, nodes are removed from the end.
B. In enqueue operation, new nodes are inserted from the end and in dequeue operation,
nodes are deleted from the beginning.
~ 394 ~
C. In enqueue operation, new nodes are inserted from the end and in dequeue operation,
nodes are deleted from the end.
D. Both A and B.
30) The necessary condition to be checked before deletion from the Queue is__
A. Overflow
B. Underflow
C. Rear value
D. Front value
31) Which data structure is the best for implementing a priority queue?
A. Stack
B. Linked list
C. Array
D. Heap
32) Which of the following principle is used if two elements in the priority queue have the
same priority?
A. LIFO
B. FIFO
C. Linear tree
D. None of the above
33) Which of the following statement is not true regarding the priority queue?
A. Processes with different priority can be easily handled
B. Easy to implement
C. Deletion is easier
D. None of the above
34) A linear data structure in which insertion and deletion operations can be performed
from both the ends is___
A. Queue
B. Dequeue
C. Priority queue
D. Circular queue
~ 395 ~
35) Which of the following statement is not true about the doubly linked list?
A. We can traverse in both the directions.
B. It requires extra space
C. Implementation of doubly linked list is easier than the singly linked list
D. It stores the addresses of the next and the previous node
36) What is the maximum number of children that a node can have in a binary tree?
A. 3
B. 1
C. 4
D. 2
37) Which one of the following techniques is not used in the Binary tree?
A. Randomized traversal
B. Preorder traversal
C. Postorder traversal
D. Inorder traversal
38) Which of the following options is not true about the Binary Search tree?
A. The value of the left child should be less than the root node
B. The value of the right child should be greater than the root node.
C. The left and right sub trees should also be a binary search tree
D. None of the above
39) How can we define a AVL tree?
A. A tree which is binary search tree and height balanced tree.
B. A tree which is a binary search tree but unbalanced tree.
C. A tree with utmost two children
D. A tree with utmost three children
40) Why do we prefer Red Black tree over AVL tree?
A. Red Black trees are not strictly balanced
B. Red black tree requires lesser rotations than AVL tree.
C. AVL tree needs more space to store the balance factor.
D. Both B and C
41) Which of the following satisfies the property of the Red Black tree?
~ 396 ~
A. A tree which is a binary search tree but not strictly balanced tree.
B. A node must be either Red or Black in color and root node must be black.
C. A tree with maximum three children
D. Both A and B
42) What would be the color of newly created node while inserting a new element in a Red
black tree?
A. Black, if the new node is not a root node
B. Red, if the new node is not a root node
C. Black, if the new node is a root node
D. Both B and C
43) Identify the AVL tree among the following options?
~ 397 ~
A. A
B. C
C. Both A and C
D. B
44) A graph with all vertices having equal degree is known as a __________
A. Multi Graph
B. Regular Graph
C. Simple Graph
D. Complete Graph
45) Consider the following Tree
Perform:
A. Inorder Traversal
B. Preorder Traversal
C. Postorder Traversal
Sample Example on Binary Tree
Create the binary search tree using the following data elements.
43, 10, 79, 90, 12, 54, 11, 9, 50
1. Insert 43 into the tree as the root of the tree.
2. Read the next element, if it is lesser than the root node element, insert it as the root of the
left sub-tree.
3. Otherwise, insert it as the root of the right of the right sub-tree.
~ 398 ~
The process of creating BST by using the given elements, is shown in the image below.
~ 399 ~
Basis for Tree Graph
comparison
Definition Tree is a non-linear data structure in which A Graph is also a non-linear data
elements are arranged in multiple levels. structure.
Structure It is a collection of edges and nodes. For It is a collection of vertices and edges.
example, node is represented by N and edge For example, vertices are represented
is represented as E, so it can be written as: by V, and edge is represented as 'E',
T = {N,E} so it can be written as:
T = {V, E}
Root node In tree data structure, there is a unique node In graph data structure, there is no
known as a parent node. It represents the unique node.
topmost node in the tree data structure.
Loop It does not create any loop or cycle. In graph, loop or cycle can be formed.
formation
Model type It is a hierarchical model because nodes are It is a network model. For example,
arranged in multiple level, and that creates a Facebook is a social network that uses
hierarchy. For example, any organization the graph data structure.
will have a hierarchical model.
Edges If there are n nodes then there would be n-1 The number of edges depends on the
number of edges. graph.
Type of edge Tree data structure will always have In graph data structure, all the edges
directed edges. can either be directed edges,
undirected edges, or both.
Applications It is used for inserting, deleting or searching It is mainly used for finding the
~ 400 ~
any element in tree. shortest path in the network.
References:
1. Griffiths, David (2012). Head First c. 1005 GravensteinHighwayNorth, Sebastopol, CA
95472 : Brian Sawyer
2. Stephen, Kochan.G (2005). A complete introduction to the C programming language.3rd
ed. Indianapolis, Indiana 46240 : Cindy Teeters
3. Mark Burnett, "Blocking Brute Force Attacks" Archived 2016-12-03 at the Wayback
Machine, UVA Computer Science, 2007
4. Web reference
- [Link]
- [Link]
- [Link]
- [Link]
- [Link]
- [Link]
- [Link]
- [Link]
- [Link]
-htps://[Link]/basics_of_computer_science/basics_of_computer_scienc
e_algorithm_flowchart.htm
~ 401 ~