Algorithms — Detailed Guide
Contents: Linear Search, Binary Search, Bubble Sort, Insertion Sort, Selection Sort, Quick Sort, Merge Sort,
Knapsack (Fractional & 0/1), Job Sequencing (Deadlines), Kruskal's MST
1. Linear Search
What: A simple search that checks each element of the array in order until it finds the target or reaches the end.
Intuition: Walk from left to right; stop when you find the item.
Use-case: Unsorted arrays or small lists; simple and predictable.
Pseudocode (iterative):
for i from 0 to n-1:
if A[i] == target:
return i # found
return -1 # not found
Trace example: A = [7, 2, 9, 4], target = 9
Step 1: i=0, compare 7 with 9 -> not equal
Step 2: i=1, compare 2 with 9 -> not equal
Step 3: i=2, compare 9 with 9 -> equal -> return 2
2. Binary Search
What: Fast search for sorted arrays. Repeatedly split the search interval in half.
Intuition: Compare target to middle element; discard half where target cannot lie.
Requirement: The array must be sorted.
Pseudocode (iterative):
low = 0; high = n - 1
while low <= high:
mid = (low + high) // 2
if A[mid] == target: return mid
elif A[mid] < target: low = mid + 1
else: high = mid - 1
return -1
Trace example: A = [1, 3, 5, 7, 9], target = 7
Step: low=0, high=4 -> mid=2, A[2]=5 (<7) -> low=3
Step: low=3, high=4 -> mid=3, A[3]=7 -> found at index 3
3. Bubble Sort (optimized)
What: Simple comparison sort that repeatedly swaps adjacent elements when out of order.
Intuition: Large values 'bubble' to the end after each pass. Can stop early if a pass makes no swaps.
Characteristics: Stable, in-place, easy to implement but O(n^2) time in general.
Pseudocode (optimized):
for pass from 1 to n-1:
swapped = false
for i from 0 to n-pass-1:
if A[i] > A[i+1]:
swap A[i], A[i+1]
swapped = true
if not swapped: break
Trace example: A = [4, 2, 3, 1]
Pass 1: compare (4,2) -> swap -> [2,4,3,1]
compare (4,3) -> swap -> [2,3,4,1]
compare (4,1) -> swap -> [2,3,1,4]
Pass 2: compare (2,3) -> ok
compare (3,1) -> swap -> [2,1,3,4]
Pass 3: compare (2,1) -> swap -> [1,2,3,4] -> sorted
4. Insertion Sort
What: Builds the final sorted array one item at a time by inserting elements into the correct place in the sorted
prefix.
Intuition: Like sorting playing cards in your hands — pick one and insert it into the right position.
Characteristics: Stable, in-place, works well for small or nearly-sorted data.
Pseudocode:
for i in range(1, n):
key = A[i]
j = i - 1
while j >= 0 and A[j] > key:
A[j+1] = A[j]
j = j - 1
A[j+1] = key
Trace example: A = [4, 2, 3, 1]
i=1: key=2, shift 4 -> [2,4,3,1]
i=2: key=3, shift 4 -> [2,3,4,1]
i=3: key=1, shift 4,3,2 -> [1,2,3,4]
5. Selection Sort
What: Repeatedly selects the minimum element from the unsorted portion and swaps it to the front.
Intuition: Find the smallest remaining item and put it in the next slot.
Characteristics: Not stable by default, in-place, performs the same number of comparisons regardless of input.
Pseudocode:
for i from 0 to n-2:
minIndex = i
for j from i+1 to n-1:
if A[j] < A[minIndex]:
minIndex = j
swap A[i], A[minIndex]
Trace example: A = [4, 2, 3, 1]
i=0: minIndex=3 (value 1) -> swap -> [1,2,3,4]
i=1: already smallest at index 1 -> no swap -> [1,2,3,4]
6. Quick Sort (Lomuto partition)
What: Fast divide-and-conquer sort. Choose a pivot, partition array, sort partitions recursively.
Intuition: Put pivot in its final position; everything left is <= pivot and right is > pivot, then sort subarrays.
Characteristics: Average O(n log n), worst O(n^2) for bad pivots — use random pivot to avoid worst-case. Not
stable by default.
Pseudocode (Lomuto partition):
function partition(A, low, high):
pivot = A[high]
i = low - 1
for j = low to high - 1:
if A[j] <= pivot:
i = i + 1
swap A[i], A[j]
swap A[i+1], A[high]
return i+1
function quicksort(A, low, high):
if low < high:
p = partition(A, low, high)
quicksort(A, low, p-1)
quicksort(A, p+1, high)
Trace example (partition step): A = [5, 2, 9, 1, 5, 6], pivot = 6
Initial: low=0, high=5, pivot=6, i=-1
j=0: A[0]=5 <=6 -> i=0 swap A[0]<->A[0] -> [5,2,9,1,5,6]
j=1: A[1]=2 <=6 -> i=1 swap -> [5,2,9,1,5,6]
j=2: A[2]=9 >6 -> no change
j=3: A[3]=1 <=6 -> i=2 swap A[2]<->A[3] -> [5,2,1,9,5,6]
j=4: A[4]=5 <=6 -> i=3 swap A[3]<->A[4] -> [5,2,1,5,9,6]
After loop swap A[i+1]<->A[high] -> swap A[4] and A[5] -> [5,2,1,5,6,9]
Pivot index returned = 4
7. Merge Sort
What: Stable divide-and-conquer sort that splits array in halves, sorts each half, and merges them.
Intuition: Recursively split until single elements, then merge small sorted lists into bigger sorted lists.
Characteristics: O(n log n) worst/avg/best, stable; requires O(n) extra memory to merge.
Pseudocode:
function merge_sort(A):
if len(A) <= 1: return A
mid = len(A) // 2
left = merge_sort(A[:mid])
right = merge_sort(A[mid:])
return merge(left, right)
function merge(left, right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
extend result with remaining elements
return result
Trace example: A = [4,1,3,2]
Split -> [4,1] and [3,2]
Left [4,1] -> split [4] and [1] -> merge -> [1,4]
Right [3,2] -> split [3] and [2] -> merge -> [2,3]
Merge [1,4] and [2,3] -> [1,2,3,4]
8. Knapsack Problem
Problem: Given items with values and weights and a capacity W, select items to maximize total value without
exceeding W.
Two main variants: Fractional (items divisible) — greedy; 0/1 (items indivisible) — dynamic programming.
Fractional (greedy) outline:
1. For each item compute ratio = value / weight
2. Sort items by ratio descending
3. Take items in that order; if next item doesn't fit fully, take fractional part to fill capacity
0/1 Knapsack (DP) outline:
Let dp[i][w] = max value using first i items and capacity w
Base: dp[0][*] = 0
For i from 1..n:
for w from 0..W:
if weight[i] <= w:
dp[i][w] = max(dp[i-1][w], value[i] + dp[i-1][w-weight[i]])
else:
dp[i][w] = dp[i-1][w]
Fractional example:
Items: (v=60,w=10), (v=100,w=20), (v=120,w=30), Capacity W=50
Ratios: 6.0, 5.0, 4.0 -> take item1 (w=10,v=60), take item2 (w=20,v=100), remaining capacity 20 -> ta
0/1 DP example (small):
Items: (v=60,w=10), (v=100,w=20), (v=120,w=30), W=50
DP table (rows items 0..3, cols 0..50) compute dp[3][50] = 220 (choose items 2 and 3 or 1+3? actual o
9. Job Sequencing with Deadlines (maximize profit)
Problem: Jobs have deadlines and profits; each job takes unit time. Schedule to maximize profit while meeting
deadlines.
Greedy approach works because scheduling the highest-profit jobs first and placing them as late as possible
leaves room for other jobs.
Pseudocode (greedy):
Sort jobs by profit descending
let maxDeadline = max(job.deadline)
create slots[1..maxDeadline] initialized to empty
for each job in sorted order:
for t from job.deadline down to 1:
if slots[t] is empty:
slots[t] = job
break
Example: Jobs = [(J1,d=2,p=100), (J2,d=1,p=19), (J3,d=2,p=27), (J4,d=1,p=25)]
Sort by profit: J1(100), J3(27), J4(25), J2(19)
Slots size = 2 -> try J1 -> place in slot 2; J3 -> place in slot 1; J4 -> no slot; J2 -> no slot
Scheduled jobs: J1 (slot2), J3 (slot1). Total profit = 127
10. Kruskal's Minimum Spanning Tree (MST)
Problem: Given a connected weighted graph, find a subset of edges that connects all vertices with minimum total
weight and no cycles.
Idea: Sort edges by increasing weight and add them if they connect two previously disconnected components
(Union-Find to detect cycles).
Pseudocode:
Sort edges by weight ascending
initialize DSU (disjoint set) for all vertices
MST = empty set
for each edge (u, v, w) in sorted edges:
if find(u) != find(v):
add edge to MST
union(u, v)
return MST
Small example:
Vertices: {A,B,C,D}
Edges sorted by weight: (A,B,1), (C,D,2), (A,C,3), (B,D,4), (A,D,5)
Process:
- Take (A,B,1): union A,B -> MST={(A,B,1)}
- Take (C,D,2): union C,D -> MST+={(C,D,2)}
- Take (A,C,3): find(A) != find(C) -> union -> MST+={(A,C,3)} ; now all vertices connected -> stop (M
Complexities Summary (quick):
Searches: Linear O(n), Binary O(log n)
Sorts: Bubble O(n^2), Insertion O(n^2)/O(n) best, Selection O(n^2), Quick O(n log n) avg, Merge O(n l
Knapsack: Fractional O(n log n), 0/1 DP O(n*W)
Kruskal: O(E log E)
Generated with detailed step-by-step explanations, pseudocode, and worked examples.