Ultimate Data Structures Notes
Ultimate Data Structures Notes
📚 TABLE OF CONTENTS
1. Prerequisites
Functions
Recursion
Arrays
Pointers
Structures
3. Stack
Stack as ADT
Array Implementation of Stack
Multiple Stacks
4. Queue
Queue as ADT
Array Implementation of Queue
Circular Queue
Priority Queue
Double Ended Queue (Deque)
Multiple Queues
5. Linked List
Concept of Linked List vs Array
Singly Linked List
Doubly Linked List
6. Tree
Tree Concepts and Terms
Binary Tree
<a id="prerequisites"></a>
0. PREREQUISITES
<a id="functions"></a>
Functions
What are functions?
Functions are blocks of code that perform specific tasks. They help us organize our program into
reusable pieces.
Example:
Algorithm Explanation:
Recursion
What is recursion?
When a function calls itself to solve a problem. It breaks a problem into smaller versions of the same
problem.
Recursive Case: The function calls itself with a simpler version of the problem
Algorithm Explanation:
4. For factorial(5):
factorial(5) = 5 * factorial(4)
factorial(4) = 4 * factorial(3)
factorial(3) = 3 * factorial(2)
factorial(2) = 2 * factorial(1)
<a id="arrays"></a>
Arrays
What are arrays?
Collections of similar data items stored at contiguous memory locations.
Random Access: Can access any element directly using its index
Example:
c
2. Access: Calculate address using base address + (index * size of data type)
<a id="pointers"></a>
<a id="structures"></a>
Structures
What are structures?
User-defined data types that allow storing different types of data items together.
Example:
c
2. Recursion
What is recursion and how does it differ from iteration?
What are the essential components of a recursive function?
How does the call stack work during recursive function calls?
3. Arrays
What is an array? How is memory allocated for arrays?
Why is index 0 used for the first element in an array?
4. Pointers
What is a pointer and what does it contain?
5. Structures
What is a structure in C? How does it differ from an array?
How do you access members of a structure?
Can structures contain other structures? Explain.
<a id="introduction"></a>
I. INTRODUCTION
<a id="what-are-data-structures"></a>
Think of it like: If data is like items, then data structures are like different types of containers to store
these items - each with its own way of adding, removing, and accessing items.
<a id="abstract-data-types-adt"></a>
Stack (Last-In-First-Out)
Queue (First-In-First-Out)
List
Tree
<a id="types-of-data-structures"></a>
1. Linear vs Nonlinear:
2. Static vs Dynamic:
3. Primitive vs Non-Primitive:
<a id="operations-on-data-structures"></a>
6. What factors should be considered when choosing a data structure for a specific problem?
7. How does memory allocation differ between static and dynamic data structures?
8. Describe the common operations that can be performed on data structures.
12. Explain how efficiency of operations varies across different data structures.
<a id="stack"></a>
II. STACK
<a id="stack-as-adt"></a>
Stack as ADT
Definition: A stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle.
Real-life example: Think of a stack of plates. You can only take the top plate (last one placed), not
any from the middle.
LIFO (Last-In-First-Out): The last element added is the first one to be removed
Restricted Access: Elements can only be added or removed from one end (top)
Basic Operations:
Push: Add an element to the top
Visual Representation:
│ │
│ C │ ← Top (most recently added)
│ B │
│ A │ ← Bottom (least recently added)
└───────┘
<a id="array-implementation-of-stack"></a>
// Global variables
int stack[MAX]; // Array to store the stack elements
int top = -1; // Index of top element, -1 means empty stack
// Sample usage
int main() {
push(10); // Add 10 to stack
push(20); // Add 20 to stack
push(30); // Add 30 to stack
printf("Top element: %d\n", peek()); // Should print 30
printf("Popped: %d\n", pop()); // Should print 30
printf("Popped: %d\n", pop()); // Should print 20
printf("Top element after pops: %d\n", peek()); // Should print 10
return 0;
}
1. Initialization:
Create an array of fixed size (MAX)
Initialize top = -1 (indicating empty stack)
2. isFull() Operation:
Check if top == MAX-1
If true, stack is full, return 1
Otherwise, return 0
3. isEmpty() Operation:
Check if top == -1
4. push() Operation:
Check if stack is full using isFull()
If full, print "Stack Overflow"
Otherwise:
Increment top
6. peek() Operation:
Check if stack is empty using isEmpty()
<a id="multiple-stacks"></a>
Multiple Stacks
Multiple stacks can be implemented in a single array by:
2. Starting stacks from opposite ends of the array and growing towards the middle:
For two stacks, first stack grows from left to right
[0][1][2][3][4][5][6][7][8][9]
↑ ↑
top1 top2
6. What happens when you try to push an element into a full stack?
7. How is a stack implemented using an array? What are the limitations of this implementation?
8. What is the time complexity of push, pop, and peek operations in a stack?
9. What are the advantages and disadvantages of implementing a stack using an array?
11. Compare the fixed partition and flexible partition methods for implementing multiple stacks.
12. What is stack overflow and stack underflow? When do they occur?
III. QUEUE
<a id="queue-as-adt"></a>
Queue as ADT
Definition: A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle.
Real-life example: Think of people standing in line for movie tickets. The person who came first gets
served first.
FIFO (First-In-First-Out): The first element added is the first one to be removed
Two Ends: Elements are added at one end (rear) and removed from the other end (front)
Basic Operations:
Visual Representation:
Front Rear
↓ ↓
[A][B][C][D][ ][ ][ ][ ]
<a id="array-implementation-of-queue"></a>
// Global variables
int queue[MAX]; // Array to store queue elements
int front = -1; // Index of front element
int rear = -1; // Index of rear element
1. Initialization:
Create an array of fixed size (MAX)
Initialize front = -1 and rear = -1 (indicating empty queue)
2. isFull() Operation:
Check if rear == MAX-1
If true, queue is full, return 1
Otherwise, return 0
3. isEmpty() Operation:
Check if front == -1 OR front > rear
4. enqueue() Operation:
Check if queue is full using isFull()
Otherwise:
If front == -1, set front = 0 (first element)
Increment rear
Place the new item at queue[rear]
5. dequeue() Operation:
Check if queue is empty using isEmpty()
If empty, print "Queue Underflow" and return error value (-1)
Otherwise:
Store the value at queue[front] in a variable
Increment front
If front > rear (last element was dequeued), reset front and rear to -1
Return the stored value
6. getFront() Operation:
Check if queue is empty using isEmpty()
Space Inefficiency: Once the queue is full and elements are dequeued, the front moves forward
leaving unused space at the beginning
Cannot reuse the free space without resetting the queue completely
<a id="circular-queue"></a>
Circular Queue
A circular queue solves the problem of wasted space in a simple queue by making the array circular
(connecting the end to the beginning).
c
#include <stdio.h>
#define MAX 5 // Maximum size of circular queue
// Global variables
int cqueue[MAX]; // Array to store circular queue elements
int front = -1; // Index of front element
int rear = -1; // Index of rear element
// Sample usage
int main() {
enqueue(10); // Add 10 to queue
enqueue(20); // Add 20 to queue
enqueue(30); // Add 30 to queue
enqueue(40); // Add 40 to queue
printf("Front element: %d\n", getFront()); // Should print 10
printf("Dequeued: %d\n", dequeue()); // Should print 10
printf("Dequeued: %d\n", dequeue()); // Should print 20
// Now we can add more elements even though the array is "full"
enqueue(50); // Add 50 to queue
enqueue(60); // Add 60 to queue
printf("Front element after changes: %d\n", getFront()); // Should print 30
return 0;
}
1. Initialization:
Create an array of fixed size (MAX)
Initialize front = -1 and rear = -1 (indicating empty queue)
2. isFull() Operation:
Check if (front == 0 AND rear == MAX-1) OR (front == rear+1)
Otherwise, return 0
3. isEmpty() Operation:
Check if front == -1
If true, circular queue is empty, return 1
Otherwise, return 0
4. enqueue() Operation:
Check if queue is full using isFull()
5. dequeue() Operation:
Check if queue is empty using isEmpty()
If empty, print "Circular Queue Underflow" and return error value (-1)
Otherwise:
Store the value at cqueue[front] in a variable
6. getFront() Operation:
Check if queue is empty using isEmpty()
If empty, print "Circular Queue is empty" and return error value (-1)
Otherwise, return the value at cqueue[front]
<a id="priority-queue"></a>
Priority Queue
A priority queue is a queue where elements