Sorting — From Bubble to Radix
৭+টি sort, runnable demo-সহ
1. Why Sort?
Sorted data binary search, dedup, median, range query — সব কিছু সহজ করে। Algorithm-এর সবচেয়ে বেশি studied family।
2. The Big Comparison Table
| Algorithm | Best | Avg | Worst | Space | Stable |
|---|---|---|---|---|---|
| Bubble | O(n) | O(n²) | O(n²) | O(1) | yes |
| Selection | O(n²) | O(n²) | O(n²) | O(1) | no |
| Insertion | O(n) | O(n²) | O(n²) | O(1) | yes |
| Merge | O(n log n) | O(n log n) | O(n log n) | O(n) | yes |
| Quick | O(n log n) | O(n log n) | O(n²) | O(log n) | no |
| Heap | O(n log n) | O(n log n) | O(n log n) | O(1) | no |
| Counting | O(n+k) | O(n+k) | O(n+k) | O(k) | yes |
| Radix | O(d(n+k)) | — | — | O(n+k) | yes |
3. Insertion Sort (nearly-sorted-এ সেরা)
#include <stdio.h>
void insertion_sort(int *a, int n) {
for (int i = 1; i < n; i++) {
int key = a[i], j = i - 1;
while (j >= 0 && a[j] > key) { a[j+1] = a[j]; j--; }
a[j+1] = key;
}
}
int main(void) {
int a[] = {42, 7, 13, 2, 99, 8, 25};
int n = 7;
insertion_sort(a, n);
for (int i = 0; i < n; i++) printf("%d ", a[i]);
putchar('\n');
return 0;
}
4. Merge Sort (guaranteed O(n log n), stable)
#include <stdio.h>
#include <stdlib.h>
void merge(int *a, int l, int m, int r) {
int n = r - l + 1, *buf = malloc(n * sizeof *buf);
int i = l, j = m + 1, k = 0;
while (i <= m && j <= r) buf[k++] = (a[i] <= a[j]) ? a[i++] : a[j++];
while (i <= m) buf[k++] = a[i++];
while (j <= r) buf[k++] = a[j++];
for (int x = 0; x < n; x++) a[l + x] = buf[x];
free(buf);
}
void merge_sort(int *a, int l, int r) {
if (l >= r) return;
int m = l + (r - l) / 2;
merge_sort(a, l, m);
merge_sort(a, m + 1, r);
merge(a, l, m, r);
}
int main(void) {
int a[] = {5, 2, 8, 1, 9, 3, 7, 4, 6};
int n = 9;
merge_sort(a, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", a[i]);
putchar('\n');
return 0;
}
5. Quick Sort (practice-এ সবচেয়ে দ্রুত)
#include <stdio.h>
static int partition(int *a, int l, int r) {
int pivot = a[r], i = l - 1;
for (int j = l; j < r; j++) {
if (a[j] <= pivot) {
i++;
int t = a[i]; a[i] = a[j]; a[j] = t;
}
}
int t = a[i + 1]; a[i + 1] = a[r]; a[r] = t;
return i + 1;
}
void quick_sort(int *a, int l, int r) {
if (l < r) {
int p = partition(a, l, r);
quick_sort(a, l, p - 1);
quick_sort(a, p + 1, r);
}
}
int main(void) {
int a[] = {10, 7, 8, 9, 1, 5};
int n = 6;
quick_sort(a, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", a[i]);
putchar('\n');
return 0;
}
Worst O(n²) এড়াতে random pivot নিন: int p = l + rand() % (r - l + 1); তারপর a[p] ও a[r] swap।
6. Stability Matters
Stable sort সমান key-এর relative order বজায় রাখে — একাধিক field-এ sort করার সময় গুরুত্বপূর্ণ (আগে city sort, তারপর name sort → একই name-এ city order বজায় থাকবে)।
7. Practice Problems
- Implement bubble, selection, insertion.তিনটি O(n²) sort।
✨ Show Answer
three.c#include <stdio.h> void bubble(int *a, int n) { for (int i = 0; i < n-1; i++) for (int j = 0; j < n-1-i; j++) if (a[j] > a[j+1]) { int t = a[j]; a[j] = a[j+1]; a[j+1] = t; } } void selection(int *a, int n) { for (int i = 0; i < n-1; i++) { int m = i; for (int j = i+1; j < n; j++) if (a[j] < a[m]) m = j; int t = a[i]; a[i] = a[m]; a[m] = t; } } int main(void) { int a[] = {5, 2, 8, 1, 9}; bubble(a, 5); for (int i = 0; i < 5; i++) printf("%d ", a[i]); putchar('\n'); return 0; } - Implement merge sort.Merge sort।
✨ Show Answer
Section 4 দেখুন।
- Implement quicksort with random pivot.Random pivot-এর quicksort।
✨ Show Answer
Section 5-এর code-এ
partition-এর শুরুতেint p = l + rand()%(r-l+1);swap(a[p], a[r]) যোগ করুন। - Implement heapsort.Heapsort।
✨ Show Answer
Array-এ max-heap build (bottom-up sift-down), তারপর বারবার root swap-with-last, heap size কমান, sift-down। O(n log n), in-place।
- Counting sort for ints in [0, k].Counting sort।
✨ Show Answer
void counting_sort(int *a, int n, int k) { int *c = calloc(k + 1, sizeof *c); for (int i = 0; i < n; i++) c[a[i]]++; int p = 0; for (int v = 0; v <= k; v++) while (c[v]--) a[p++] = v; free(c); } - Radix sort for 32-bit unsigned integers.Radix sort।
✨ Show Answer
প্রতিটি digit (বা byte)-এর জন্য stable counting sort, LSB থেকে MSB-তে। k = 256, d = 4 passes — মোট O(4(n + 256))।
- Compare all 6 sorts on n = 10⁵.৬টি sort তুলনা।
✨ Show Answer
n = 10⁵-এ O(n²) sorts ~10¹⁰ ops, কয়েক সেকেন্ড; O(n log n) ~10⁷, মিলিসেকেন্ড।
clock()দিয়ে benchmark করুন। - Sort structs by two fields (primary, secondary).দুটি field-এ sort।
✨ Show Answer
Compare function-এ প্রথমে primary field মিলান, সমান হলে secondary। Stable sort থাকলে আগে secondary, পরে primary আলাদা pass-এও কাজ করে।
- Quickselect: kth smallest in O(n) avg.Kth smallest — O(n) avg।
✨ Show Answer
Quicksort-এর partition ব্যবহার করুন; শুধু k-side-এ recurse। T(n) = T(n/2) + n → O(n) amortized।
- Median of medians — worst-case O(n).Median of medians — worst-case O(n)।
✨ Show Answer
Array-কে 5-element group-এ ভাগ করুন; প্রতিটি group-এর median; সেই median-গুলোর median বের করে pivot হিসেবে ব্যবহার। Worst-case Θ(n), কিন্তু constant বড় — practice-এ quickselect-ই দ্রুত।
- Sort a nearly-sorted array (k-away) — which algo?Nearly-sorted-এ কোন algo?
✨ Show Answer
Insertion sort O(nk) — k ছোট হলে O(n)। Tim sort এই idea-ই ব্যবহার করে (small run-এ insertion + merge)।
- Sort a linked list via merge sort.Linked list-এ merge sort।
✨ Show Answer
Slow/fast দিয়ে middle বের করুন, split, recursively sort, merge। Extra array লাগে না।
- Custom comparator via function pointer (qsort).qsort-এ custom compare।
✨ Show Answer
L22-এর
qsort_int.creference। Compare-এ(x > y) - (x < y)overflow এড়ায়। - Sort strings lexicographically.String lexicographic sort।
✨ Show Answer
int cmp_str(const void *a, const void *b) { return strcmp(*(const char *const*)a, *(const char *const*)b); } qsort(arr, n, sizeof arr[0], cmp_str); - Dutch National Flag (3-way partition).Dutch flag algorithm।
✨ Show Answer
Three pointers: low, mid, high। a[mid] = 0 → swap(low, mid), low++, mid++। = 1 → mid++। = 2 → swap(mid, high), high--। O(n)।
- Sort colors (0/1/2).0/1/2 color sort।
✨ Show Answer
উপরের Dutch-flag-ই উত্তর। Counting sort-ও চলবে কিন্তু এক-pass O(n), O(1) space-এ Dutch flag সেরা।
- External merge sort for a file too big for RAM.RAM-এর চেয়ে বড় file-এ external merge sort।
✨ Show Answer
File-কে RAM-size chunk-এ ভাগ করুন; প্রতিটি in-memory sort করে temp file-এ লিখুন। তারপর k-way merge (min-heap) দিয়ে মেলান।
- Why is quicksort faster than merge sort in practice?Quicksort practice-এ দ্রুত কেন?
✨ Show Answer
In-place (extra allocation নেই), cache-friendly (sequential scan), কম constant। Merge sort stable কিন্তু O(n) extra space লাগে।
- Prove merge sort is O(n log n) via recurrence.Merge sort-এর O(n log n) প্রমাণ।
✨ Show Answer
T(n) = 2T(n/2) + n। Master Theorem Case 2 → Θ(n log n)। অথবা recursion tree: প্রতিটি level-এ মোট কাজ n, level log n। ∎
- Prove there is no comparison sort better than O(n log n).Comparison sort-এর নিম্নসীমা প্রমাণ।
✨ Show Answer
Comparison-based sort-এর decision tree-তে ≥ n! leaf থাকতে হবে। Depth ≥ log₂(n!) = Ω(n log n)। তাই worst-case comparison ≥ Ω(n log n)। ∎
- Introsort — quicksort + heapsort hybrid.Introsort।
✨ Show Answer
Quicksort চালান, কিন্তু recursion depth 2·log n ছাড়ালে সেই subrange heapsort-এ সরান। C++
std::sort-এর basis। Worst-case O(n log n), average quick-এর মতোই দ্রুত। - Timsort — merge + insertion hybrid (Python default).Timsort।
✨ Show Answer
Array-কে "run"-এ ভাগ করুন (ছোট অংশ insertion), তারপর sophisticated merge (minrun, galloping)। Real-world data-তে বিশেষভাবে দ্রুত।
- Bucket sort for uniformly distributed floats.Uniform float-এ bucket sort।
✨ Show Answer
[0, 1)-এ uniform হলে n-টি bucket-এ ভাগ করুন (
bucket[floor(x*n)]), প্রতিটি bucket insertion sort, concat। Expected O(n)। - Why is stability useful for multi-key sorting?Stability কেন জরুরি?
✨ Show Answer
আগে secondary key-তে sort, তারপর stable sort-এ primary key-তে sort করলে একই primary-র মধ্যে secondary order বজায় থাকে — multi-key sort পরিষ্কারভাবে কাজ করে।
- Write a stable sort for a custom struct.Custom struct-এ stable sort।
✨ Show Answer
qsortstable নয়। POSIX-এqsort_r/stable version নেই; manual merge sort ব্যবহার করুন, অথবা compare function-এ original index tie-breaker যোগ করুন।
Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Sorting | Arranging elements according to a total order. | একটি ক্রম অনুযায়ী element সাজানো। |
| Comparison Sort | Sorts using only pairwise comparisons — Ω(n log n). | শুধু তুলনার মাধ্যমে sort — Ω(n log n)। |
| Bubble Sort | Repeatedly swap adjacent out-of-order pairs — O(n²). | পাশাপাশি ভুল-ক্রমে থাকা জোড়া বদলে দেওয়া। |
| Selection Sort | Pick the smallest, move to front, repeat — O(n²). | সবচেয়ে ছোট খুঁজে সামনে আনা। |
| Insertion Sort | Insert each element into its place in a sorted prefix — O(n²). | Sorted অংশে প্রতিটি element সঠিক জায়গায় বসানো। |
| Merge Sort | Divide-and-conquer sort — O(n log n), stable. | Divide-and-conquer sort — O(n log n), stable। |
| Quick Sort | Pivot-based partitioning — O(n log n) average, O(n²) worst. | Pivot-ভিত্তিক — গড়ে O(n log n)। |
| Heap Sort | Build a heap, repeatedly extract max — O(n log n) in-place. | Heap বানিয়ে বারবার max বের করা। |
| Counting Sort | Linear-time sort for small integer keys. | ছোট integer key-এর জন্য linear-time sort। |
| Radix Sort | Sort digit by digit — O(n·k). | একটি একটি অঙ্ক ধরে sort। |
| Stable Sort | Equal-key elements keep their original order. | সমান-key element-দের original ক্রম থাকে। |
| In-place Sort | Uses only O(1) extra memory. | O(1) অতিরিক্ত memory-তেই sort। |
| Pivot | The element quicksort partitions around. | Quicksort যেটির চারপাশে partition করে। |
qsort | C standard library's generic sort function. | C standard library-র generic sort। |
Summary — Module 31
O(n²) sort ছোট বা nearly-sorted data-এ; O(n log n) general workhorse; O(n) linear sort constrained input-এ। Stability ও in-place need অনুযায়ী বেছে নিন।