CS2203T Data Structures - Unit1 - Introduction To Algorithms Notes
CS2203T Data Structures - Unit1 - Introduction To Algorithms Notes
Problem solving is a critical skill in computer science and programming. It refers to the process of
finding solutions to problems or challenges by applying logic and critical thinking.
1. Understanding the problem: This involves carefully reading and comprehending the
problem statement and defining the problem in your own words.
2. Analyzing the problem: This involves breaking down the problem into smaller, more
manageable parts and identifying the information and data required to solve the problem.
3. Formulating a plan: This involves creating a step-by-step plan for solving the problem,
including the methods and algorithms that will be used.
4. Implementing the plan: This involves coding the solution and testing it to ensure that it
works as expected.
5. Evaluating the solution: This involves analyzing the solution to ensure that it’s correct,
efficient, and meets the requirements of the problem.
It’s important to note that problem solving is an iterative process and may require multiple
iterations of the above steps. The goal is to find a solution that works and meets the requirements
of the problem. Effective problem solving skills require patience, persistence, and a willingness to
try different approaches until the right solution is found
Problem solving in programming requires critical thinking, creativity, and a deep understanding
of the programming concepts and algorithms. It’s important to be able to identify patterns and use
abstraction and decomposition to break down complex problems into simpler parts. Effective
problem solving skills also require patience, persistence, and a willingness to try different
approaches until the right solution is found.
Steps in problem solving
1. Define the problem: Clearly identify and understand the problem that needs to be solved.
2. Gather information: Collect data and information related to the problem.
3. Develop potential solutions: Generate multiple possible solutions to the problem.
4. Evaluate potential solutions: Assess each solution based on its potential effectiveness,
feasibility, and impact.
5. Select a solution: Choose the best solution based on the evaluation.
6. Implement the solution: Put the chosen solution into action.
7. Monitor progress: Continuously monitor and evaluate the solution to ensure it is solving
the problem effectively.
8. Refine the solution: Make necessary adjustments to the solution if it is not working as
intended.
The concept of an algorithm is fundamental to computer science. Algorithms exist for many
common problems, and designing efficient algorithms plays a crucial role in developing large-
scale computer systems. Therefore, before we proceed further we need to discuss this concept
more fully. We begin with a definition.
Definition: An algorithm is a finite set of instructions that, if followed, accomplishes a particular
task. In addition, all algorithms must satisfy the following criteria:
(1) Input. There are zero or more quantities that are externally supplied.
(2) Output. At least one quantity is produced.
(3) Definiteness. Each instruction is clear and unambiguous.
(4) Finiteness. If we trace out the instructions of an algorithm, then for all cases, the algorithm
terminates after a finite number of steps.
(5) Effectiveness. Every instruction must be basic enough to be carried out, in principle, by a
person using only pencil and paper. It is not enough that each operation be definite as in (3); it
also must be feasible.
In computational theory, one distinguishes between an algorithm and a program, the latter of
which does not have to satisfy the fourth condition. For example, we can think of an operating
system that continues in a wait loop until more jobs are entered. Such a program does not
terminate unless the system crashes. Since our programs will always terminate, we will use
algorithm and program interchangeably in this text.
We can describe an algorithm in many ways. We can use a natural language like English,
although, if we select this option, we must make sure that the resulting instructions are definite.
Graphic representations called flowcharts are another possibility, but they work well only if the
algorithm is small and simple. In this text we will present most of our algorithms in C,
occasionally resorting to a combination of English and C for our specifications. An examples will
help to illustrate the process of translating a problem into an algorithm.
Example 1 [Selection sort]: Suppose we must devise a program that sorts a set of n >= 1 integers.
A simple solution is given by the following:
From those integers that are currently unsorted, find the smallest and place it next in the sorted list.
Although this statement adequately describes the sorting problem, it is not an algorithm since it
leaves several unanswered questions. For example, it does not tell us where and how the integers
are initially stored, or where we should place the result. We assume that the integers are stored in
an array, list, such that the ith integer is stored in the ith position, list [i], 0 <= i < n.
Program 1.1 is our first attempt at deriving a solution. Notice that it is written partially in C and
partially in English.
Based on the different types of steps in an Algorithm, it can be divided into three
categories, namely
Sequence
Selection and
Iteration
Sequence: The steps described in an algorithm are performed successively one by one
without skipping any step. The sequence of steps defined in an algorithm should be simple
Example 2:
Step 1: start
Step 3: Sum=a+b
Step 5: stop
Selection: The sequence type of algorithms are not sufficient to solve the problems, which
involves decision and conditions. In order to solve the problem which involve decision
making or option selection, we go for Selection type of algorithm. The general format of
Statement-1;
else
Statement-2;
The above syntax specifies that if the condition is true, statement-1 will be executed
sequence of algorithm should be changed/ corrected in such a way that the system will re
Iteration: Iteration type algorithms are used in solving the problems which involves
Example 3:
Step 1 : start
Step 2 : read n
(b) s=s+r
(c) n=n/10
Step 5 : write s
Step 6 : stop
Input / Output
Process / Instruction
Decision
Connector / Arrow
The symbols above represent different parts of a flowchart. The process in a flowchart can be
expressed through boxes and arrows with different sizes and colors. In a flowchart, we can easily
highlight certain elements and the relationships between each part.
ALGORITHM
Flowchart
Algorithm:
Step 1: Initialize X as 0,
Step 2: Increment X by 1,
Step 3: Print X,
Step 4: If X is less than 20 then go back to step 2.
Flowchart:
Algorithm:
Flowchart:
1.3 Recursion
A recursive algorithm calls itself which usually passes the return value as a parameter to the
algorithm again. This parameter is the input while the return value is the output.
Recursive algorithm is a method of simplification that divides the problem into sub-problems of
the same nature. The result of one recursion is the input for the next recursion. The repletion is in
the self-similar fashion. The algorithm calls itself with smaller input values and obtains the results
by simply performing the operations on these smaller values.
Generation of factorial, Fibonacci number series are the examples of recursive algorithms.
Example 6:Writing factorial function using recursion
int factorial(int n)
{
return n * factorial(n-1);
}
Program 1.2: Factorial function
Figure 1.1
1 Data structure is an arrangement of data in computer's memory. It makes the data quickly
available to the processor for required operations.
2 It is a software artifact which allows data to be stored, organized and accessed.
3 It is a structure program used to store ordered data, so that various operations can be
performed on it easily.
For example, if we have an employee's data like name 'ABC' and salary 10000. Here, 'ABC'
is of String data type and 10000 is of Float data type.
We can organize this data as a record like Employee record and collect & store employee's
records in a file or database as a data structure like 'ABC' 10000, 'PQR' 15000, 'STU' 5000.
4 Data structure is about providing data elements in terms of some relationship for better
organization and storage.
5 It is a specialized format for organizing and storing data that can be accessed within
appropriate ways.
Primitive data types are the data types available in most of the programming languages.
These data types are used to represent single value.
It is a basic data type available in most of the programming language.
Data type Description
Data type derived from primary data types are known as Non-Primitive data types.
Non-Primitive data types are used to store group of values.
Types Description
Linked list is a collection of data elements. It consists of two parts: Info and Link.
Linked list Info gives information and Link is an address of next node. Linked list can be
implemented by using pointers.
Stack is a list of elements. In stack, an element may be inserted or deleted at one end
which is known as Top of the stack. It performs two operations: Push and Pop. Push
Stack
means adding an element in stack and Pop means removing an element in stack. It is
also called Last-in-First-out (LIFO).
Queue is a linear list of element. In queue, elements are added at one end called rear
Queue and the existing elements are deleted from other end called front. It is also called as
First-in-First-out (FIFO).
Graph Graph is a non-linear data structure which consists of a finite set of ordered pairs
called edges. Graph is a set of elements connected by edges. Each elements are
called a vertex and node.
If any algorithm requires a fixed amount of space for all input values then that space complexity is
said to be Constant Space Complexity.
If the amount of space required by an algorithm is increased with the increase of input value, then
that space complexity is said to be Linear or Variable Space Complexity.
If any program requires a fixed amount of time for all input values then its time complexity is said
to be Constant Time Complexity.
If the amount of time required by an algorithm is increased with the increase of input value then
that time complexity is said to be Linear or variable Time Complexity.
Where T (P) is the time taken by a program P, it is the sum of its compile time C and its run (or
execution) time, TP (I)
Fixed time requirements are Compile time (C), it is independent of instance characteristics and
Variable time requirements are Run (execution) time TP.
Consider the following program 1.4
int sum(int A[], int n)
{
int sum = 0, i;
for(i = 0; i < n; i++)
sum = sum + A[i];
return sum;
}
Program 1.4: Summing a list of Numbers
Algorihtm 1 : 5n2 + 2n + 1
Algorihtm 2 : 10n2 + 8n + 3
Generally, when we analyze an algorithm, we consider the time complexity for larger values of
input data (i.e. 'n' value). In above two time complexities, for larger value of 'n' the term in
algorithm 1 '2n + 1' has least significance than the term '5n2', and the term in algorithm 2 '8n + 3'
has least significance than the term '10n2'.
Here for larger value of 'n' the value of most significant terms ( 5n2 and 10n2 ) is very larger than
the value of least significant terms ( 2n + 1 and 8n + 3 ). So for larger value of 'n' we ignore the
least significant terms to represent overall time required by an algorithm. In asymptotic notation,
we use only the most significant terms to represent the time complexity of an algorithm.
Majorly, we use THREE types of Asymptotic Notations and those are as follows...
1. Big - Oh (O)
2. Big - Omega (Ω)
3. Big - Theta (Θ)
f(n) = O(g(n))
Consider the following graph drawn for the values of f(n) and C g(n) for input (n) value on X-
Axis and time required is on Y-Axis
Figure 1.4
In above graph after a particular input value n0, always C g(n) is greater than f(n) which indicates
the algorithm's upper bound.
Example
Consider the following f(n) and g(n)...
f(n) = 3n + 2
g(n) = n
If we want to represent f(n) as O(g(n)) then it must satisfy f(n) <= C x g(n) for all values of C > 0
and n0>= 1
f(n) <= C g(n)
⇒3n + 2 <= C n
Above condition is always TRUE for all values of C = 4 and n >= 2.
By using Big - Oh notation we can represent the time complexity as follows...
3n + 2 = O(n)
Big - Omega Notation can be defined as follows...Consider function f(n) the time complexity of
an algorithm and g(n) is the most significant term. If f(n) >= C x g(n) for all n >= n0, C > 0 and
n0 >= 1. Then we can represent f(n) as Ω(g(n)).
Consider the following graph drawn for the values of f(n) and C g(n) for input (n) value on X-
Axis and time required is on Y-Axis
Figure 1.5
In above graph after a particular input value n0, always C x g(n) is less than f(n) which indicates
the algorithm's lower bound.
Example
Consider the following f(n) and g(n)...
f(n) = 3n + 2
g(n) = n
If we want to represent f(n) as Ω(g(n)) then it must satisfy f(n) >= C g(n) for all values of C > 0
and n0>= 1
f(n) >= C g(n)
⇒3n + 2 <= C n
Above condition is always TRUE for all values of C = 1 and n >= 1.
By using Big - Omega notation we can represent the time complexity as follows...
3n + 2 = Ω(n)
f(n) = Θ(g(n))
Consider the following graph drawn for the values of f(n) and C g(n) for input (n) value on X-
Axis and time required is on Y-Axis
Figure 1.6
In above graph after a particular input value n0, always C1 g(n) is less than f(n) and C2 g(n) is
greater than f(n) which indicates the algorithm's average bound.
Example
Consider the following f(n) and g(n)...
f(n) = 3n + 2
g(n) = n
If we want to represent f(n) as Θ(g(n)) then it must satisfy C1 g(n) <= f(n) >= C2 g(n) for all
values of C1, C2 > 0 and n0>= 1
C1 g(n) <= f(n) >= ⇒C2 g(n)
C1 n <= 3n + 2 >= C2 n
Above condition is always TRUE for all values of C1 = 1, C2 = 4 and n >= 1.
By using Big - Theta notation we can represent the time compexity as follows...
3n + 2 = Θ(n)
1.8 Testing
Testing is the process of evaluating a system or its component(s) with the intent to find whether it
satisfies the specified requirements or not.Testing is executing a system in order to identify any
gaps, errors, or missing requirements in contrary to the actual requirements.
The process of software testing aims not only at finding faults in the existing software but also at
finding measures to improve the software in terms of efficiency, accuracy and usability. It mainly
aims at measuring specification, functionality and performance of a software program or
application.
Software testing can be divided into two steps:
1. Verification: it refers to the set of tasks that ensure that software correctly implements a
specific function.
2. Validation: it refers to a different set of tasks that ensure that the software that has been built is
traceable to customer requirements.
Verification: “Are we building the product right?”
Validation: “Are we building the right product?”