Data Structures and Algorithms Overview
Data Structures and Algorithms Overview
Semester : 03 Directed by
2024/2025 Dr. DABBA ALI
Contact us
alidabba@[Link]
[Link]@[Link]
Algorithm Analysis
Plan
I. Introduction
II. Data Structure
III. What is an Algorithm?
IV. Algorithm Analysis
V. Algorithm Complexity
VI. Mathematical Notation
[Link]-Time Calculations
Dr. Dabba Ali 4
I. Introduction
Problem Algorithm Result
Efficiency
of an Data
algorithm
Efficiency of an algorithm can be measured in terms of:
Execution time (time complexity)
The amount of memory required (space complexity)
II. Data Structure
Linus Torvalds (creator of Linux)
“ I will, in fact, claim that the difference between a bad
programmer and a good one is whether he considers his
code or his data structures more important. Bad
programmers worry about the code. Good
programmers worry about data structures and their
relationships. ”
II. Data Structure
Data structure is a particular way of storing and
organizing data in a computer so that it can be
used efficiently.
A data structure is a special format for organizing
and storing data.
General data structure types include arrays, files,
linked lists, stacks, queues, trees, graphs, and so
on.
II. Data Structure
Integer
Real
Primitive data
structure
Character
Arrays
Boolean
Data structure Linked
Linear data
structures
Stacks
Non-primitive data
Queues
structures
Trees
Non-linear data
structures
Graphs
III. What is an Algorithm?
Algorithm is a step-by-step procedure to solve a
problem, where each step indicates an intermediate task.
Algorithm contains a finite number of instructions to be
executed in a certain order to get the desired output.
Algorithms are generally created independent of
underlying languages, i.e., an algorithm can be
implemented in more than one programming language.
III.1. Characteristics of Algorithms
The main Characteristics or features of Algorithms are:
Input Output
Definiteness Finiteness
Effectiveness
Feasible
Language Independent
III.2. Advantages of Algorithms
Easy: Algorithm is easy to understand.
Reusability: Once developed, algorithms can be reused
in different applications and scenarios, reducing the
need to start from scratch each time.
Scalability: Good algorithms can handle varying input
sizes without a significant increase in resources.
III.3. Disadvantages of Algorithms
∶
the number of elementary operations performed by on input .
ℕ → ℕ such that:
The execution time of in the worst case is the function
: .
In other words, indicates the largest number of operations
performed among all inputs of size .
IV.1.1. Runtime Based on a Single Parameter
Example:
0 1 2 3 4 5 6 7 8 9
15 25 3 7 33 28 7 9 10 11
15 OK
⇒ Best-case
IV.1.3. Types of Time Complexity Analysis
Example: Find a given value (if it exists!) in this array
0 1 2 3 4 5 6 7 8 9
15 25 3 7 33 28 7 9 10 11
OK
11 11 11 11 11 11 11 11 11 11
⇒ Worst-case
IV.1.3. Types of Time Complexity Analysis
IV.1.3. Types of Time Complexity Analysis
We have three types of analysis related to time complexity:
Worst-case time complexity: For 'n' input size, the worst-case
time complexity can be defined as the maximum amount of
time needed by an algorithm to complete its execution.
Thus, it is nothing but a function defined by the maximum
number of steps performed on an instance having an input size
of n. Computer Scientists are more interested in this.
Big-O Notation
IV.1.3. Types of Time Complexity Analysis
We have three types of analysis related to time complexity:
Average case time complexity: For 'n' input size, the average
case time complexity can be defined as the average amount of
time needed by an algorithm to complete its execution.
Thus, it is nothing but a function defined by the average number
of steps performed on an instance having an input size of n.
∗
The complexity can be found in any form such as constant,
logarithmic, linear, , quadratic, cubic,
exponential, factorial etc.
To make it even more precise, we often call the complexity of
an algorithm "running time".
VI. Mathematical Notation
Algorithms are widely used in various fields of study.
:ℕ → ℝ ∶ ∃ ∈ ℕ, ∀
belonging to:
∈
∈ :∃ ∈ ℝ ,∃ ∈ ℕ, ∀ .
Let . The set is defined by:
Input Size
VI.1.1. Big-O Notation
Example:
.
for all
for all
∈
Thus, taking as a multiplicative constant and
as a threshold, we conclude that .
VI.1.1. Big-O Notation
Example:
∈ .
VI.1.2. Big-O Notation for Complexity Class
The most common complexity classes (in increasing order) are the following:
!
VI.1.2. Big-O Notation for Complexity Class
. .
.
1 1 1 1
.
.
1 2 3
.
. .
2 4 8
.
. .
4 16
.
. .
8 64
.
. .
16 256
.
.
64 4096
16 65536 . .
VI.1.2. Big-O Notation for Complexity Class
, ,
&
e.g.,
⟹
here,
VI.1.4. Limitations of Big O Notation
Many algorithms are too hard to analyze mathematically.
Big-O analysis only tells us how the algorithm grows with
the size of the problem, not how efficient it is, because it does
not consider the programming effort.
It does not take into account important constants. For
example, if one algorithm takes time to execute and the
other takes time to execute, then according to
Big O, both algorithms have the same time complexity. In real-
time systems, this is an important consideration.
VII. Running-Time Calculations
There are several ways to estimate the running time of a
program. To simplify the analysis, we will adopt the convention
that there are no particular units of time.
We are essentially doing is computing a Big-Oh running
time. Since Big-Oh is an upper bound, we must be careful never
to underestimate the running time of the program.
In effect, the answer provided is a guarantee that the program
will terminate within a certain period.
VII.1. Assumptions in Complexity Analysis
Uniform Cost Model: Assumes that all basic instruction (such
as arithmetic, comparisons, assignments, . . . ) have the same
cost. This simplifies the analysis by treating each operation as
taking constant time, represented by the notation O(1).
Input size: Analysis is generally performed based on the input
size, often referred to as “n”, which represents the number of
elements or the size of the input data structure.
VII.1. Assumptions in Complexity Analysis
Worst-case scenario: Complexity analysis often focuses on the
worst-case input, i.e., assuming the input that leads to the
maximum number of operations.
Loops: each iteration of a loop adds the complexity of what is
done in the loop body.
Functions (procedures): each function call adds the
complexity of that function to the total complexity.
VII.2. Simplification Rules
Drop Constants: Constants in front of the dominant term are
dropped in Big-O notation. For example: simplifies to
.
Dominant Term Rule: only the term with the largest growth rate
dominates in Big-O notation. For example, in , a
dominant term is , so the complexity is .
Additive Rule: When analyzing multiple operations in sequence, the
complexities are added. For example, if an algorithm has two
.
separate loops with complexities and , the total
complexity is
VII.2. Simplification Rules
Multiplicative Rule: When analyzing nested operations, the
complexities are multiplied. For example, if an algorithm has
∗
nested loops with complexities and , the total
complexity is .
1 Operation (assig) ⟹
E.g.,
int x = 1;
VII.4.1. Simple Statement and O(1)
x=x+1;
VII.4.2. Sequence of Simple Statements
Example:
if (cond1) statement1 //
else if (cond2) statement2 //
else if (cond3) statement3 //
else statement4 //
The time complexity of this code segment will be:
VII.4.5. Iterations (Loops)
. .
3. Then the complexity of the loop would be:
VII.4.5.1. Simple Loops
Example:
Total time = .
VII.4.5.2. Loops with Uniform Step Size
0 1 2 3 4 5 6 7 8 9
5 8 13 19 25 28 37 49 50 64
13 13 13
VII.4.5.3. Simple Loops with Variable Step Size
2 2 2 1 2 2
2 4
Search Size … …
VII.4.5.3. Simple Loops with Variable Step Size
Before concluding this discussion on simple loops, let us look at a last, but
quite deceiving
Example: The following piece of code prints the table for number m.
For the purpose of this discussion, we can divide the nested loops
in two categories:
1. Independent loops: the number of iterations of the inner loop
are independent of any variable controlled in the outer loop.
2. Dependent loops: the number of iterations of the inner loop
are dependent upon some variable controlled in the outer loop.
VII.4.5.5. Nested Loop (Independent Loops)
Example 1:
for (i = 0; i < N ; i++) {
min = i;
for (j = i; j < N; j++)
if (a[min] > a[j]) min = j;
swap(a[i], a[min]);
}
As can be clearly seen, the value of j is dependent upon i, and hence the
number of iterations of the inner loop depend upon the value of i which
is controlled in the outer loop.
VII.4.5.5. Nested Loop (Dependent Loops)
Example 1:
Value of i 0 1 2 … I … N-1
1 2 1
Number of
iterations of the … …
inner loop
1 2 ⋯ ⋯ 1
1
Total Number of
2
times the body of
the inner loop is
executed
VII.4.5.5. Nested Loop (Dependent Loops)
Example 2:
for (i = 1; i < N; i++)
for (j = 1; j <= i; j = j * 2)
k++;
The first thing to note here is that the inner loop follows a geometric
progression, going from 1 to i, with 2 as the common ratio between
consecutive terms.
We have already seen that in such cases the number of iterations is given
by
VII.4.5.5. Nested Loop (Dependent Loops)
Example 2:
Value of i 1 2 3 4 … N-1
Number of
iterations of the 1 log 2 2 log 3 2 log 4 3 log 5 … log
inner loop
Total Number of log 2 log 3 log 4 log 5 ⋯ log
log 2 3 4 5 ⋯ = log !
times the body
’
of the inner loop
is executed
VII.4.5.5. Nested Loop (Dependent Loops)
Example 1: =
for (i = 0; i < N ; i++) {
min = i;
for (j = i; j < N; j++)
if (a[min] > a[j]) min = j;
swap(a[i], a[min]);
}
Example 2: =
for (i = 1; i < N; i++)
for (j = 1; j <= i; j = j * 2)
k++;
VII.4.5.5. Nested Loop (Dependent Loops)
Example 3: =
for (i = 1; i < N; i = i*2)
for (j = 1; j < i; j++)
k++;
This is very different from what we get if we apply the approach used in
the previous examples.
VII.4.5.5. Nested Loop (Dependent Loops)
Example 3:
2
Value of i 1 2 3 4 … N-1
iterations of the 1 2 2 2 4 2 8 2 ℎ
Number of
…
inner loop
Total Number of 2 2 2 2 ⋯ 2
2 1 2 2 1
times the body of
the inner loop is
executed
VII.4.6. Non-Recursive Subprogram Call
It is easy to see that this function has a linear time complexity, that is,
( ).
VII.4.6. Non-Recursive Subprogram Call
Example 2:
float average(float data[], int size) // assuming size > 0
{
float total, mean;
total = sum(data, size);
mean = total/size;
return mean;
}
It is easy to see that this function has a linear time complexity, that is,
( ).
VII.4.6. Non-Recursive Subprogram Call
Example 3:
int foo(int n) {
int count = 0;
for (int j = 0; j < n; j++)
count++;
return count;
}
It is easy to see that this function has a linear time complexity, that is,
( ).
VII.4.6. Non-Recursive Subprogram Call
Example 4:
int bar(int n) {
temp = 0;
for (int i = 1; i < n; i = i * 2) {
temp1 = foo(i);
temp = temp1 + temp;
}
}
.
It is easy to see that this function has a linear time complexity, that is,
VII.4.6. Non-Recursive Subprogram Call
Example 5:
int factorial(int n){ //Returns a factorial of the natural n.
int fact = 1;
int i = 2;
while (i <= n){
fact = fact * i;
i = i + 1;
}
return fact;
}
VII.5. Guidelines for Asymptotic Analysis of
Recursive Algorithms
Example:
Compute the factorial function for an arbitrary non-
! ⋯ !
negative integer . Since
Example:
int Fact(int n) {
(1) if (n==0);
(2) return 1; /* basis */
else
(3) return n*Fact(n-1); /*induction */
}
VII.5.1. Recurrence Relation
Example:
int Fact(int n) {
(1) if (n==0);
(2) return 1;
else
(3) return n*Fact(n-1);
}
VII.5.1. Recurrence Relation
Example:
We can thus define by the following recurrence relation:
VII.5.2. Solving Recurrence Relations
,
follows:
⋮ ⋮ ⋮
1) Iterative Substitution Method
Example 1 (Solution):
To solve the generalized a last relation, we have to find the value
of . We note that , that is the number of operations
needed to raise a number to power needs just one
operation.
We can reduce
⟹
to by setting
1) Iterative Substitution Method
Example 1 (Solution):
We can reduce
⟹
to by setting
⋮ ⋮ ⋮
. ;
1) Iterative Substitution Method
Example 2 (Solution):
The maximum value of (then only we get )
. .
.
1) Iterative Substitution Method
Note: If you want to write a recurrence relation for , you must
replace ALL instances of with , even the terms outside the recursive
call! A common mistake is only substituting the in the recursive term.
Example 3:
Correct:
Incorrect:
2) Recurrence Tree Method
While the substitution method works well for many
recurrence relations, it is not an appropriate technique
when recurrence relation model algorithms are based on
the “divide and conquer” paradigm.
The recurrence tree method is a popular technique for
solving such recurrence relations, particularly for
solving unbalanced recurrence relations.
2) Recurrence Tree Method
In the recursion method, we draw a recurrence tree and calculate
the time taken at each level of the tree.
Finally, we sum the costs within each of the levels of the tree to
obtain a set of pre-level costs and then sum all pre-level costs to
determine the total cost of all levels of the recursion.
To draw the recurrence tree, we start from the given recurrence
and keep drawing until we find a pattern among levels. This
pattern is usually an arithmetic or geometric series.
2) Recurrence Tree Method
Given a recurrence relation, we can build a recursion tree
by repeating the following steps until we reach the base
case:
At each level, each node is assigned the total amount of
work that is done outside the recursive call(s).
Each recursive call creates a branch to the next level of
the tree.
2) Recurrence Tree Method
Example:
Consider the following recurrence relation :
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
Total complexity : . ∑
3) Master Theorem Method
The master method provides a “cookbook” method for
solving recurrences of the form
0, then
• Case 3: If , then
0, then
If .
If .
a) The Extended Master Theorem for
Polylogarithmic Functions
Example 1:
Consider the following recurrence relation :
Example 1 (Solution):
Here, the master theorem does not apply, but rather we apply
the extended master theorem.
, , and ⟹ &
For this recurrence, we have
Since ,
As per of the extended master theorem, the solution is :
a) The Extended Master Theorem for
Polylogarithmic Functions
Example 2:
Consider the following recurrence relation :
Example 2 (Solution):
Here, the master theorem does not apply, but rather we apply the extended
master theorem.
, , and ⟹ &
For this recurrence, we have
,
⟹
Since
As per of the extended master theorem, , the
solution is :
Exercise
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
/
Questions ?