Design and Analysis
Design and Analysis
Introduction
What is an Algorithm?
An algorithm is a step-by-step procedure for solving a problem. It's like a recipe for a
computer, providing a sequence of instructions to achieve a desired outcome.
Algorithms are the backbone of computer science. They are essential for:
Example:
Analysis of Algorithms
Nature of Input
The nature of the input data significantly impacts the algorithm's performance. Consider these
factors:
• Data Type: The type of data (e.g., numbers, strings, structures) affects the operations involved.
• Distribution: The distribution of values within the input can influence the algorithm's
efficiency.
• Constraints: Any constraints on the input (e.g., range, uniqueness) can be exploited for
optimization.
Size of Input
The size of the input data is a crucial factor in determining the algorithm's running time. Larger
inputs generally require more computational resources.
• Input Size: The number of elements or the amount of data in the input.
• Growth Rate: How the algorithm's running time increases with the input size.
Example:
• The simple algorithm to find the largest number has a linear growth rate. Its running time
increases linearly with the number of elements in the list.
Key Considerations:
• Worst-Case Analysis: Analyzing the algorithm's performance under the worst-possible input
conditions.
• Average-Case Analysis: Analyzing the performance for typical or average input cases.
• Best-Case Analysis: Analyzing the performance under the best-possible input conditions.
Summary
1. Time Complexity
• Definition: The amount of time an algorithm takes to sort as the input size grows.
• Common Classes:
o O(n^2): Example: Bubble Sort, Selection Sort
o O(n log n): Example: Merge Sort, Quick Sort
o O(n): Example: Counting Sort (under certain conditions)
2. Space Complexity
3. Stability
• Definition: A stable sorting algorithm maintains the relative order of equal elements.
• Stable Algorithms: Merge Sort, Bubble Sort
• Unstable Algorithms: Quick Sort, Selection Sort
Loop Invariants
A loop invariant is a property that holds true before and after each iteration of a loop. It helps
in proving the correctness of an algorithm.
1. Definition
• A loop invariant is a condition that remains unchanged throughout the execution of the loop.
2. Importance
3. Example
Insertion Sort:
• Invariant: At the start of each iteration, the left side of the array is sorted.
• How it Works: Each new element is inserted into its correct position in the sorted portion,
maintaining the invariant.
Summary
Recursion
1. Definition
• Recursion is a programming technique where a function calls itself to solve smaller instances
of the same problem.
2. Components
• Base Case: The condition under which the recursion stops. It prevents infinite loops.
• Recursive Case: The part of the function where the recursion occurs, breaking the problem into
smaller subproblems.
3. Example
• Factorial Function:
#include <iostream>
int main() {
int number;
std::cout << "Enter a positive integer: ";
std::cin >> number;
if (number < 0) {
std::cout << "Factorial is not defined for negative numbers." << std::endl;
} else {
std::cout << "Factorial of " << number << " is " << factorial(number) << std::endl;
}
return 0;
}
Recurrence Relations
1. Definition
• Recurrence Relations are equations that define a sequence based on previous terms. They are
used to analyze the time complexity of recursive algorithms.
2. Formulation
4. Example
Summary
These techniques provide various strategies for approaching and solving algorithmic problems
effectively.
• Definition: A sorting algorithm that divides an array into two halves, sorts each half, and
merges them.
• Steps:
1. Divide the array.
2. Recursively sort each half.
3. Merge the sorted halves.
• Time Complexity: O(n log n), stable.
Quick Sort
• Definition: A sorting algorithm that selects a "pivot" and partitions the array into elements less
than and greater than the pivot.
• Steps:
1. Choose a pivot.
2. Partition the array around the pivot.
3. Recursively sort the sub-arrays.
• Time Complexity: Average O(n log n), worst O(n²), not stable.
Greedy Approach
• Definition: Makes the best choice at each step, hoping to find the overall best solution.
• Example: Coin change problem or activity selection.
• Pros: Often simple and fast.
• Cons: Does not guarantee an optimal solution for all problems.
Dynamic Programming
• Definition: A method for solving complex problems by breaking them down into simpler
subproblems, solving each subproblem just once, and storing their solutions.
1. Overlapping Subproblems
o Definition: The problem can be broken down into smaller, recurring subproblems.
o Example: In calculating the Fibonacci sequence, the same Fibonacci numbers are
calculated multiple times.
2. Optimal Substructure
o Definition: An optimal solution to the problem can be constructed from optimal
solutions of its subproblems.
o Example: In the shortest path problem, the shortest path to a destination can be found
by combining the shortest paths to intermediate points.
3. Memoization
o Definition: A top-down approach where results of subproblems are stored (cached) to
avoid redundant calculations.
o Example: Storing Fibonacci numbers once calculated.
4. Tabulation
o Definition: A bottom-up approach where solutions to subproblems are computed and
stored in a table.
o Example: Filling in a table for Fibonacci numbers starting from the base cases up to
the desired number.
Search Trees
• Definition: Data structures that store data in a hierarchical manner, allowing for efficient
searching, insertion, and deletion.
• Types:
o Binary Search Tree (BST): Each node has at most two children; left child is less than
the parent, and right child is greater.
o Balanced Trees: Such as AVL trees or Red-Black trees, which maintain balance to
ensure efficient operations.
• Operations: Average time complexity for search, insert, and delete is O(log n).
Heaps
• Definition: A special tree-based structure that satisfies the heap property, which can be a max-
heap or min-heap.
• Properties:
o Max-Heap: The value of each parent node is greater than or equal to its children.
o Min-Heap: The value of each parent node is less than or equal to its children.
• Uses: Efficiently implements priority queues and is used in algorithms like Heap Sort.
• Operations: Insertion and deletion of the root node take O(log n) time.
Hashing
• Definition: A technique that converts data into a fixed-size value (hash code) to enable fast
data retrieval.
• Hash Table: A data structure that uses a hash function to map keys to values.
• Collision Handling:
o Chaining: Storing multiple items in the same bucket using a linked list.
o Open Addressing: Finding another open slot in the array for the colliding item.
• Time Complexity: Average case for search, insert, and delete is O(1), but can degrade to O(n)
in the worst case due to collisions.
Summary
Graph Algorithms
• Definition: Algorithms designed to solve problems related to graph structures (nodes and
edges).
• Key Concepts: Includes traversal (DFS, BFS), searching for shortest paths, and detecting
cycles.
Shortest Paths
• Definition: Algorithms that find the shortest path between nodes in a graph.
• Common Algorithms:
o Dijkstra's Algorithm: Finds the shortest path from a source to all other nodes in a
weighted graph with non-negative weights. Time complexity: O((V + E) log V) with a
priority queue.
o Bellman-Ford Algorithm: Handles graphs with negative weights and finds the
shortest path from a single source. Time complexity: O(V * E).
o Floyd-Warshall Algorithm: Computes shortest paths between all pairs of nodes. Time
complexity: O(V³).
Sparse Graphs
• Definition: Graphs with relatively few edges compared to the number of vertices (E << V²).
• Properties: Efficient storage using adjacency lists rather than adjacency matrices.
• Implication: Many graph algorithms perform better on sparse graphs, as they can avoid
unnecessary computations associated with the large number of potential edges.
String Matching
• Definition: Algorithms used to find occurrences of a substring (pattern) within a larger string
(text).
• Common Algorithms:
o Naive Approach: Checks all possible positions for the pattern in the text. Time
complexity: O(n * m).
o Knuth-Morris-Pratt (KMP) Algorithm: Preprocesses the pattern to skip unnecessary
comparisons, improving efficiency. Time complexity: O(n + m).
o Rabin-Karp Algorithm: Uses hashing to find any one of a set of patterns in the text.
Time complexity: O(n + m) on average.
Summary
• Graph Algorithms: Solve problems related to graph structures, including traversal and
pathfinding.
• Shortest Paths: Focus on finding the minimal distance between nodes, using algorithms like
Dijkstra’s and Bellman-Ford.
• Sparse Graphs: Characterized by fewer edges, allowing for more efficient algorithms and data
storage.
• String Matching: Techniques to locate substrings within larger strings efficiently.
Complexity classes are categories used to classify computational problems based on the
resources required to solve them, primarily focusing on time and space. Understanding these
classes helps in analyzing the efficiency of algorithms and determining the feasibility of
solving problems.
1. P (Polynomial Time)
o Definition: The class of decision problems that can be solved by a deterministic Turing
machine in polynomial time.
o Example: Sorting algorithms (like Merge Sort and Quick Sort) and finding the greatest
common divisor.
2. NP (Nondeterministic Polynomial Time)
o Definition: The class of decision problems for which a solution can be verified in
polynomial time by a deterministic Turing machine.
o Example: The Boolean satisfiability problem (SAT) and the Hamiltonian path
problem.
3. NP-Complete
o Definition: A subset of NP problems that are at least as hard as the hardest problems in
NP. If any NP-complete problem can be solved in polynomial time, then all problems
in NP can be solved in polynomial time.
o Example: Traveling salesman problem, 3-SAT, and the clique problem.
4. NP-Hard
o Definition: Problems that are at least as hard as the hardest problems in NP. They may
not be decision problems and do not have to belong to NP.
o Example: The halting problem and the optimization version of NP-complete problems.
5. BPP (Bounded-error Probabilistic Polynomial Time)
o Definition: The class of decision problems that can be solved by a probabilistic Turing
machine in polynomial time, with a bounded error probability.
o Example: Randomized algorithms like the Miller-Rabin primality test.
PAST PAPERS QUESTIONS
Solution
Question 2:
Part-a:
Part-b:
Question 3:
Part-a:
• Formal definition: f(n) = O(g(n)) if there exist positive constants c and n₀ such that
f(n) ≤ cg(n) for all n ≥ n₀.
• Examples:
o f(n) = 2n² and g(n) = n²: f(n) = O(g(n)) because 2n² ≤ 2n² for all n ≥
1.
o f(n) = n log n and g(n) = n²: f(n) = O(g(n)) because n log n ≤ n² for
all n ≥ 1.
Part-b:
• Formal definition: f(n) = Ω(g(n)) if there exist positive constants c and n₀ such that
f(n) ≥ cg(n) for all n ≥ n₀.
• Examples:
o f(n) = n² and g(n) = n: f(n) = Ω(g(n)) because n² ≥ n for all n ≥ 1.
o f(n) = 2^n and g(n) = n²: f(n) = Ω(g(n)) because 2^n ≥ n² for all n ≥
4.
Question 4:
Part-I:
a) T(n) = T(n - 1) + n:
b) T(n) = 2T(n - 1) + 1:
Part-II:
b) T(n) = 4T(n/2) + n:
Question 5:
Part-a:
• Directed graph:
• Adjacency matrix:
o [Matrix representation of the directed graph]
Part-b:
Bellman-Ford Algorithm
Pseudocode:
Explanation:
1. Initialization:
o Set the distance to the source vertex to 0.
o Set the distance to all other vertices to infinity.
2. Relaxation:
o Iterate over the edges of the graph V - 1 times (where V is the number of vertices).
o For each edge (u, v), check if the distance to v can be shortened by going through u.
If so, update the distance to v.
3. Negative Cycle Detection:
o After the main loop, iterate over the edges again to check for negative cycles. If a
distance can still be shortened, it indicates a negative cycle.
1. Input:
o The given graph (adjacency list or matrix).
o The source vertex S.
2. Run the algorithm:
o Call the BellmanFord function with the graph and S as input.
3. Output:
o If no negative cycle is detected, the dist array will contain the shortest distances from
vertex S to all other vertices.
o If a negative cycle is detected, the algorithm will print a message indicating this.
Example:
A -- 1 --> B -- 2 --> C
| |
| v
| D
| ^
| |
v |
E -- 3 --> F
To find the shortest paths from vertex A, we can apply the Bellman-Ford algorithm:
• dist[A] = 0
• dist[B] = 1
• dist[C] = 3
• dist[D] = 2
• dist[E] = 4
• dist[F] = 7
These values represent the shortest distances from vertex A to each other vertex in the graph.