1 Data - Structures - Course - 20242
1 Data - Structures - Course - 20242
Data Structures
Course Overview
Course
Syllabus Grading criteria
Grading Tools
Midterm Exam
Due Date
8th week
(100%)
30%
Tools
Mid-Term Exam
Due Date
8th week
(100%)
20%
Final Exam
Next lecture
16th or 17th
30%
40%
Activities (≥5)
Final Exam
Next lecture
16th or 17th
40%
40%
week week
Recommended Textbooks
Real-world
applications
Ways to organize, store,
and manage data
efficiently.
What Are Provide structured
Data formats for handling
Structures? data.
Examples: Arrays, Linked
Lists, Stacks, Queues,
Trees, Graphs.
Data structures are crucial because they:
DATA STRUCTURES ARE DATABASES – ORGANIZING OPERATING SYSTEMS – WEB SEARCH ENGINES –
WIDELY USED IN: RECORDS EFFICIENTLY MANAGING PROCESSES STORING AND RETRIEVING
USING TREES AND HASH AND MEMORY WITH DATA QUICKLY USING
TABLES. QUEUES AND STACKS. TREES AND GRAPHS.
An Abstract Data
Specifies what
Type (ADT) defines a
operations can be
data structure
performed, not how
conceptually,
they are
without specifying
implemented.
implementation.
Example: A Stack
ADT defines push
Examples: Stack, and pop operations
Queue, List, Set. but not the
implementation
(array or linked list).
Role of ADTs in Software Design
ADTS ARE IMPORTANT IN ENCAPSULATE DATA – HIDES ENHANCE MODULARITY – IMPROVE REUSABILITY –
SOFTWARE DEVELOPMENT IMPLEMENTATION DETAILS, MAKES PROGRAMS EASIER PREDEFINED STRUCTURES
BECAUSE THEY: SIMPLIFYING COMPLEXITY. TO MAINTAIN AND UPDATE. CAN BE USED ACROSS
MULTIPLE PROJECTS.
C++ Syntax and Memory Management
Recap
Template
Functions Benefits of
& Class reusable code
Templates
Examples of
function and class
templates
Templates in C++ allow
writing generic code that
works with any data type.
to
Templates Avoid writing separate
functions for different
types.
Reusable
Increases flexibility – Works
Code with different data types.
int main() {
cout << add(5, 10) << endl; // Works with integers
cout << add(5.5, 2.5) << endl; // Works with floating points
return 0;
}
• Example of a generic class:
•
template <class T>
class Box {
private:
Class T value;
public:
Box(T v) { value = v; }
void show() { cout << "Box contains: " <<
int main() {
intBox.show();
strBox.show();
}
• Time complexity (Big-O
Algorithm notation)
• Logarithms and series
Analysis • Running time calculations
• Worst-case, best-case, and
average-case analysis
Algorithm
Analysis
Analysis
Unsorted List
(Array) –
Introduction
List (Array) –
Introduction Searching takes O(n) time (linear
search).
Use Cases:
int main() {
C++ Example: int arr[5] = {10, 20, 30, 40, 50};
int n = 5;
Deleting an // Delete element at index 2 (30)
Element for(int i = 2; i < n-1; i++) {
arr[i] = arr[i+1];
}
n--;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
// Modify value
arr[2] = 99;
cout << "Modified element at index 2: " << arr[2] << endl;
}
Unsorted List (Array)
– Implementation
(Cont’d)
• Optimizing performance
• Code examples and exercises
Unsorted List (Array)
– Implementation
(Cont’d)
Further improvements for performance
optimization.
• Efficient Insertion:
• Insert at the end of the array (O(1)
time complexity).
• Fast Deletion (Lazy Deletion):
Optimizing • Instead of shifting elements, mark
deleted items to improve efficiency.
Performance • Resizing the Array (Dynamic
Arrays):
• Use dynamic arrays (e.g., vector in
C++) to automatically resize when
needed.
C++ Example: Using
Dynamic Array (Vector)
•
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> arr = {10, 20, 30, 40, 50};
arr.push_back(60); // Efficient insertion at end (O(1))
Code
Examples
and Modify Exercise 2: Modify your deletion function to use lazy deletion
instead of shifting.
Exercises
Comparison
Why use
with
sorted lists?
unsorted lists
Sorted List (Array) – Introduction
• A sorted list stores elements in a specific
order (ascending/descending).
• Improves search efficiency.
• Allows binary search (O(log n) time
complexity).
• Useful in databases and search engines.
Why Use Sorted
Lists?
• Faster Searching – Binary search works in O(log n)
compared to O(n) in unsorted lists.
• Better Organization – Data is structured and easier
to retrieve.
• Easier Merging – Combining multiple sorted lists is
more efficient.
Feature Sorted List (Array) Unsorted List
(Array) Comparison:
Insertion Slow (O(n)) -
maintains order
Fast (O(1)) - added
at the end
Sorted vs.
Search Fast (O(log n) with Slow (O(n), Linear
Unsorted
Binary Search) Search)
Lists
Deletion Requires shifting Requires shifting
(O(n)) (O(n))
Insert in }
}
arr[i + 1] = value; // Insert element
n++;
List }
insertSorted(arr, n, 40);
for (int i = 0; i < n; i++) cout << arr[i] << " ";
Operation Time Complexity
Insertion O(n) (due to shifting)
Complexity
Analysis
Search O(log n) (Binary Search)
Binary • Steps:
1. Find the middle element.
2. If it matches the target, return the
index.
Search 3. If the target is smaller, search the
left half.
4. If the target is larger, search the
Implementation }
}
return -1;
•
struct Node {
int data;
Node* next;
};
Adding a Node
(At the
Beginning)
•
void insertAtBeginning(Node*&
head, int value) {
Node* newNode = new
Node();
newNode->data = value;
newNode->next = head;
head = newNode;
}
Deleting a Node
•
void printList(Node* head) {
while (head != nullptr) {
cout << head->data << " ";
head = head->next;
}
}
Insert in }
return;
}
current = current->next;
Order }
newNode->next = current->next;
current->next = newNode;
When data
must stay sorted
automatically.
When to
Use a
Priority-based
Sorted applications like
task scheduling.
Linked
List?
Databases
where maintaining
order is crucial.
Revision for
Midterm Exam
• Exam instructions
• Time management tips
The Stack ADT
• Definition and
characteristics
• Representation methods
• Operations on stacks
(push, pop, peek)
The Stack ADT
• Array-Based Stack –
Fixed size, fast operations.
• Linked List-Based
Stack – Allows dynamic
memory allocation, no
size limitation.
Time
Operation Description Complexity
Push Adds an element
to the top of the
O(1) Operations
Pop
stack
#define MAX 5
class Stack {
int top;
int arr[MAX];
public:
Stack() { top = -1; }
Stack
arr[++top] = value;
}
void pop() {
if (isEmpty()) { cout << "Stack Underflow
}
top--;
int peek() {
if (!isEmpty()) return arr[top];
return -1;
}
};
int main() {
Stack s;
s.push(10);
s.push(20);
cout << "Top element: " << s.peek() << endl;
s.pop();
cout << "Top element after pop: " << s.peek() << endl;
}
Undo/Redo in
text editors.
Expression
evaluation (postfix,
infix, prefix
conversion).
Array
Implementation
of Stacks
C++
using namespace std;
#define MAX 5
class Stack {
int top;
int arr[MAX];
public:
Example:
Stack() { top = -1; }
void pop() {
Static
if (top == -1) { cout << "Stack Underflow
"; return; }
top--;
}
int main() {
Stack
Stack s;
s.push(10);
s.push(20);
cout << "Top element: " << s.peek() << endl;
s.pop();
cout << "Top after pop: " << s.peek() << endl;
}
• Static Allocation: Memory is
allocated at compile time (fixed-
size array).
Memory • Dynamic Allocation:
Memory is allocated at runtime
Allocation using `new` or `malloc()`.
Strategies • Static arrays are faster but
limited in size.
• Dynamic arrays provide
flexibility but require memory
management.
•
#include <iostream>
using namespace std;
C++
class Stack {
int* arr;
int top, capacity;
public:
Stack(int size) {
capacity = size;
arr = new int[capacity];
top = -1;
Example:
}
}
top--;
class Stack {
int* arr;
int top, capacity;
public:
Stack(int size) {
capacity = size;
arr = new int[capacity]; // Allocate memory dynamically
top = -1;
}
C++ Example: ~Stack() { delete[] arr; } // Free memory when object is destroyed
Dynamic Stack
if (top == capacity - 1) { cout << "Stack Overflow
"; return; }
arr[++top] = value;
}
}
top--;
int main() {
Stack s(5);
s.push(10);
s.push(20);
cout << "Top element: " << s.peek() << endl;
s.pop();
cout << "Top after pop: " << s.peek() << endl;
}
• Dynamic stacks provide
flexibility and efficient
memory management.
Key • Memory is allocated at
runtime, preventing
Takeaways wastage.
• Ideal for applications
where the maximum stack
size is unknown.
Linked List • Benefits over array-based
Implementation stacks
of Stacks • Memory efficiency
considerations
Linked List
Implementation
of Stacks
Using linked lists to
implement dynamic stacks.
Benefits • Dynamic Size – No fixed size,
grows/shrinks dynamically.
Over •
•
Efficient Memory Usage –
Memory allocated only when needed.
No Overflow (Unless Memory is
Array- •
Full) – Unlike array stacks, no size
limit.
No Need for Resizing – Avoids
costly array resizing.
Based • Extra Memory Overhead –
Requires extra storage for pointers.
• Slower Access – Linked list
Stacks traversal is slower than array indexing.
Memory Efficiency
Considerations
• Linked list stacks consume more memory due to
pointer storage.
• When to use linked list stacks?
• - When frequent insertions/deletions are needed.
• - When memory availability varies (allocates only what
is needed).
• - When the stack size is unpredictable.
•
#include <iostream>
using namespace std;
struct Node {
int data;
Node* next;
};
class Stack {
private:
Node* top;
public:
Stack() { top = nullptr; }
C++ }
newNode->next = top;
top = newNode;
Implementation:
void pop() {
if (top == nullptr) {
cout << "Stack Underflow
";
return;
int peek() {
if (top != nullptr) return top->data;
return -1;
}
int main() {
Stack s;
s.push(10);
s.push(20);
cout << "Top element: " << s.peek() << endl;
s.pop();
cout << "Top after pop: " << s.peek() << endl;
}
• Linked lists are useful for
dynamic stacks where size
changes frequently.
Key • Extra memory overhead
exists due to storing pointers.
Takeaways • Use linked list stacks when
insertions and deletions are
more frequent than direct
access needs.
• Parsing expressions
Applications
• Evaluating arithmetic
of Stacks expressions
• Other real-world uses
Applications
of Stacks
Common uses of stacks in programming
and real-world applications.
• Stacks help in parsing
expressions in
programming languages
and compilers.
Parsing
• Used for syntax
Expressions checking (e.g., ensuring
balanced parentheses).
• Helps convert
expressions between infix,
postfix, and prefix notation.
•
C++
#include <iostream>
#include <stack>
using namespace std;
Checking else if (ch == ')' && !s.empty() && s.top() == '(') s.pop();
else if (ch == '}' && !s.empty() && s.top() == '{') s.pop();
else if (ch == ']' && !s.empty() && s.top() == '[') s.pop();
else return false;
Balanced }
}
return s.empty();
}
string expr = "{[()]}";
cout << (isBalanced(expr) ? "Balanced" : "Not Balanced") << endl;
Stacks are used in
evaluating mathematical
expressions.
Example:
for (char ch : exp) {
if (isdigit(ch)) s.push(ch - '0');
else {
int val2 = s.top(); s.pop();
int val1 = s.top(); s.pop();
Expression
case '/': s.push(val1 / val2); break;
}
}
}
return s.top();
Evaluation }
int main() {
string postfix = "23*5+";
cout << "Result: " << evaluatePostfix(postfix) << endl;
}
Other • Undo/Redo in text
editors.
Real- • Function call
management (Recursion).
Types of •
Out.
Circular Queue – The rear
connects to the front, making
Queues efficient use of memory.
• Priority Queue – Elements
are removed based on
priority, not order.
C++ Implementation: Queue
•
#include <iostream>
using namespace std;
#define SIZE 5
class Queue {
int arr[SIZE], front, rear;
public:
Queue() { front = -1; rear = -1; }
void dequeue() {
if (isEmpty()) { cout << "Queue Underflow
"; return; }
front++;
}
int peek() {
if (!isEmpty()) return arr[front];
return -1;
}
};
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
cout << "Front element: " << q.peek() << endl;
q.dequeue();
cout << "Front after dequeue: " << q.peek() << endl;
}
• Queues follow the FIFO
principle, making them useful
for scheduling and buffering.
Key • Different types of queues
exist for different applications
Takeaways (FIFO, circular, priority).
• Used in CPU scheduling,
network buffering, and real-
world task management.
Array
• Enqueue and dequeue
Implementation
operations
of Queues
• Efficiency comparison
Array
Implementation
of Queues
• Using arrays to implement
a queue with enqueue
and dequeue operations.
Enqueue (Insertion at Rear): Adds a new element at
the rear of the queue.
C++
using namespace std;
#define SIZE 5
class Queue {
int arr[SIZE], front, rear;
public:
Example:
Queue() { front = -1; rear = -1; }
Queue
if (front == -1) front = 0;
arr[++rear] = value;
}
void dequeue() {
if (isEmpty()) { cout << "Queue Underflow
"; return; }
front++;
Using
}
int peek() {
if (!isEmpty()) return arr[front];
return -1;
}
};
Arrays
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
cout << "Front element: " << q.peek() << endl;
q.dequeue();
cout << "Front after dequeue: " << q.peek() << endl;
}
Efficiency Comparison
• Memory management
benefits
• Handling dynamic
queues
Linked List
Implementation
of Queues
• Using linked lists to
implement dynamic
queues with efficient
memory management.
Memory Management Benefits
C++
struct Node {
int data;
Node* next;
};
class Queue {
private:
Node *front, *rear;
Example: public:
Queue() { front = rear = nullptr; }
Queue
if (!rear) {
front = rear = newNode;
return;
}
rear->next = newNode;
rear = newNode;
}
Using
void dequeue() {
if (!front) {
cout << "Queue Underflow
";
return;
}
Node* temp = front;
front = front->next;
Linked
if (!front) rear = nullptr;
delete temp;
}
int peek() {
if (front) return front->data;
return -1;
}
List
};
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
cout << "Front element: " << q.peek() << endl;
q.dequeue();
cout << "Front after dequeue: " << q.peek() << endl;
}
• Linked list queues are
flexible and handle
dynamic memory
Key •
efficiently.
No fixed size, no
Takeaways wasted space, unlike
arrays.
• Best for applications
with frequent insertions
and deletions.
Applications • Process scheduling
of Queues • Networking (packet
processing)
Applications of
Queues
while (!processQueue.empty()) {
cout << "Executing: " <<
processQueue.front() << endl;
processQueue.pop();
}
}
Networking (Packet Processing)
Queues manage network Packets are processed in Routers and switches use
packet transmission and order (FIFO) before being queues to handle network
processing. sent to their destination. traffic efficiently.
•
#include <iostream>
#include <queue>
int main() {
Example:
queue<int> packetQueue;
Packet packetQueue.push(102);
packetQueue.push(103);
while (!packetQueue.empty()) {
Recursion is useful for solving Tail recursion is more memory- Used in algorithms like tree
problems that can be divided efficient than standard traversal, divide & conquer,
into smaller instances. recursion. and backtracking.
Designing
• Common recursive
Recursive patterns
Solutions • Recursion vs. iteration
Designing • Understanding recursive
Recursive patterns and when to use
recursion vs. iteration.
Solutions
Direct Recursion – Function calls itself
directly (e.g., Factorial).
int fibonacciIterative(int n) {
Example: int a = 0, b = 1, next;
for (int i = 2; i <= n; i++) {
next = a + b;
Iterative }
a = b;
b = next;
Fibonacci }
return b;
of •
levels are filled except possibly
the last.
Perfect Binary Tree –
struct Node {
int data;
Node* left;
Node* right;
};
C++ Example: }
Implementing
else root->right = insert(root->right, value);
return root;
}
a BST
if (root == nullptr) return;
inorderTraversal(root->left);
cout << root->data << " ";
inorderTraversal(root->right);
}
int main() {
Node* root = nullptr;
root = insert(root, 50);
insert(root, 30);
insert(root, 70);
insert(root, 20);
insert(root, 40);
insert(root, 60);
insert(root, 80);
Binary trees organize data BSTs efficiently store and retrieve Used in search algorithms,
hierarchically with at most two ordered data using the left- databases, and file systems.
children per node. smaller, right-larger rule.
Basic
• Insert, delete, search
Operations operations
on BSTs • Efficiency of BSTs
Basic Operations on
BSTs
• Understanding insert, delete,
search operations, and
efficiency of BSTs.
Insert, Delete, and
Search Operations
• Insert Operation:
• - If value < root, insert in left subtree.
• - If value > root, insert in right subtree.
• Search Operation:
• - If value matches node, return node.
• - If value is smaller, search left subtree.
• - If value is larger, search right subtree.
• Delete Operation Cases:
• - Leaf Node: Remove directly.
• - One Child: Replace node with child.
• - Two Children: Replace with inorder successor.
Best Case
(Balanced Worst Case Efficiency
Operation BST) (Skewed BST)
Insertion O(log n) O(n) of BSTs
Search O(log n) O(n)
struct Node {
int data;
Node* left;
Node* right;
};
C++
return new Node{value, nullptr, nullptr};
}
Example:
}
Insert,
while (root->left) root = root->left;
return root;
}
Search,
else {
if (!root->left) return root->right;
if (!root->right) return root->left;
Node* temp = findMin(root->right);
root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
Delete in
}
struct Node {
int data;
Node* left;
Node* right;
};
C++
Node* insert(Node* root, int value) {
if (!root) return createNode(value);
if (value < root->data) root->left = insert(root->left, value);
else root->right = insert(root->right, value);
return root;
}
Example:
if (!root) return;
inOrder(root->left);
cout << root->data << " ";
inOrder(root->right);
}
BST
preOrder(root->left);
preOrder(root->right);
}
Traversals
}
int main() {
Node* root = nullptr;
root = insert(root, 50);
insert(root, 30);
insert(root, 70);
insert(root, 20);
insert(root, 40);
insert(root, 60);
insert(root, 80);
Linear Lists
BINARY SEARCH TREE LINEAR LISTS
FEATURE (BST) (ARRAY/LINKED LIST)
Efficiency for Large Fast for dynamic Fast for static data
Datasets data
• Use BSTs When:
• - Fast searching, insertion, and deletion
When to •
are required.
- Data is dynamic and frequently
updated.
Use BSTs • - Elements need to be kept sorted
automatically.
vs. Arrays •
•
Use Arrays When:
- Random access is needed (O(1) access
time).
or Linked • - The dataset is static and doesn't
change often.
Lists •
•
Use Linked Lists When:
- Frequent insertions and deletions are
required.
• - The structure needs flexibility in size.
C++ •
// BST Search (Logarithmic Time)
Node* search(Node* root, int value) {
Example: if (!root || root->data == value) return
root;
return (value < root->data) ? search(root-
Comparing >left, value) : search(root->right, value);
}
}
if (arr[i] == key) return i;
Search }
return -1;
Key Takeaways
BSTs are great for searching Arrays are better for fast Linked lists are best when
and maintaining sorted data random access and static insertions and deletions
but require balancing. datasets. occur frequently.
More Applications of Data Structures