Data Structures and Algorithms Note
Data Structures and Algorithms Note
COURSE INFORMATION
Course Description
This course introduces fundamental concepts of data structures and algorithms, which
are essential for problem-solving in computer science and software engineering.
Students will learn how to design, analyze, and implement efficient algorithms and
data structures to solve real-world problems. The course covers topics like arrays,
linked lists, stacks, queues, trees, graphs, sorting, searching, and algorithmic
complexity.
Course Aims
5. To prepare students for advanced courses in computer science and software engineering.
Course Objectives
Learning Outcomes
1. Analyze problems and select appropriate data structures and algorithms for their solution.
2. Implement data structures and algorithms in a programming language.
3. Evaluate the performance of algorithms in terms of time and space complexity.
4. Apply data structures and algorithms in practical applications like databases, operating
systems, and software engineering projects.
5. Demonstrate critical thinking and problem-solving skills in the design and analysis of
algorithms.
Course Outline and Weekly Breakdown
Recommended Textbooks
3. "Data Structures and Algorithms in Python" by Michael T. Goodrich, Roberto Tamassia, and
Michael H. Goldwasser
Course Policies
Attendance: Students must attend at least 75% of classes to be eligible for the final exam.
Late Submission: Assignments submitted late will incur a penalty of 10% per day.
Academic Integrity: Plagiarism and cheating will result in disciplinary action, including
Office Hours: Available for consultation by appointment or during scheduled office hours.
Data Structures and Algorithms Explained
DATA STRUCTURES
A data structure is a way of organizing and storing data so that it can be accessed
and modified efficiently. It defines the relationship between the data elements and
provides a means of storing and retrieving them.
Arrays: A collection of elements identified by index or key. They store elements of the same
data type in a contiguous block of memory.
Linked Lists: A linear data structure where elements (nodes) are linked using pointers. Each
node contains data and a reference to the next node.
Stacks: A collection of elements that follow the Last In, First Out (LIFO) principle. Operations
are done at one end, called the "top."
Queues: A collection of elements that follow the First In, First Out (FIFO) principle. Elements
are inserted at the rear and removed from the front.
Trees: A hierarchical data structure that consists of nodes, with each node having a value and
references to its child nodes.
Graphs: A collection of nodes (vertices) and edges connecting them. Graphs can represent
complex relationships like social networks or transportation systems.
Algorithms
Scenario: When using a GPS for driving or walking directions, the system treats the map as a graph,
with roads as edges and intersections as nodes. Algorithms like Dijkstra's are used to find the shortest
route from one location to another, minimizing time or distance.
Scenario: Social media platforms like Facebook or Twitter manage your newsfeed using a queue.
Posts, comments, and notifications are processed in the order they arrive. The oldest posts (first in)
are shown first, and as new content is posted (new in), it replaces older content in the feed.
Algorithm: Hashing
Scenario: When you visit a website, images, text, and other resources may be stored temporarily in
your browser cache. A hash table is used to store these resources by their URLs (keys) and the
corresponding data (values). This allows for fast retrieval of resources on subsequent visits, reducing
load times.
Scenario: When using word processors, design software, or even online platforms, the "undo"
function relies on a stack data structure. Each action you take (e.g., typing, deleting) is pushed onto
the stack. When you press "Undo," the most recent action (last in) is popped off and reverted.
5. Product Recommendation Systems (Sorting and Searching)
Scenario: E-commerce websites like Amazon use sorting algorithms to rank products based on
popularity, reviews, or other criteria. They also use collaborative filtering algorithms to recommend
products to users by analyzing past purchasing patterns (finding similar users or products).
Scenario: When you start typing in a search bar or text input field (e.g., Google Search, email client, or
code editor), the autocomplete feature suggests words or phrases based on the characters you've
typed. A trie data structure is commonly used to efficiently store and search for prefix-based matches,
making autocomplete fast even with large datasets of words.
Scenario: A bank might use a linked list to track the sequence of transactions on an account. Each
node in the linked list represents a transaction (such as deposit or withdrawal) and contains details
like transaction amount, time, and type. Linked lists allow for efficient insertion and deletion,
especially when managing dynamic transaction histories that may vary in length.
Scenario: In real-time online multiplayer games, like those involving team-based interactions, a graph
can represent player connections (nodes) and their relationships (edges). Algorithms like DFS or BFS
can be used to identify groups of connected players (e.g., enemies, allies), analyze distances between
players, or even find the optimal path for players' movements.
9. Flight Reservation System (Heap/Priority Queue)
Scenario: In flight reservation systems, the priority queue (often implemented with a heap) is used to
manage flight tickets based on their urgency or availability. For example, flights may be prioritized
based on factors such as seat availability, flight duration, and pricing, ensuring that customers get the
most relevant options first when booking.
Scenario: In data compression tools like ZIP files or MP3 audio compression, Huffman coding is used
to reduce file size. The algorithm builds a tree where frequently occurring characters (like letters or
data symbols) are assigned shorter codes, and less frequent characters are assigned longer codes.
This minimizes the overall size of the data, making it more efficient for storage or transmission.
Week 1: Introduction to Data Structures &
Algorithms
1. Course Overview
Data structures and algorithms are fundamental to computer science and software
engineering. They enable us to:
Efficiently manage data in software applications (e.g., databases, file systems, and search
engines).
Optimize performance by using the right data structures and algorithms, which can
significantly affect the speed and efficiency of programs.
Solve complex problems in various fields like artificial intelligence, machine learning, web
development, and data analytics.
Improve coding skills for technical interviews and competitive programming.
3.1 Definition
A data structure is a way of organizing and storing data in a computer so that it can be
accessed and modified efficiently. Data structures define the layout of data in memory,
enabling efficient data retrieval, manipulation, and storage.
3.2 Types of Data Structures
Primitive Data Structures: Basic structures like integers, floats, characters, and pointers.
Non-Primitive Data Structures: More complex structures that can hold multiple values.
o Linear Data Structures: Elements are arranged in a sequential manner (e.g., arrays,
linked lists, stacks, queues).
o Non-Linear Data Structures: Elements are arranged in a hierarchical or
interconnected manner (e.g., trees, graphs, heaps).
4.1 Definition
Correctness: The algorithm should produce the correct output for all valid inputs.
Efficiency: The algorithm should be optimized for time and space, minimizing computational
resources.
Clarity: The algorithm should be easy to understand, write, and debug.
Scalability: The algorithm should perform well as the input size grows.
Sorting Algorithms: Arrange data in a specific order (e.g., Bubble Sort, Quick Sort, Merge
Sort).
Searching Algorithms: Find specific elements within data (e.g., Linear Search, Binary Search).
Graph Algorithms: Traverse and analyze graphs (e.g., Depth-First Search (DFS), Breadth-First
Search (BFS)).
Dynamic Programming: Solve complex problems by breaking them into simpler subproblems
(e.g., Fibonacci sequence, Knapsack problem).
5. Algorithm Analysis
Analyzing algorithms helps us understand their efficiency in terms of time and space.
It answers questions like:
7. Practical Examples
<?php
function getFirstElement($arr) {
return $arr[0];
}
<?php
function printAllElements($arr) {
foreach ($arr as $element) {
echo $element . "\n";
}
}
<?php
function binarySearch($arr, $target) {
$left = 0;
$right = count($arr) - 1;
if ($arr[$mid] == $target) {
return $mid; // Target found at index $mid
} elseif ($arr[$mid] < $target) {
$left = $mid + 1; // Search in the right half
} else {
$right = $mid - 1; // Search in the left half
}
}
// Example usage
$sortedArray = [1, 3, 5, 7, 9, 11, 13, 15];
$target = 7;
<?php
function printPairs($arr) {
$length = count($arr);
for ($i = 0; $i < $length; $i++) {
for ($j = 0; $j < $length; $j++) {
echo "(" . $arr[$i] . ", " . $arr[$j] . ")\n";
}
}
}
8. Summary
Data structures and algorithms are essential for optimizing software performance.
Understanding time and space complexity helps in choosing the right approach for solving
problems.
Big O notation is a powerful tool for analyzing the efficiency of algorithms.
9. Practice Questions
10. Assignment
Task: Write a program that takes an array of integers and returns the sorted array using
Bubble Sort. Analyze the time complexity of your solution.
Lecture Note: Arrays & Linked Lists
1. Course Overview
Topic: Arrays & Linked Lists
Duration: 3 hours
Objective: Understand the various types of arrays and linked lists, their operations,
advantages, disadvantages, and practical use cases.
2. Learning Objectives
By the end of this lecture, students should be able to:
3. Introduction
What are Arrays?
An array is a data structure that stores a fixed-size sequence of elements of the same data
type.
Arrays provide random access to elements using an index, which makes retrieval very
efficient.
A linked list is a linear data structure where each element (called a node) contains a value
and a reference (or link) to the next element in the sequence.
Linked lists do not have a fixed size, which allows them to grow or shrink dynamically.
4. Types of Arrays
4.1. Static Arrays
Example in PHP:
<?php
// Static Array
?>
Example in PHP:
<?php
// Dynamic Array
$colors = [];
print_r($colors);
// Output: Array ( [0] => Red [1] => Green [2] => Blue )
?>
Definition: A linked list where each node contains data and a reference to the next node.
Structure:
Example in PHP:
<?php
class Node {
public $data;
public $next;
$this->data = $data;
$this->next = null;
class SinglyLinkedList {
private $head;
$this->head = null;
}
// Add node at the end
$this->head = $newNode;
} else {
$current = $this->head;
$current = $current->next;
$current->next = $newNode;
$current = $this->head;
$current = $current->next;
echo "NULL\n";
}
}
$list->append(10);
$list->append(20);
$list->append(30);
$list->display();
?>
Dynamic size.
Easy insertion and deletion at the beginning.
Definition: A linked list where each node contains data, a reference to the next node, and a
reference to the previous node.
Structure:
o Node: Contains data, a pointer to the next node, and a pointer to the previous node.
o Head: The first node.
o Tail: The last node.
Example in PHP:
<?php
class DoublyNode {
public $data;
public $prev;
public $next;
$this->data = $data;
$this->prev = null;
$this->next = null;
class DoublyLinkedList {
private $head;
$this->head = null;
$this->head = $newNode;
} else {
$current = $this->head;
$current = $current->next;
$current->next = $newNode;
$newNode->prev = $current;
$current = $this->head;
$current = $current->next;
echo "NULL\n";
$doublyList->append(5);
$doublyList->append(15);
$doublyList->append(25);
$doublyList->display();
?>
Advantages of Doubly Linked Lists
7. Summary
Arrays and linked lists are fundamental data structures with distinct advantages and trade-
offs.
Arrays are suitable for scenarios requiring fast access to elements.
Linked lists are ideal for applications requiring frequent insertions and deletions.
8. Practice Questions
1. Implement a function in PHP to reverse a singly linked list.
2. Write a PHP function to search for a specific element in a doubly linked list.
3. Compare the performance of array insertion vs. linked list insertion for 10,000 elements.
Lecture Note: Stacks & Queues
1. Introduction
Stacks
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle.
Think of it like a stack of books: the last book added is the first one to be removed.
Queues
A queue is a linear data structure that follows the First In, First Out (FIFO) principle.
Imagine a line of people waiting for a bus: the first person in line is the first to board the bus.
2. Learning Objectives
By the end of this lecture, you will be able to:
3. Stack Operations
Operation Description Time Complexity
push Adds an element to the top of the stack O(1)
pop Removes the top element O(1)
peek Returns the top element without removing it O(1)
isEmpty Checks if the stack is empty O(1)
Stack Implementation using Arrays in PHP
4. Queues
Queue Operations
Time
Operation Description
Complexity
enqueue Adds an element to the rear of the queue O(1)
dequeue Removes the front element O(1)
Returns the front element without removing
peek O(1)
it
isEmpty Checks if the queue is empty O(1)
Applications of Queues
Scheduling Algorithms: Used in operating systems for task scheduling (e.g., CPU scheduling)..
Breadth-First Search (BFS): Used in graph traversal.
Print Queue: Managing print jobs in printers.
Call Center Systems: Managing customer calls in the order they arrive.
7. Practice Questions
1. Implement a stack that supports the min() function, which returns the minimum element
in the stack in O(1) time.
2. Write a function to reverse a queue using a stack.
3. Implement a circular queue using an array in PHP.
4. Explain the differences between stacks and queues with real-world examples.
Lecture Note: Recursion
1. Introduction to Recursion
What is Recursion?
Key Concepts
Base Case: The condition under which the recursion stops. Without a base case, the function
would call itself indefinitely, leading to a stack overflow.
Recursive Case: The part of the function where it calls itself with new parameters, aiming to
reach the base case.
2. Learning Objectives
By the end of this lecture, you will be able to:
3. Recursive Algorithms
Structure of a Recursive Function
The factorial of a number nnn (denoted as n!n!n!) is the product of all positive integers less
than or equal to nnn.
Mathematically: n!=n×(n−1)!n! = n \times (n - 1)!n!=n×(n−1)!, with the base case being 0!=10!
= 10!=1.
Explanation
If n = 5
o factorial(5) = 5 × factorial(4)
o factorial(4)= 4 × factorial(3)
o factorial(3)= 3 × factorial(2)
o factorial(2)= 2 × factorial(1)
o factorial(1)= 1 × factorial(0)
o factorial(0) = 1 (Base Case)
The time complexity of a recursive function depends on the number of times the function
calls itself.
For the factorial function, the time complexity is O(n) since it makes n calls.
Space Complexity
The space complexity includes the memory used by the function call stack.
Each recursive call adds a new frame to the stack, so the space complexity is also O(n) for the
factorial function.
Explanation
If n=6:
7. Practice Questions
1. Write a recursive function in PHP to reverse a string.
2. Implement a recursive function to find the sum of digits of a given number.
3. Write a PHP function using recursion to determine if a number is a palindrome.
8. Summary
Recursion is a powerful concept where a function calls itself to solve a problem.
Always define a base case to avoid infinite recursion.
Analyze time and space complexity to determine if recursion is efficient for your problem.
Practice with simple examples to build a strong foundation in recursion.
Real-World Analogy
2. Learning Objectives
By the end of this lecture, you will be able to:
1. Understand the concept of hash functions and their role in hash tables.
2. Implement a simple hash table in PHP.
3. Understand collision resolution techniques.
4. Explore real-world applications of hash tables.
3. Hash Functions
What is a Hash Function?
A hash function is a function that takes an input (a key) and returns a fixed-size string of
bytes. The output is typically an index that corresponds to a position in the hash table.
Example: hash(key) → index.
A collision occurs when two different keys produce the same hash index.
Collisions are unavoidable in most hash table implementations, so it's important to have
strategies to handle them.
1. Separate Chaining
Each bucket in the hash table stores a linked list of entries that hash to the same index.
If a collision occurs, the new key-value pair is added to the end of the linked list at that index.
Pros:
Simple to implement.
Dynamically handles an unlimited number of collisions.
Cons:
2. Open Addressing
Instead of storing multiple elements in the same bucket, open addressing looks
for the next available slot using a probing sequence.
Types of Probing:
Pros:
Cons:
Hash tables are commonly used in caches to store frequently accessed data for quick
retrieval.
2. Database Indexing
Hash tables are used in databases to speed up the search for data records using keys.
Hash tables are used to store variables and their values for quick access during program
compilation.
4. Counting Frequencies
Useful for counting the occurrence of elements in a collection (e.g., word frequency in a
document).
Average Case: Most operations in hash tables are O(1) because a good hash function
distributes keys evenly.
Worst Case: Degrades to O(n) if all keys hash to the same index (extremely rare with a good
hash function).
8. Summary
Hash tables provide an efficient way to store and retrieve data with average O(1) time
complexity.
Choosing a good hash function and collision resolution technique is crucial for performance.
Hash tables are widely used in real-world applications like caching, database indexing, and
counting frequencies.
9. Practice Questions
1. Implement a hash table in PHP using linear probing for collision resolution.
2. Write a function to count the frequency of each character in a given string using a hash table.
3. Explain how hash tables are used in the implementation of caching systems.
Lecture Note: Trees
1. Introduction to Trees
What is a Tree?
Basic Terminology:
Node: A single element in the tree, containing data and references to child nodes.
Edge: The connection between nodes.
Root: The topmost node of the tree.
Leaf node: A node with no children.
Parent: A node that has one or more child nodes.
Child: A node that is a descendant of another node.
2. Binary Trees
What is a Binary Tree?
A binary tree is a type of tree in which each node has at most two children (left and right).
Each node in a binary tree has the following properties:
Example:
Types of Binary Trees:
A binary search tree (BST) is a binary tree with the following properties:
1. The left subtree of a node contains only nodes with keys less than the node's key.
2. The right subtree of a node contains only nodes with keys greater than the node's
key.
3. Both the left and right subtrees are also binary search trees.
Example:
10
/ \
5 15
/ \ \
3 7 20
Operations on BST:
1. Insertion: Insert a new key by traversing from the root, following the BST property.
2. Search: Search for a key by comparing with the current node, recursively going to the left or
right subtree.
3. Deletion: Deleting a node in a BST involves three cases:
Tree traversal refers to the process of visiting all the nodes in a tree in a specific order. There
are three main types of depth-first traversal methods:
In-order traversal visits the left subtree, the root node, and then the right subtree.
This traversal method is particularly useful for binary search trees because it visits the nodes
in ascending order.
Example:
10
/ \
5 15
/ \ \
3 7 20
3, 5, 7, 10, 15, 20
Pre-order traversal visits the root node first, then the left subtree, and finally the right
subtree.
Example:
10
/ \
5 15
/ \ \
3 7 20
10, 5, 3, 7, 15, 20
Post-order traversal visits the left subtree, the right subtree, and then the root node.
Example:
10
/ \
5 15
/ \ \
3 7 20
3, 7, 5, 20, 15, 10
5. Simple PHP Code for Binary Search Tree
Here’s a simple implementation of a binary search tree (BST) with in-order
traversal:
6. Summary
Binary Tree: A tree in which each node has at most two children.
Binary Search Tree (BST): A binary tree with properties that make it efficient for search,
insertion, and deletion operations.
Tree Traversals: Methods for visiting all nodes in a tree in a specific order, such as in-order,
pre-order, and post-order.
The in-order traversal of a BST gives the nodes in sorted order.
7. Practice Questions
1. Implement a binary search tree in PHP and perform all three types of traversals.
2. Write a function to search for a node in a binary search tree.
3. Implement the deletion operation for a node in a binary search tree.
Lecture Note: Advanced Trees
Advanced trees are specialized types of binary trees used to optimize certain operations and
solve specific problems that basic binary trees (e.g., Binary Search Trees or BST) cannot
efficiently handle.
These trees have self-balancing properties, meaning they automatically adjust their structure
to maintain certain performance guarantees, such as logarithmic time complexity for search,
insert, and delete operations.
AVL Trees
Red-Black Trees
B-Trees
2. AVL Trees
What is an AVL Tree?
Left Rotation (Single Rotation): Used when the right subtree is taller than the left.
Right Rotation (Single Rotation): Used when the left subtree is taller than the right.
Left-Right Rotation (Double Rotation): Used when a left rotation is followed by a right
rotation.
Right-Left Rotation (Double Rotation): Used when a right rotation is followed by a left
rotation.
Example:
3. Red-Black Trees
What is a Red-Black Tree?
A Red-Black Tree is another type of self-balancing binary search tree with the following
properties:
These rules ensure that the tree remains approximately balanced, providing O(log n)
time complexity for insertion, deletion, and searching operations.
Example:
Operations in Red-Black Trees:
Insertion: Insert a node like in a BST, but after insertion, the tree may need to be recolored
or rotated to maintain the red-black properties.
Deletion: A bit more complex than in AVL trees, but it ensures that the tree remains
balanced.
Search: Same as BST, but with guaranteed O(log n) time complexity due to balancing
properties.
4. B-Trees
What is a B-Tree?
Properties of B-Trees:
1. Node Size: Each node can store more than one key, and the keys are kept sorted within each
node.
2. Balanced Structure: All leaf nodes are at the same level.
3. Order: A B-tree is defined by its order (m), which dictates the maximum number of children
a node can have.
[10 | 20]
/ | \
[5] [15] [25 | 30]
Operations in B-Trees:
Insertion: Inserting keys into a B-tree might require splitting nodes if they exceed the
maximum number of keys.
Deletion: Deletion involves merging nodes if necessary to maintain the B-tree properties.
Search: Efficient due to the multi-level structure and the fact that all leaf nodes are at the
same depth.
5. Applications of Trees
Applications of AVL Trees and Red-Black Trees:
Database Indexing: Both AVL and Red-Black trees are used in databases for fast indexing and
efficient querying.
Memory Management: Used in operating systems for managing memory allocations and
deallocations.
File Systems: Both AVL and Red-Black trees help organize and manage file directories
efficiently.
Applications of B-Trees:
Database Management: B-trees are widely used in databases for indexing large datasets
stored on disk.
File Systems: File systems like FAT and NTFS use B-trees to store file metadata and index files
efficiently.
6. Summary
AVL Trees are self-balancing BSTs where each node maintains a balance factor. They provide
O(log n) search, insertion, and deletion.
Red-Black Trees are balanced BSTs with additional color properties that guarantee O(log n)
time complexity for search, insert, and delete operations.
B-Trees are multi-way balanced search trees used for storing large datasets in external
storage (e.g., databases, file systems).
Advanced trees like AVL, Red-Black, and B-Trees help optimize performance for complex data
management and retrieval tasks.
7. Practice Questions
1. Implement an AVL tree in PHP and perform insertion and search operations.
2. Write a PHP function to perform a left rotation on a node in an AVL tree.
3. Create a simple B-tree implementation to handle insertion and searching.
This lecture covered advanced trees such as AVL trees, Red-Black trees, and B-
trees, their properties, operations, and applications.
Lecture Note: Graphs
1. Introduction to Graphs
What is a Graph?
Graph Terminology:
2. Graph Representations
There are two common ways to represent graphs:
a. Adjacency Matrix:
Where:
b. Adjacency List:
In this representation:
Each vertex points to a list of vertices that are directly connected to it.
3. Graph Traversal
Graph traversal refers to the process of visiting all the nodes in a graph. There are two
main types of graph traversal:
BFS Algorithm:
DFS Algorithm:
Dijkstra's Algorithm finds the shortest path from a source node to all other nodes in a
weighted graph with non-negative edge weights.
It works by visiting nodes in increasing order of their distance from the source node.
1. Initialize the distance to the source node as 0 and all other nodes as infinity.
2. Mark all nodes as unvisited. Start with the source node.
3. For the current node, consider all its unvisited neighbors and calculate their tentative
distances.
4. Choose the unvisited node with the smallest tentative distance and mark it as visited.
5. Repeat until all nodes are visited or the smallest tentative distance is infinity.
class Graph {
private $adjList;
while (!empty($unvisited)) {
$minNode = $this->getMinNode($unvisited, $distances);
unset($unvisited[$minNode]);
return $distances;
}
Comparison-based Sorting: These algorithms compare elements to decide their order (e.g.,
bubble sort, quicksort).
Non-comparison-based Sorting: These algorithms do not compare elements, but instead use
other methods, like counting (e.g., counting sort).
In this lecture, we'll focus on the most commonly used comparison-based sorting
algorithms.
2. Bubble Sort
Bubble sort is one of the simplest sorting algorithms. It works by repeatedly swapping
adjacent elements if they are in the wrong order. This process continues until no more
swaps are needed, which means the list is sorted.
Time Complexity:
3. Insertion Sort
Insertion sort works by dividing the list into a sorted and an unsorted part. It picks
elements from the unsorted part and inserts them into the correct position in the sorted
part.
Time Complexity:
4. Selection Sort
Selection sort works by repeatedly finding the minimum element from the unsorted
part and swapping it with the first unsorted element.
Time Complexity:
5. Merge Sort
Merge sort is a divide and conquer algorithm. It divides the array into two halves,
sorts each half recursively, and then merges the sorted halves.
Time Complexity:
6. Quick Sort
Quick sort is another divide and conquer algorithm. It works by selecting a "pivot"
element from the array, partitioning the other elements into two sub-arrays, and
recursively sorting the sub-arrays.
Time Complexity:
7. Heap Sort
Heap sort is based on the binary heap data structure. It involves building a max-
heap from the input data and then extracting the maximum element from the heap to
sort the array.
Conclusion
Each sorting algorithm has its strengths and weaknesses. Bubble Sort, Insertion Sort,
and Selection Sort are simple and easy to understand but are inefficient for large
datasets. Merge Sort, Quick Sort, and Heap Sort are more efficient for larger
datasets, with quick sort being the most widely used for practical applications.
Lecture Note: Searching Algorithms
In this lecture, we'll cover some basic and advanced searching algorithms, including
linear search, binary search, and searching algorithms on trees and graphs.
2. Linear Search
Linear search, also known as sequential search, is the most straightforward searching
algorithm. It checks each element in the list or array one by one until the desired
element is found or all elements are checked.
Time Complexity:
3. Binary Search
Binary search is an efficient search algorithm that works on sorted data structures
(arrays or lists). It divides the search space into half at each step, making it much
faster than linear search, especially for large datasets.
if ($arr[$mid] == $target) {
return $mid; // Element found
}
Time Complexity:
DFS is a graph and tree traversal algorithm that explores as far as possible along each
branch before backtracking. It is useful for exploring all nodes of a tree or graph.
if ($tree[0] == $target) {
return true; // Target found
}
// Example usage
$tree = [10, [5, null, null], [15, null, null]]; // [value, left_subtree, right_subtree]
$target = 5;
$result = dfs($tree, $target);
echo $result ? "Found" : "Not Found"; // Output: Found
BFS is another tree and graph traversal algorithm, which explores all nodes at the
present depth level before moving on to nodes at the next depth level.
BFS Algorithm:
if (empty($tree)) {
return false;
}
$queue = [$tree];
$currentNode = array_shift($queue);
if ($currentNode[0] == $target) {
if ($currentNode[1]) {
$queue[] = $currentNode[1];
if ($currentNode[2]) {
$queue[] = $currentNode[2];
// Example usage
$target = 5;
DFS in graphs is similar to DFS in trees, but you must keep track of visited nodes to
avoid revisiting the same node.
Similarly, BFS can be used in graphs with the same logic as in trees, ensuring that you
avoid revisiting nodes.
Conclusion
Searching algorithms are essential tools for efficiently finding elements in data
structures. Linear Search is simple but inefficient for large datasets. Binary Search
is more efficient but requires sorted data. For tree and graph structures, DFS and BFS
are widely used, with DFS going deeper into a structure before backtracking and BFS
exploring level by level. Understanding these algorithms helps in building efficient
applications, particularly in areas such as database searching, web searches, and
pathfinding.
Lecture Note: Dynamic Programming
1. Introduction to Dynamic Programming (DP)
Key Concepts:
Overlapping Subproblems: When the same subproblems are solved multiple times.
Optimal Substructure: The optimal solution of the problem can be constructed from the
optimal solutions of its subproblems.
Memoization (Top-Down Approach): This approach solves the problem recursively and
stores the results of subproblems to avoid redundant calculations.
Tabulation (Bottom-Up Approach): This approach solves the problem iteratively, filling up a
table based on previously computed values.
The Fibonacci sequence is a series of numbers where each number is the sum of the
two preceding ones, starting from 0 and 1.
return $memo[$n];
}
// Example usageecho fibonacci(10); // Output: 55
This code memoizes the results of the Fibonacci function to avoid recalculating the
same values multiple times.
The 0/1 Knapsack problem involves selecting items with given weights and values,
such that the total weight does not exceed a given limit, and the total value is
maximized.
Problem Statement:
Recursive Formula:
K(n,W)=max(K(n−1,W),value[n−1]+K(n−1,W−weight[n−1]))K(n, W) =
\max(K(n-1, W), \text{value}[n-1] + K(n-1, W - \text{weight}[n-
1]))K(n,W)=max(K(n−1,W),value[n−1]+K(n−1,W−weight[n−1]))
Where:
K(n,W)K(n, W)K(n,W) is the maximum value for the first n items and weight W.
return $memo[$n][$capacity];
}
// Example usage$weights = [2, 3, 4, 5];$values = [3, 4, 5,
6];$capacity = 5;$n = count($weights);
echo knapsack($weights, $values, $capacity, $n); // Output: 7
In this solution, we use memoization to store the results of subproblems and avoid
redundant calculations.
Fibonacci with Memoization: O(n) because we calculate each Fibonacci number once.
Knapsack Problem with Memoization: O(n * W), where n is the number of items and W is
the maximum weight capacity of the knapsack.
Optimization Problems: Problems like shortest path, longest common subsequence, and
sequence alignment in bioinformatics.
Game Theory: Solving problems like optimal strategies in two-player games.
Resource Allocation: Problems like job scheduling, partition problems, and others requiring
maximizing or minimizing resources.
Lecture Note : Greedy Algorithms
12.1 Introduction to Greedy Algorithms
Greedy algorithms are a class of algorithms that follow the problem-solving heuristic
of making the locally optimal choice at each stage with the hope of finding the global
optimum. The basic idea is to choose the best possible option at each step, without
reconsidering previous choices. Greedy algorithms are efficient, but they don’t always
lead to the optimal solution for every problem. They work best when the problem has
the greedy-choice property (local choices lead to global solutions) and optimal
substructure (optimal solutions to subproblems can be combined to form an optimal
solution to the overall problem).
Key Characteristics:
Make a series of choices by picking the best option available at each step.
Do not reconsider or backtrack (thus faster than exhaustive search algorithms).
Do not guarantee an optimal solution for all problems.
In the coin change problem, given a set of coin denominations and a total amount of
money, the goal is to determine the minimum number of coins needed to make the
total amount.
Greedy Approach:
Always pick the largest coin denomination that does not exceed the remaining amount.
Repeat until the amount is reduced to zero.
let count = 0;
// Example usage
Explanation:
In this approach, the function always chooses the largest coin denomination that
doesn't exceed the remaining amount, reducing the problem to smaller subproblems.
The job scheduling problem involves scheduling a set of jobs, each with a start time,
end time, and profit. The goal is to select a subset of jobs that don’t overlap,
maximizing the total profit.
Greedy Approach:
function jobScheduling(jobs) {
let lastEndTime = 0;
if (job[0] >= lastEndTime) { // Job start time >= last selected job's end
time
selectedJobs.push(job);
return selectedJobs;
// Example usage
const jobs = [[1, 4, 50], [2, 6, 60], [5, 7, 120], [3, 8, 30]]; // [start, finish,
profit]
The jobs are sorted based on their finish time, and jobs are selected greedily by
checking if they do not overlap with the previously selected job.
Lecture Note:Algorithm Complexity
Analysis
13.1 Introduction to Algorithm Complexity Analysis
Time Complexity: The amount of time an algorithm takes to run as a function of the input
size.
Space Complexity: The amount of memory space an algorithm uses as a function of the input
size.
Time complexity gives an estimate of how long an algorithm takes to run based on the
size of the input. It is often expressed using Big O notation, which describes the upper
bound of an algorithm's growth rate. Common time complexities include:
O(1): Constant time – the algorithm’s running time does not depend on the input size.
O(n): Linear time – the running time grows linearly with the input size.
O(log n): Logarithmic time – the running time grows logarithmically with the input size.
O(n^2): Quadratic time – the running time grows quadratically with the input size.
function printArray(arr) {
function bubbleSort(arr) {
let n = arr.length;
bubbleSort(arr);
Input data
Auxiliary space (extra space used by the algorithm)
O(1): Constant space – the algorithm uses a fixed amount of space regardless of the input
size.
O(n): Linear space – the algorithm's space requirement grows linearly with the input size.
Best Case: The minimum amount of time or space an algorithm will take for the best possible
input.
Worst Case: The maximum time or space the algorithm will take for the worst possible input.
Average Case: The expected time or space the algorithm will take over all possible inputs.
Best Case (O(1)): The target element is found at the first index.
Worst Case (O(n)): The target element is not found, or it’s at the last index.
Average Case (O(n)): On average, the algorithm will search half of the list.
13.5 Conclusion