Sorting Searching Algorithms Roshan
Sorting Searching Algorithms Roshan
Hinglish: Sorting matlab data ko order mein lagana. Jaise library mein books alphabetically rakhi hoti
hain, phone contacts A-Z hote hain, leaderboard marks ke hisaab se hota hai — yahi sorting hai! Sorted
data ke saath kaam karna bahut aasaan ho jaata hai.
✓ Better Performance: Many algorithms work better on sorted data — finding duplicates, merging lists,
database queries.
✓ Data Presentation: Leaderboards, product listings, search results — all require sorted data for good
UX.
✓ Foundation for Bigger Problems: Sorting is used inside many complex algorithms — graph
algorithms, string matching, etc.
✓ Interviews!: Every DSA interview asks sorting questions. Understanding sorting shows you
understand algorithms deeply.
KEY FACT The best possible comparison-based sort is O(n log n). This is mathematically proven —
you cannot sort by comparing elements faster than this. Non-comparison sorts can achieve O(n) but
need special conditions (integer data, bounded range).
Hinglish: Big-O matlab "agar n double ho jaaye toh kitna time zyada lagega?" O(n) matlab n double →
time double. O(n²) matlab n double → time 4x. O(log n) matlab n double → time sirf +1 step! Algorithm
choose karte waqt yahi sochna hai.
INTERVIEW TIP What to say in interviews: Always mention Best Case, Average Case, and Worst
Case. For example: Quick Sort is O(n log n) average but O(n²) worst case (when already sorted and
pivot is always last element). Merge Sort is always O(n log n) — guaranteed!
Hinglish: Bubble Sort ek soda bottle jaisa hai — bade bubbles upar aajaate hain! Har pass mein sabse
bada element sahi jagah pe pahunch jaata hai. Jaise queue mein sab log adjust karte hain — agar koi
bada hai peeche wale se, toh swap karo.
34 25 12 22 11 64
11 12 22 25 34 64
Bubble Sort: largest element "bubbles up" to end in each pass
Fig 3.1: Each pass moves the largest unsorted element to its correct position at the end
Step-by-Step Trace
Array: [64, 34, 25, 12, 22, 11]
Java Code
public static void bubbleSort(int[] arr) {
int n = [Link];
swapped = false;
arr[j + 1] = temp;
swapped = true;
WHEN TO USE When to use Bubble Sort: Almost never in production! Only useful for teaching
purposes and when array is nearly sorted (best case O(n) with optimization). For real problems, use
Merge Sort or Quick Sort.
Hinglish: Selection Sort ek topper student jaisa hai — pehle poori class mein sabse kam marks wala
dhundho, usse pehle position pe rakho. Phir baaki class mein se minimum dhundho, doosri position pe
rakho. Baar baar minimum select karo, isliye "Selection" Sort!
POSwith index 0
Pass 1: Find min in [0..4] → 11 at index 4 → swap MIN
64 25 12 22 11
Pass 3: Find min in [2..4] → 22 at index 3 → swap with index 2 POS MIN
11 12 25 22 64
Java Code
public static void selectionSort(int[] arr) {
int n = [Link];
int minIdx = i;
if (minIdx != i) {
arr[minIdx] = arr[i];
arr[i] = temp;
// Swaps: O(n) — very few swaps! Good when write cost is high
KEY INSIGHT Key advantage of Selection Sort: It makes at most O(n) swaps, unlike Bubble Sort
which can make O(n²) swaps. When data write is expensive (e.g., memory wear on SSDs), fewer swaps
= better. But time is always O(n²) regardless.
Hinglish: Insertion Sort bilkul waise hai jaise tum taash (playing cards) sort karte ho haath mein. Ek card
uthao, dekho kahan fit hota hai sorted portion mein, baaki cards seedhe karo, card daalo. Ek ek card
uthate rehte ho jab tak sab sort nahi ho jaate!
Java Code
public static void insertionSort(int[] arr) {
int n = [Link];
int j = i - 1;
j--;
WHEN TO USE When to use Insertion Sort: (1) Small arrays (n < 20) — very fast in practice, (2)
Nearly sorted arrays — best case O(n), (3) Online sorting — elements arrive one by one. Java's TimSort
uses Insertion Sort for small subarrays!
Hinglish: Merge Sort ek "divide and rule" strategy hai. Problem bahut badi hai → do hisson mein baanto
→ har hissa sort karo → phir dono sorted halves ko merge karo. Yeh ek chef jaisa hai jo bada khana
banane ke liye apni team ko alag alag kaam deta hai aur baad mein sab combine karta hai!
38 27 43 3 9 82 10
Original Array
38 27 43 3 9 82 10
Left half Right half
38 27 43 3 9 82 10
[38] [27,43] [3,9] [82,10]
27 38 43 3 9 10 82
Merge → sorted Merge → sorted
3 9 10 27 38 43 82
Final Merge → SORTED!
DIVIDE → CONQUER → MERGE
Merge Sort: split in half until 1 element, then merge in sorted order
Fig 6.1: Merge Sort splits down to single elements, then merges back in sorted order
Java Code
public static void mergeSort(int[] arr, int left, int right) {
private static void merge(int[] arr, int left, int mid, int right) {
int i = 0, j = 0, k = left;
// Time: Always O(n log n) — guaranteed! Best choice for linked lists.
// Stable: YES
WHEN TO USE Merge Sort is the best choice when: (1) Stability is required, (2) Sorting linked lists
(no random access — merge sort doesn't need it), (3) External sorting (data too large for RAM — read
from disk in chunks). Guaranteed O(n log n) — no worst case.
Hinglish: Quick Sort ek leader (pivot) choose karta hai. Phir sab chhote log left side mein jaate hain, sab
bade log right side. Leader apni perfect position pe aa jaata hai. Phir left aur right groups mein yahi
process repeat hoti hai — har baar ek leader apni jagah fix ho jaata hai!
10 80 30 90 40 50 70
10 30 40 50 80 90 70
10 30 40 50 70 80 90
Orange=pivot | Green=less than pivot | Red=greater than pivot
Fig 7.1: Orange=pivot, Green=less than pivot (correct side), Red=greater than pivot
Java Code
public static void quickSort(int[] arr, int low, int high) {
i++;
// Time: Best/Average O(n log n) | Worst O(n²) [sorted array, bad pivot]
// Stable: NO
Always last element Already sorted array → O(n²) Avoid for production
REAL WORLD Quick Sort in practice: Despite O(n²) worst case, Quick Sort is the fastest sorting
algorithm in practice for random data because of excellent cache performance (in-place, contiguous
memory access). Java's [Link]() uses Dual-Pivot QuickSort for primitives!
Hinglish: Heap Sort ek president election jaisa hai. Pehle sabse bade candidate ko top pe laao (build
max heap). Phir wo retire hota hai — end mein jaata hai. Ab baaki mein se naya president chunte hain —
phir retire. Baar baar yahi process jab tak sab sorted na ho jaayein!
90
80 70
50 60 30 40
Array representation:
90 80 70 50 60 30 40
[0] [1] [2] [3] [4] [5] [6]
Parent of i = (i-1)/2
Left child = 2i+1 Heap Sort: Build max-heap → swap root with last → heapify → repeat
Right child =Fig 8.1: Max-Heap structure — parent always >= children. Root is always maximum.
2i+2
Java Code
public static void heapSort(int[] arr) {
int n = [Link];
heapify(arr, n, i);
heapify(arr, i, 0);
if (largest != i) {
// Stable: NO
Hinglish: Socho class mein grades A,B,C,D hain. Count karo kitne A hain, kitne B, kitne C, kitne D. Phir
list mein pehle saare A, phir saare B — sorted! Koi comparison nahi, sirf count!
output[count[arr[i]] - 1] = arr[i];
count[arr[i]]--;
Hinglish: Linear Search ek lost key dhundne jaisa hai — ghar ka har kamra check karo ek ek karke jab
tak key na mile. Simple hai lekin slow! Agar 1000 rooms hain aur key last room mein hai toh 1000 baar
check karna padega!
64 34 25 12 22 11 90
■
64 34 25 12 22 11 90
■
64 34 25 12 22 11 90
■
64 34 25 12 22 11 90
■
Step 5: Check [4]=22 → ✓ FOUND at index 4!
Java Code
public static int linearSearch(int[] arr, int target) {
if (arr[i] == target) {
// Space: O(1)
// For objects:
if (arr[i].equals(target)) return i;
return -1;
WHEN TO USE Advantages of Linear Search: Works on unsorted data. Works with any data type.
Simple to implement. For very small arrays (< 10 elements), faster than Binary Search in practice due to
no setup cost.
Hinglish: Binary Search bilkul dictionary use karne jaisa hai. Page 500 kholo — word "apple" se pehle
hai toh pehli 500 pages mein dekho, nahi toh last 500 mein. Phir aadha again. Baar baar aadha karte
rehte hain jab tak word mile! 1000 pages ki dictionary mein sirf 10 steps lagenge!
2 5 8 12 16 23 38 56 72 91
MID
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
low high
2 5 8 12 16 23 38 56 72 91
MID
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
low high
2 5 8 12 16 23 38 56 72 91
MID
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
low high
Binary Search eliminates HALF the array each step → O(log n) — much faster!
Fig 11.1: Binary Search eliminates half the remaining elements each step — O(log n)
int low = 0;
} else {
return result;
return result;
Small array (n < 20) Insertion Sort Low overhead, fast in practice
No extra memory allowed Heap Sort O(1) space, O(n log n) time
[Link](nums); // [1, 2, 5, 8, 9]
// Sort with custom Comparator (only for Object arrays, not primitives)
// [[1,4],[2,2],[3,1]]
// ["fig","kiwi","apple","banana"]
[Link](list); // [1, 2, 5, 8, 9]
[Link]([Link]());
// By marks ascending
TIMSORT FACT Java's TimSort: Java uses TimSort for object arrays and Collections. TimSort is a
hybrid of Merge Sort and Insertion Sort. It detects already-sorted "runs" and uses Insertion Sort for small
arrays. It is stable, O(n log n) worst case, O(n) best case. Best sorting algorithm for real-world data!
Sorting Questions
A: A STABLE sort preserves the relative order of elements that are equal. If two elements have the same
key, their original order is maintained in the output. UNSTABLE sort may change the relative order of
equal elements. Stable: Bubble Sort, Insertion Sort, Merge Sort, Counting Sort, TimSort. Unstable:
Selection Sort, Quick Sort, Heap Sort. Stability matters when: sorting objects by multiple keys (sort by
marks, then by name — must be stable for the second sort to work correctly).
A: There is no single "best" algorithm — it depends on the use case: Quick Sort: fastest in practice for
random data (cache-friendly, in-place). Merge Sort: when stability required, guaranteed O(n log n), or
sorting linked lists. Insertion Sort: small arrays (< 20), nearly sorted data. Heap Sort: when O(1) space is
required with O(n log n) time. Counting/Radix Sort: when data is integers in a known range — beats O(n
log n)! Java uses Dual-Pivot QuickSort for primitives and TimSort for objects.
Q: What is the time complexity of Merge Sort? Why is it always O(n log n)?
A: Merge Sort is always O(n log n) for best, average, and worst case. The array is divided into halves: log
n levels of division. At each level, we merge n total elements: O(n) work per level. Total: O(n) × O(log n) =
O(n log n). This never degrades because the split is always in half — no pivot selection problem. Space:
O(n) extra for the temporary merge arrays. This guaranteed performance makes it preferred for external
sorting.
A: Worst case occurs when pivot is always the minimum or maximum element — creating partitions of size
0 and n-1. Example: sorted array with last element as pivot → n levels deep × O(n) per level = O(n²).
Avoidance strategies: (1) Random pivot: arr[low + [Link](high-low+1)] — makes worst case
extremely unlikely. (2) Median of three: choose median of first, middle, last elements. (3) Dual-pivot
(Java's implementation): use two pivots to create three partitions. With random pivot, expected time is O(n
log n) with very high probability.
A: Quick Sort: In-place (O(log n) stack space), unstable, average O(n log n), worst O(n²). Pivot-based
partitioning. Faster in practice due to cache efficiency. Merge Sort: O(n) extra space, stable, always O(n
log n). Divide by half, merge step required. Better for linked lists and when stability needed. Key difference:
Quick Sort does work during partitioning (before recursion). Merge Sort does work during merging (after
recursion). Java uses Quick Sort for primitives (stability not needed) and TimSort (Merge+Insertion) for
objects.
A: A Max-Heap is a complete binary tree where every parent >= both children. Root is always the
maximum element. Array representation: parent(i) = (i-1)/2, left(i) = 2i+1, right(i) = 2i+2. Heap Sort steps:
(1) Build Max-Heap from array (O(n) — heapify from last non-leaf). (2) Repeat n-1 times: swap root (max)
with last element, reduce heap size by 1, heapify root. Each extraction puts one element in its final sorted
position. Time: O(n log n) always. Space: O(1) in-place. Unstable.
A: The theoretical lower bound for comparison-based sorting is Ω(n log n). Proof: Any comparison-based
sort can be modelled as a decision tree. For n elements, there are n! possible orderings (leaf nodes).
Height of tree ≥ log■(n!) ≈ n log n (by Stirling's approximation). Therefore, we need at least n log n
comparisons in the worst case. This means NO comparison-based sort can be faster than O(n log n) in the
worst case — ever. Counting, Radix, Bucket sort beat this by NOT using comparisons!
A: Counting Sort counts the frequency of each element, computes prefix sums for positions, then places
each element in its correct position. Steps: (1) Find max, create count array of size max+1. (2) Count
occurrences. (3) Compute cumulative sum (gives final positions). (4) Build output by traversing input in
reverse (for stability). Time: O(n+k) where k = range of values. Space: O(n+k). Use when: data is integers,
range k is small (k ≈ n). Not suitable for large ranges (floating point, negative numbers need modifications).
A: In-place: uses only O(1) extra space (constant, regardless of n). Examples: Bubble Sort, Selection Sort,
Insertion Sort, Heap Sort, Quick Sort. Out-of-place: uses O(n) or more extra space. Examples: Merge Sort
(O(n) for temp arrays), Counting Sort (O(k)), Radix Sort. In-place is preferred when memory is limited.
Out-of-place often achieves better or guaranteed time complexity (Merge Sort).
Q: What sorting algorithm would you use for sorting a linked list?
A: Merge Sort is the best choice for linked lists. Why: Merge Sort doesn't require random access — it
always moves sequentially. Splitting a linked list is easy (find middle with slow/fast pointer). Merging two
sorted linked lists is O(n) and done in-place (just redirect pointers, no extra array). Quick Sort on linked list
needs random pivot access which is O(n). Heap Sort requires random access by index — doesn't work on
linked lists. Merge Sort on linked list: Time O(n log n), Space O(log n) [recursive stack only].
Searching Questions
A: Binary Search is a search algorithm that finds a target in a SORTED array by repeatedly halving the
search space. Prerequisite: array MUST be sorted. Process: compare target with middle element. If equal
→ found. If target < middle → search left half. If target > middle → search right half. Time: O(log n) —
eliminates half the elements each step. For n=1,000,000: linear search worst case = 1,000,000 steps.
Binary search worst case = only 20 steps! That's the power of O(log n).
A: Integer overflow! In Java, int has max value = 2,147,483,647. If low = 1,500,000,000 and high =
2,000,000,000: low + high = 3,500,000,000 → overflows int range → negative result → wrong mid! low +
(high - low) / 2 = 1,500,000,000 + 250,000,000 = 1,750,000,000 → correct. This is a subtle but critical bug
in many implementations. Always use: mid = low + (high - low) / 2.
A: Linear Search: works on unsorted or sorted data. Time O(n). Space O(1). Check each element one by
one from left. Simple, no requirement. Binary Search: requires SORTED data. Time O(log n). Space O(1)
iterative. Eliminates half the search space each step. Much faster for large arrays. When to use Linear:
data is unsorted, small array, only one search needed, or searching by complex criteria that can't be
sorted. When to use Binary: sorted data, multiple searches on same data, or after sorting (sort + binary
search can be faster than n linear searches).
A: A rotated sorted array like [4,5,6,7,0,1,2] can be searched with modified Binary Search. Key insight: in a
rotated array, at least ONE half is always sorted. Algorithm: find mid. Check which half is sorted (compare
arr[low] with arr[mid]). If left half sorted (arr[low] <= arr[mid]): check if target in arr[low..mid]. If yes →
search left. If no → search right. Else right half is sorted: check if target in arr[mid..high]. Same logic. Time:
O(log n). This is a classic Google/Amazon interview question!
Q: What is a Binary Search Tree (BST) and how does it differ from Binary Search?
A: Binary Search is an algorithm applied to a sorted ARRAY. Binary Search Tree (BST) is a DATA
STRUCTURE (tree) where: left subtree contains only nodes < current node, right subtree contains only
nodes > current node. BST search: start at root, go left if target < node, right if target > node. BST average
search: O(log n) for balanced tree, O(n) for skewed tree. Difference: Binary Search on array is always
O(log n). BST depends on whether it's balanced (use AVL Tree or Red-Black Tree for guaranteed O(log
n)).
A: Binary Search needs at most log■(n) + 1 comparisons. log■(1,000,000) = log■(2²■) ≈ 20. So at most
21 comparisons to search 1 million elements! Compare: Linear Search needs up to 1,000,000
comparisons. This illustrates the massive difference between O(n) and O(log n). Each doubling of n only
adds 1 more step for Binary Search — it scales incredibly well.
A: Interpolation Search is an improvement of Binary Search for uniformly distributed sorted data. Instead
of always checking the middle, it estimates where the target likely is: pos = low + ((target - arr[low]) /
(arr[high] - arr[low])) * (high - low). It "interpolates" the position like a human searching a phone book —
looking for "Zhao" you'd jump near the end, not the middle. Time: O(log log n) average for uniform data —
better than O(log n). Worst case: O(n) for non-uniform data. Use when: data is uniformly distributed and
sorted.
A: A 2D matrix where rows and columns are both sorted (each row left-to-right, each column
top-to-bottom). Optimal approach: Staircase Search — start at top-right corner. If current == target →
found. If current > target → move left (eliminate this column). If current < target → move down (eliminate
this row). Time: O(m + n) where m=rows, n=columns — much better than O(m*n) brute force. Alternative:
Binary Search on each row → O(m log n). Staircase is better for square matrices. Binary search on rows is
simpler to code.
A: Insertion Sort is the best for nearly sorted data. If each element is at most k positions from its sorted
position, Insertion Sort runs in O(nk) time. For k=1 (nearly sorted), it's O(n) — linear! Shell Sort also works
well for nearly sorted data. TimSort (Java's sort) takes advantage of existing "runs" of sorted data — this is
why it's O(n) best case for already-sorted arrays.
A: Yes! If the integers are bounded in a known range k. Counting Sort: O(n + k). If k is similar to n, this is
O(n) — linear! Radix Sort: O(d*(n+k)) where d = number of digits. For 32-bit integers, d=10 (base 10).
Practically O(n) for fixed-size integers. Bucket Sort: O(n) average if data is uniformly distributed. These are
possible because they don't use comparisons — they exploit the structure of data. The Ω(n log n) lower
bound only applies to comparison-based sorts!
A: For PRIMITIVE arrays (int[], double[], etc.): Java uses Dual-Pivot QuickSort. Two pivots partition array
into 3 parts. Average O(n log n), in-place, faster than classic QuickSort. Unstable (but stability doesn't
matter for primitives — all elements are values, not objects). For OBJECT arrays (Integer[], String[],
custom objects): Java uses TimSort. TimSort = Merge Sort + Insertion Sort hybrid. Stable, O(n log n) worst
case, O(n) for sorted data. Stability matters for objects — equal objects should maintain their original
relative order.
A: With naive Quick Sort (last element as pivot): worst case O(n²)! On array [1,2,3,4,5], pivot=5, partition
creates: [] and [1,2,3,4]. Next: pivot=4, creates: [] and [1,2,3]. We get n-1 recursive calls, each doing O(n)
work → O(n²). The recursion tree is a straight line (no branching) — n levels deep. Solution: use random
pivot or median-of-three. With random pivot, the probability of this worst case is astronomically small.
Quick Sort O(n log n) O(n²) O(log n) Unstable Fastest in practice. Use random pivot.
Counting Sort O(n+k) O(n+k) O(k) Stable Linear! Only integers in known range.
Radix Sort O(d(n+k)) O(d(n+k)) O(n+k) Stable Linear for fixed-size integers.
Linear Search O(1) O(n) O(1) N/A Any data. Unsorted. Simple.
Binary Search O(1) O(log n) O(1) N/A REQUIRES sorted array. Very fast.