Design and Analysis of Algorithm
BPSC Syllabus (Algorithm)
Algorithm and complexity: Asymptotic notations, Basic algorithm techniques and analysis, Divide and conquer, Dynamic programming, greedy method, branch and bound, string matching, computational geometric problems, graph algorithms, spanning. trees, shortest paths, max-flow problem, searching algorithms. Techniques for analysis of algorithms, approximation algorithms, parallel algorithms..
Introduction to Algorithm
What is an Algorithm?
An algorithm is a step-by-step procedure or a set of instructions that are executed in a specific order to achieve a desired output. Algorithms are generally independent of any programming language, meaning they can be implemented in more than one programming language.
Criteria of an Algorithm:
An algorithm should have the following characteristics:
Unambiguous: Each step of the algorithm must be clear and unambiguous. Every step and its inputs/outputs should have only one meaning.
Input: An algorithm should have zero or more well-defined inputs.
Output: An algorithm should produce one or more well-defined outputs, which match the desired result.
Finiteness: An algorithm must terminate after a finite number of steps.
Feasibility: The algorithm should be feasible with the available resources.
Independent: The algorithm should be independent of programming code, providing step-by-step instructions.
Algorithm কী?
একটি algorithm হলো একটি step-by-step প্রক্রিয়া বা নির্দেশাবলির সেট যা একটি নির্দিষ্ট order-এ execute করা হয় যাতে একটি desired output অর্জন করা যায়। Algorithm সাধারণত যেকোনো programming language-এ স্বাধীন, অর্থাৎ এটি একাধিক programming language-এ implement করা যেতে পারে।
Algorithm-এর Criteria:
একটি algorithm-এর নিম্নলিখিত characteristics থাকা উচিত:
Unambiguous: algorithm-এর প্রতিটি step স্পষ্ট এবং unambiguous হওয়া উচিত। প্রতিটি step এবং এর inputs/outputs-এর একটি একক meaning থাকা উচিত।
Input: একটি algorithm-এ শূন্য বা একাধিক well-defined inputs থাকা উচিত।
Output: একটি algorithm-এ এক বা একাধিক well-defined outputs থাকা উচিত, যা desired result অনুযায়ী মিলবে।
Finiteness: একটি algorithm-কে একটি finite number of steps-এর মধ্যে terminate হতে হবে।
Feasibility: algorithmটি available resources-এর সাথে feasible হওয়া উচিত।
Independent: algorithmটি programming code-এর স্বাধীন হওয়া উচিত, এবং এটি step-by-step instructions প্রদান করতে হবে।
Searching Algorithm
A searching algorithm is a method used to find a specific element or record from a collection of data such as arrays, lists, or trees.
- Purpose: To locate required data efficiently from large datasets.
- Result: If the element is found → successful search; otherwise → unsuccessful search.
Types of Searching Algorithm
- Sequential Searching:
- Checks each element one by one.
- Does not require sorted data.
- Example: Linear Search.
- Interval Searching:
- Works on sorted data.
- Searches by dividing data into parts or intervals.
- Example: Binary Search, Jump Search.
Searching algorithm হলো এমন একটি পদ্ধতি যা data collection (array, list, tree ইত্যাদি) থেকে নির্দিষ্ট কোনো element বা record খুঁজে বের করতে ব্যবহৃত হয়।
- Purpose: বড় data থেকে দ্রুত নির্দিষ্ট তথ্য খুঁজে বের করা।
- Result: Element পাওয়া গেলে successful search, না পেলে unsuccessful search।
Searching Algorithm-এর ধরন
- Sequential Searching:
- প্রতিটি element একে একে check করা হয়।
- Data sorted হওয়া জরুরি নয়।
- Example: Linear Search।
- Interval Searching:
- Sorted data প্রয়োজন হয়।
- Data-কে ভাগ করে বা interval অনুযায়ী search করা হয়।
- Example: Binary Search, Jump Search।
Linear Search is a simple sequential searching algorithm where each element of the array is checked one by one until the desired element is found.
Algorithm (Steps)
1) Start from the first element (index 0) of the array.
2) Compare the current element with the key value.
3) If match is found, return the position of the element.
4) If not matched, move to the next element.
5) Repeat the process until the element is found or the array ends.
6) If not found, report that the element is not present.
linearSearch(array, n, key)
for i ← 0 to n − 1
if array[i] == key
return i
return -1
end linearSearch
10, 14, 19, 26, 27, 31, 31, 33, 35, 42, 44
[urcr_restrict]
Data: 10, 14, 19, 26, 27, 31, 31, 33, 35, 42, 44
Search item: 35
Step 1: Compare 10 → Not equal
Step 2: Compare 14 → Not equal
Step 3: Compare 19 → Not equal
Step 4: Compare 26 → Not equal
Step 5: Compare 27 → Not equal
Step 6: Compare 31 → Not equal
Step 7: Compare 31 → Not equal
Step 8: Compare 33 → Not equal
Step 9: Compare 35 → Found
Element found at position 9[/urcr_restrict]
[urcr_restrict]
Binary Search is an efficient searching algorithm that works on sorted arrays using the divide and conquer technique.
Binary Search হলো একটি efficient searching algorithm যা sorted array-এ কাজ করে এবং divide and conquer technique ব্যবহার করে।
Algorithm (Steps)
1) Find the middle element of the array.
2) Compare the middle element with the key value.
3) If match is found, return the position.
4) If key is greater, search in the right sub-array.
5) If key is smaller, search in the left sub-array.
6) Repeat until element is found or sub-array size becomes zero.
binarySearch(array, n, key)
low ← 0
high ← n − 1
while low ≤ high
mid ← low + (high − low) / 2
if array[mid] == key
return mid
else if array[mid] < key
low ← mid + 1
else
high ← mid − 1
return -1
end binarySearch
[/urcr_restrict]
10, 14, 19, 26, 27, 31, 31, 33, 35, 42, 44
Data: 10, 14, 19, 26, 27, 31, 31, 33, 35, 42, 44
Search item: 35
Step 1:
Low = 1
High = 11
Mid = (1 + 11) / 2 = 6
10 14 19 26 27 31 31 33 35 42 44
Data[6] = 31 < 35 → Search right side
--------------------------------------------------
Step 2:
Low = 7
High = 11
Mid = (7 + 11) / 2 = 9
10 14 19 26 27 31 31 33 35 42 44
Data[9] = 35
So, item found at position 9.
How Binary Search Works (Step-by-Step)
Binary Search is a searching technique used to find a target value in a sorted array by repeatedly dividing the search range into half.
Steps
1) Set two pointers: low = first index, high = last index.
2) Find the middle index: mid = (low + high) / 2.
3) Compare the target with array[mid]:
• If target == array[mid] → target found.
• If target < array[mid] → search left half (high = mid − 1).
• If target > array[mid] → search right half (low = mid + 1).
4) Repeat steps 2–3 until the target is found or low > high (target not present).
Example
Array: [10, 20, 30, 40, 50], Target = 40
low=0, high=4 → mid=2 → array[2]=30 (40>30) → search right
low=3, high=4 → mid=3 → array[3]=40 → found
Why it fails if the array is unsorted
Binary Search depends on the sorted order to decide whether to go left or right. If the array is unsorted, the comparisons (target < or > mid value) do not correctly indicate which half contains the target, so the algorithm may skip the target and give wrong results.
Binary Search কীভাবে কাজ করে (Step-by-Step)
Binary Search হলো একটি searching technique যা sorted array তে target value খুঁজতে search range কে বারবার half করে।
Steps
1) দুইটি pointer সেট করি: low = প্রথম index, high = শেষ index।
2) middle index বের করি: mid = (low + high) / 2।
3) target এর সাথে array[mid] compare করি:
• যদি target == array[mid] → target পাওয়া গেছে।
• যদি target < array[mid] → left half এ যাব (high = mid − 1)।
• যদি target > array[mid] → right half এ যাব (low = mid + 1)।
4) target না পাওয়া পর্যন্ত অথবা low > high হওয়া পর্যন্ত step গুলো repeat করি।
Example
Array: [10, 20, 30, 40, 50], Target = 40
low=0, high=4 → mid=2 → array[2]=30 (40>30) → right half এ search
low=3, high=4 → mid=3 → array[3]=40 → found
Array unsorted হলে কেন fail করে
Binary Search এর মূল শর্ত হলো array টি sorted হতে হবে, কারণ mid এর value দেখে সিদ্ধান্ত নেওয়া হয় target left এ নাকি right এ। Array unsorted হলে এই সিদ্ধান্ত ভুল হয়, ফলে target থাকা সত্ত্বেও algorithm সেটিকে skip করে ভুল result দিতে পারে।
Sorting Algorithm
What is Sorting Algorithm?
A Sorting Algorithm is a method used to arrange the elements of an array or list in a specific order, such as ascending or descending. Sorting helps in organizing data efficiently and makes searching and processing faster.Example
Unsorted Array:
10 50 21 36 45 62 19
Sorted Array (Ascending Order):
10 19 21 36 45 50 62
What is Sorting Algorithm?
Sorting Algorithm হলো এমন একটি পদ্ধতি যা ব্যবহার করে কোনো Array অথবা List-এর element গুলোকে একটি নির্দিষ্ট ক্রমে সাজানো হয়, যেমন ascending order বা descending order। Sorting ডাটা কে সুশৃঙ্খল করে এবং searching ও processing সহজ করে তোলে।
Example
Unsorted Array:
10 50 21 36 45 62 19
Sorted Array (Ascending Order):
10 19 21 36 45 50 62
bubbleSort(array)
for i ← 1 to sizeOfArray − 1 do for j ← 1 to sizeOfArray − 1 − i do
if array[j] > array[j + 1] then
swap array[j] and array[j + 1]
end if end for end for
end bubbleSort
Bubble Sort (Ascending) — Step-by-step Iterations
Initial List: [30, 25, 62, 45, 7, 10]
Pass 1 (i = 1)
1) Compare 30 and 25 → swap → [25, 30, 62, 45, 7, 10]
2) Compare 30 and 62 → no swap → [25, 30, 62, 45, 7, 10]
3) Compare 62 and 45 → swap → [25, 30, 45, 62, 7, 10]
4) Compare 62 and 7 → swap → [25, 30, 45, 7, 62, 10]
5) Compare 62 and 10 → swap → [25, 30, 45, 7, 10, 62]
End of Pass 1: [25, 30, 45, 7, 10, 62]
Pass 2 (i = 2)
1) Compare 25 and 30 → no swap → [25, 30, 45, 7, 10, 62]
2) Compare 30 and 45 → no swap → [25, 30, 45, 7, 10, 62]
3) Compare 45 and 7 → swap → [25, 30, 7, 45, 10, 62]
4) Compare 45 and 10 → swap → [25, 30, 7, 10, 45, 62]
End of Pass 2: [25, 30, 7, 10, 45, 62]
Pass 3 (i = 3)
1) Compare 25 and 30 → no swap → [25, 30, 7, 10, 45, 62]
2) Compare 30 and 7 → swap → [25, 7, 30, 10, 45, 62]
3) Compare 30 and 10 → swap → [25, 7, 10, 30, 45, 62]
End of Pass 3: [25, 7, 10, 30, 45, 62]
Pass 4 (i = 4)
1) Compare 25 and 7 → swap → [7, 25, 10, 30, 45, 62]
2) Compare 25 and 10 → swap → [7, 10, 25, 30, 45, 62]
End of Pass 4: [7, 10, 25, 30, 45, 62]
Pass 5 (i = 5)
1) Compare 7 and 10 → no swap → [7, 10, 25, 30, 45, 62]
End of Pass 5: [7, 10, 25, 30, 45, 62]
Note: No swaps happened, so the list is already sorted (early stop).
Final Sorted List (Ascending): [7, 10, 25, 30, 45, 62]
Time and Space Complexity of Bubble Sort
Time Complexity
The time complexity of Bubble Sort depends on the initial order of the elements in the array.
• Best Case: O(n)
When the array is already sorted, Bubble Sort requires only one pass with no swaps.
• Average Case: O(n²)
When the elements are in random order, Bubble Sort performs multiple comparisons and swaps.
• Worst Case: O(n²)
When the array is sorted in reverse order, maximum comparisons and swaps are required.
Space Complexity
The space complexity of Bubble Sort is O(1) because it is an in-place sorting algorithm and does not require any additional memory apart from a few variables.
Bubble Sort এর Time এবং Space Complexity
Time Complexity
Bubble Sort এর Time Complexity array-এর initial order এর উপর নির্ভর করে।
• Best Case: O(n)
যখন array আগে থেকেই sorted থাকে, তখন Bubble Sort শুধুমাত্র একটি pass এ কোনো swap ছাড়াই কাজ শেষ করে।
• Average Case: O(n²)
যখন element গুলো random order এ থাকে, তখন Bubble Sort অনেক comparison এবং swap করে।
• Worst Case: O(n²)
যখন array reverse order এ sorted থাকে, তখন সর্বাধিক comparison এবং swap প্রয়োজন হয়।
Space Complexity
Bubble Sort এর Space Complexity হলো O(1), কারণ এটি একটি in-place sorting algorithm
এবং input data ছাড়া অতিরিক্ত কোনো memory ব্যবহার করে না।
Selection Sort is a simple comparison-based sorting algorithm. It works by repeatedly selecting
the smallest element from the unsorted part of the array and placing it at the beginning.
The array is divided into a sorted part and an unsorted part.
Selection Sort হলো একটি সহজ comparison-based sorting algorithm। এই algorithm-এ array কে
একটি sorted part এবং একটি unsorted part এ ভাগ করা হয়। Unsroted part থেকে প্রতিবার
সবচেয়ে ছোট element নির্বাচন করে সেটিকে সামনে এনে বসানো হয়।
Algorithm (Steps)
1) Start from the first element of the array.
2) Assume the first element is the minimum element.
3) Compare this minimum element with the remaining elements of the array.
4) If a smaller element is found, update the minimum element.
5) Swap the minimum element with the first element of the unsorted part.
6) Move the boundary of the sorted part one position forward.
7) Repeat the above steps until the entire array is sorted.
selectionSort(array)
for i ← 1 to sizeOfArray − 1
minIndex ← i
for j ← i + 1 to sizeOfArray
if array[j] < array[minIndex]
minIndex ← j
end for
if minIndex ≠ i
swap array[i] and array[minIndex]
end for
end selectionSort
[urcr_restrict]
Insertion Sort — Step-by-step Execution
Initial Array: [30, 25, 62, 45, 7, 10]
Pass 1 (i = 2, key = 25)
Compare 25 with 30 → shift 30 to the right
Insert 25 at position 1
Array after Pass 1: [25, 30, 62, 45, 7, 10]
Pass 2 (i = 3, key = 62)
Compare 62 with 30 → no shift required
Array after Pass 2: [25, 30, 62, 45, 7, 10]
Pass 3 (i = 4, key = 45)
Compare 45 with 62 → shift 62 to the right
Compare 45 with 30 → stop shifting
Insert 45 in correct position
Array after Pass 3: [25, 30, 45, 62, 7, 10]
Pass 4 (i = 5, key = 7)
Compare 7 with 62 → shift 62 to the right
Compare 7 with 45 → shift 45 to the right
Compare 7 with 30 → shift 30 to the right
Compare 7 with 25 → shift 25 to the right
Insert 7 at the beginning
Array after Pass 4: [7, 25, 30, 45, 62, 10]
Pass 5 (i = 6, key = 10)
Compare 10 with 62 → shift 62 to the right
Compare 10 with 45 → shift 45 to the right
Compare 10 with 30 → shift 30 to the right
Compare 10 with 25 → shift 25 to the right
Compare 10 with 7 → stop shifting
Insert 10 in correct position
Array after Pass 5: [7, 10, 25, 30, 45, 62]
Final Sorted Array (Ascending Order): [7, 10, 25, 30, 45, 62][/urcr_restrict]
Selection Sort Algorithm
Selection Sort is a simple comparison-based sorting algorithm that works by repeatedly selecting the smallest element from the unsorted part and placing it at the beginning.
Selection Sort Algorithm
Selection Sort হলো একটি simple comparison-based sorting algorithm যা unsorted অংশ থেকে সবচেয়ে ছোট element নির্বাচন করে সেটিকে শুরুতে স্থাপন করে।
Algorithm (Steps)
1) Start from the first element of the array.
2) Find the smallest element in the unsorted part of the array.
3) Swap it with the first unsorted element.
4) Move the boundary of sorted and unsorted part one step forward.
5) Repeat the process for the remaining elements.
6) Continue until the entire array is sorted.
selectionSort(array, n)
for i ← 0 to n − 2
minIndex ← i
for j ← i + 1 to n − 1
if array[j] < array[minIndex]
minIndex ← j
swap array[i] and array[minIndex]
end selectionSort
[urcr_restrict]
Selection Sort — Step-by-step Example
Given Array: [30, 25, 62, 45, 7, 10]
Pass 1 (i = 1)
Assume minimum = 30
Compare 30 with 25 → new minimum = 25
Compare 25 with 62 → no change
Compare 25 with 45 → no change
Compare 25 with 7 → new minimum = 7
Compare 7 with 10 → no change
Swap 30 and 7
Array after Pass 1: [7, 25, 62, 45, 30, 10]
Pass 2 (i = 2)
Assume minimum = 25
Compare 25 with 62 → no change
Compare 25 with 45 → no change
Compare 25 with 30 → no change
Compare 25 with 10 → new minimum = 10
Swap 25 and 10
Array after Pass 2: [7, 10, 62, 45, 30, 25]
Pass 3 (i = 3)
Assume minimum = 62
Compare 62 with 45 → new minimum = 45
Compare 45 with 30 → new minimum = 30
Compare 30 with 25 → new minimum = 25
Swap 62 and 25
Array after Pass 3: [7, 10, 25, 45, 30, 62]
Pass 4 (i = 4)
Assume minimum = 45
Compare 45 with 30 → new minimum = 30
Compare 30 with 62 → no change
Swap 45 and 30
Array after Pass 4: [7, 10, 25, 30, 45, 62]
Pass 5 (i = 5)
Assume minimum = 45
Compare 45 with 62 → no change
No swap needed
Array after Pass 5: [7, 10, 25, 30, 45, 62]
Final Sorted Array (Ascending Order): [7, 10, 25, 30, 45, 62][/urcr_restrict]
[urcr_restrict]
Merge Sort Algorithm
Merge Sort is an efficient, comparison-based sorting algorithm that follows the
Divide and Conquer strategy. It divides the array into smaller subarrays,
sorts them recursively, and then merges the sorted subarrays to produce the final sorted array.
Merge Sort Algorithm
Merge Sort হলো একটি efficient comparison-based sorting algorithm যা
Divide and Conquer technique অনুসরণ করে। এই algorithm-এ array কে
ছোট ছোট subarray তে ভাগ করা হয়, তারপর প্রতিটি subarray recursively sort করা হয়
এবং শেষে sorted subarray গুলো merge করে একটি sorted array তৈরি করা হয়।
Algorithm (Steps)
1) Divide the given array into two halves.
2) Recursively apply Merge Sort on the left half.
3) Recursively apply Merge Sort on the right half.
4) Merge the two sorted halves into a single sorted array.
5) Repeat the process until the entire array is sorted.
mergeSort(array, left, right)
if left < right
mid ← (left + right) / 2
mergeSort(array, left, mid)
mergeSort(array, mid + 1, right)
merge(array, left, mid, right)
end mergeSort
merge(array, left, mid, right)
n1 ← mid − left + 1
n2 ← right − mid
create array L[1..n1] and R[1..n2]
for i ← 1 to n1
L[i] ← array[left + i − 1]
for j ← 1 to n2
R[j] ← array[mid + j]
i ← 1, j ← 1, k ← left
while i ≤ n1 and j ≤ n2
if L[i] ≤ R[j]
array[k] ← L[i]
i ← i + 1
else
array[k] ← R[j]
j ← j + 1
k ← k + 1
while i ≤ n1
array[k] ← L[i]
i ← i + 1
k ← k + 1
while j ≤ n2
array[k] ← R[j]
j ← j + 1
k ← k + 1
end merge
[/urcr_restrict]
Merge Sort — Step-by-step Example (Ascending)
Given Array: [30, 25, 62, 45, 7, 10]
Step 1: Divide (Split) the array
Split into two halves:
Left = [30, 25, 62]
Right = [45, 7, 10]
Step 2: Divide the Left half
Left = [30, 25, 62] → Split into:
L1 = [30, 25]
L2 = [62]
Step 3: Divide L1 further
L1 = [30, 25] → Split into:
[30] and [25]
Step 4: Merge (Sort) [30] and [25]
Merge → [25, 30]
Step 5: Merge [25, 30] and [62]
Merge → [25, 30, 62]
Sorted Left Half: [25, 30, 62]
Step 6: Divide the Right half
Right = [45, 7, 10] → Split into:
R1 = [45, 7]
R2 = [10]
Step 7: Divide R1 further
R1 = [45, 7] → Split into:
[45] and [7]
Step 8: Merge (Sort) [45] and [7]
Merge → [7, 45]
Step 9: Merge [7, 45] and [10]
Merge → [7, 10, 45]
Sorted Right Half: [7, 10, 45]
Step 10: Final Merge of two sorted halves
Merge [25, 30, 62] and [7, 10, 45]:
Result → [7, 10, 25, 30, 45, 62]
Final Sorted Array (Ascending Order): [7, 10, 25, 30, 45, 62]
<
Time Complexity of Merge Sort (Iteration-wise)
Merge Sort follows the Divide and Conquer approach. The array is repeatedly divided into smaller subarrays and then merged in a sorted manner.
Division Phase:
At each level, the array is divided into two halves until subarrays of size 1 are obtained.
Number of levels of division = log₂n
Merging Phase:
At each level, all elements are merged once.
Time taken at each level = O(n)
Total Time Complexity:
Since there are log n levels and each level takes O(n) time,
Time Complexity = O(n log n)
Best Case: O(n log n)
Average Case: O(n log n)
Worst Case: O(n log n)
Merge Sort এর Time Complexity (Iteration-wise)
Merge Sort একটি Divide and Conquer based sorting algorithm। এখানে array-কে বারবার ছোট ছোট subarray-তে ভাগ করা হয় এবং পরে সেগুলো merge করে sorted করা হয়।
Division Phase:
প্রতিটি ধাপে array-কে দুই ভাগে ভাগ করা হয় যতক্ষণ না subarray-এর size 1 হয়।
Division level এর সংখ্যা = log₂n
Merging Phase:
প্রতিটি level-এ সব element একবার করে merge হয়।
প্রতিটি level-এ সময় লাগে = O(n)
Total Time Complexity:
যেহেতু মোট log n টি level আছে এবং প্রতিটি level-এ O(n) সময় লাগে,
Time Complexity = O(n log n)
Best Case: O(n log n)
Average Case: O(n log n)
Worst Case: O(n log n)
Time Complexity of Merge Sort (Iteration-wise)
Merge Sort follows the Divide and Conquer approach. The array is repeatedly divided into smaller subarrays and then merged in a sorted manner.
Division Phase:
At each level, the array is divided into two halves until subarrays of size 1 are obtained.
Number of levels of division = log₂n
Merging Phase:
At each level, all elements are merged once.
Time taken at each level = O(n)
Total Time Complexity:
Since there are log n levels and each level takes O(n) time,
Time Complexity = O(n log n)
Best Case: O(n log n)
Average Case: O(n log n)
Worst Case: O(n log n)
Merge Sort এর Time Complexity (Iteration-wise)
Merge Sort একটি Divide and Conquer based sorting algorithm। এখানে array-কে বারবার ছোট ছোট subarray-তে ভাগ করা হয় এবং পরে সেগুলো merge করে sorted করা হয়।
Division Phase:
প্রতিটি ধাপে array-কে দুই ভাগে ভাগ করা হয় যতক্ষণ না subarray-এর size 1 হয়।
Division level এর সংখ্যা = log₂n
Merging Phase:
প্রতিটি level-এ সব element একবার করে merge হয়।
প্রতিটি level-এ সময় লাগে = O(n)
Total Time Complexity:
যেহেতু মোট log n টি level আছে এবং প্রতিটি level-এ O(n) সময় লাগে,
Time Complexity = O(n log n)
Best Case: O(n log n)
Average Case: O(n log n)
Worst Case: O(n log n)
Quick Sort Algorithm
Quick Sort is an efficient comparison-based sorting algorithm that follows the Divide and Conquer technique.
It works by selecting a pivot element, partitioning the array around the pivot, and recursively sorting the subarrays.
Quick Sort Algorithm
Quick Sort হলো একটি efficient comparison-based sorting algorithm যা Divide and Conquer technique ব্যবহার করে।
এখানে একটি pivot element নির্বাচন করা হয়, pivot-এর চারপাশে array partition করা হয় এবং তারপর subarray গুলো recursively sort করা হয়।
Algorithm (Steps)
1) Choose a pivot element from the array.
2) Rearrange the array so that elements smaller than the pivot are on the left and larger elements are on the right.
3) Place the pivot in its correct position.
4) Recursively apply Quick Sort on the left subarray.
5) Recursively apply Quick Sort on the right subarray.
6) Repeat the process until the entire array is sorted.
quickSort(array, low, high)
if low < high
pivotIndex ← partition(array, low, high)
quickSort(array, low, pivotIndex − 1)
quickSort(array, pivotIndex + 1, high)
end quickSort
partition(array, low, high)
pivot ← array[high]
i ← low − 1
for j ← low to high − 1
if array[j] < pivot
i ← i + 1
swap array[i] and array[j]
swap array[i + 1] and array[high]
return i + 1
Given list:
41, 25, 87, 57, 80, 79, 19, 36, 42, 7
Quick Sort idea:
Choose a pivot, then place smaller elements on the left and larger elements on the right. Repeat the same process for each sub-list.
Here I use the first element as pivot in each step.
Step 1: Sort the full list
List: 41, 25, 87, 57, 80, 79, 19, 36, 42, 7
Pivot = 41
Elements smaller than 41: 25, 19, 36, 7
Elements greater than 41: 87, 57, 80, 79, 42
So now:
25, 19, 36, 7 | 41 | 87, 57, 80, 79, 42
Step 2: Sort left sub-list
Sub-list: 25, 19, 36, 7
Pivot = 25
Elements smaller than 25: 19, 7
Elements greater than 25: 36
So:
19, 7 | 25 | 36
Step 3: Sort the left part of that sub-list
Sub-list: 19, 7
Pivot = 19
Elements smaller than 19: 7
Elements greater than 19: none
So:
7 | 19
Now the full left side becomes:
7, 19, 25, 36
Step 4: Sort right sub-list of 41
Sub-list: 87, 57, 80, 79, 42
Pivot = 87
Elements smaller than 87: 57, 80, 79, 42
Elements greater than 87: none
So:
57, 80, 79, 42 | 87
Step 5: Sort 57, 80, 79, 42
Pivot = 57
Elements smaller than 57: 42
Elements greater than 57: 80, 79
So:
42 | 57 | 80, 79
Step 6: Sort 80, 79
Pivot = 80
Elements smaller than 80: 79
Elements greater than 80: none
So:
79 | 80
Now the full right side becomes:
42, 57, 79, 80, 87
Step 7: Combine everything
Left side: 7, 19, 25, 36
Pivot: 41
Right side: 42, 57, 79, 80, 87
Final sorted list:
7, 19, 25, 36, 41, 42, 57, 79, 80, 87
Given list:
41, 25, 87, 57, 80, 79, 19, 36, 42, 7
Quick Sort idea:
Choose a pivot, then place smaller elements on the left and larger elements on the right. Repeat the same process for each sub-list.
Here I use the first element as pivot in each step.
Step 1: Sort the full list
List: 41, 25, 87, 57, 80, 79, 19, 36, 42, 7
Pivot = 41
Elements smaller than 41: 25, 19, 36, 7
Elements greater than 41: 87, 57, 80, 79, 42
So now:
25, 19, 36, 7 | 41 | 87, 57, 80, 79, 42
Step 2: Sort left sub-list
Sub-list: 25, 19, 36, 7
Pivot = 25
Elements smaller than 25: 19, 7
Elements greater than 25: 36
So:
19, 7 | 25 | 36
Step 3: Sort the left part of that sub-list
Sub-list: 19, 7
Pivot = 19
Elements smaller than 19: 7
Elements greater than 19: none
So:
7 | 19
Now the full left side becomes:
7, 19, 25, 36
Step 4: Sort right sub-list of 41
Sub-list: 87, 57, 80, 79, 42
Pivot = 87
Elements smaller than 87: 57, 80, 79, 42
Elements greater than 87: none
So:
57, 80, 79, 42 | 87
Step 5: Sort 57, 80, 79, 42
Pivot = 57
Elements smaller than 57: 42
Elements greater than 57: 80, 79
So:
42 | 57 | 80, 79
Step 6: Sort 80, 79
Pivot = 80
Elements smaller than 80: 79
Elements greater than 80: none
So:
79 | 80
Now the full right side becomes:
42, 57, 79, 80, 87
Step 7: Combine everything
Left side: 7, 19, 25, 36
Pivot: 41
Right side: 42, 57, 79, 80, 87
Final sorted list:
7, 19, 25, 36, 41, 42, 57, 79, 80, 87
Given Array: 170, 45, 75, 90, 802, 24, 2, 66
Radix Sort sorts numbers digit by digit (from Least Significant Digit – LSD to Most Significant Digit).
Step 1: Sort by Unit Digit (1’s place)
- 170 → 0
- 45 → 5
- 75 → 5
- 90 → 0
- 802 → 2
- 24 → 4
- 2 → 2
- 66 → 6
After sorting: 170, 90, 802, 2, 24, 45, 75, 66
Step 2: Sort by Tens Digit (10’s place)
- 170 → 7
- 90 → 9
- 802 → 0
- 2 → 0
- 24 → 2
- 45 → 4
- 75 → 7
- 66 → 6
After sorting: 802, 2, 24, 45, 66, 170, 75, 90
Step 3: Sort by Hundreds Digit (100’s place)
- 802 → 8
- 2 → 0
- 24 → 0
- 45 → 0
- 66 → 0
- 170 → 1
- 75 → 0
- 90 → 0
After sorting (Final Output): 2, 24, 45, 66, 75, 90, 170, 802 ✅
Advantages of Sorting Algorithms
Bubble Sort: Simple to understand and implement. Suitable for small datasets and educational purposes.
Insertion Sort: Efficient for small or nearly sorted datasets. Uses less overhead and is stable.
Selection Sort: Performs minimum number of swaps. Useful when memory writes are costly.
Merge Sort: Highly efficient and consistent performance. Suitable for large datasets and external sorting.
Quick Sort: Very fast in practice with good average-case performance. Requires less extra memory.
Heap Sort: Guarantees O(n log n) time complexity and works in-place.
Comparison of Sorting Algorithms
Sorting Algorithm-এর Advantages
Bubble Sort: বোঝা ও implement করা খুব সহজ। ছোট dataset এবং learning purpose-এর জন্য উপযোগী।
Insertion Sort: Small বা nearly sorted dataset-এর জন্য efficient। এটি stable এবং কম overhead ব্যবহার করে।
Selection Sort: Minimum swap ব্যবহার করে। যেখানে memory write costly সেখানে কার্যকর।
Merge Sort: Large dataset-এর জন্য খুব efficient এবং consistent performance দেয়। External sorting-এর জন্য উপযোগী।
Quick Sort: Practical ক্ষেত্রে খুব দ্রুত কাজ করে এবং average case performance ভালো। Extra memory কম লাগে।
Heap Sort: Guaranteed O(n log n) time complexity দেয় এবং in-place কাজ করে।
Sorting Algorithms-এর Comparison

Divide and Conquer

The Divide and Conquer approach is a problem-solving technique where a large problem is divided into smaller sub-problems, each solved independently, and then combined to get the final solution.
Steps of Divide and Conquer:
1. Divide (Break):
Break the main problem into smaller sub-problems recursively until they cannot be divided further.
2. Conquer (Solve):
Solve each smaller sub-problem independently (base case).
3. Merge (Combine):
Combine the solutions of sub-problems to get the final solution.
Example:
Sorting an array using Merge Sort, where the array is divided, sorted, and then merged.
Divide and Conquer হলো একটি problem-solving technique যেখানে একটি বড় সমস্যাকে ছোট ছোট sub-problem-এ ভাগ করে, প্রতিটিকে আলাদাভাবে solve করে এবং শেষে একত্র করে final solution পাওয়া যায়।
ধাপসমূহ:
1. Divide (Break):
সমস্যাকে ছোট ছোট sub-problem-এ ভাগ করা হয় যতক্ষণ না আর ভাগ করা সম্ভব।
2. Conquer (Solve):
প্রতিটি ছোট sub-problem আলাদাভাবে solve করা হয়।
3. Merge (Combine):
সব sub-problem-এর solution একত্র করে final solution তৈরি করা হয়।
উদাহরণ:
Merge Sort algorithm যেখানে array ভাগ করে, solve করে এবং পরে merge করা হয়।
Dividing the Problem:
The main problem is divided into smaller and manageable subproblems. This division is usually done recursively until the subproblems become simple enough to solve directly (base case).
Independence of Subproblems:
Each subproblem is independent, meaning solving one does not depend on others. This allows parallel or concurrent execution, improving efficiency.
Conquering Each Subproblem:
After division, each subproblem is solved individually. This may use the same recursive approach or a simple direct solution when the problem becomes small.
Combining Solutions:
Finally, the solutions of all subproblems are combined efficiently to form the solution of the original problem. This step should be simple and well-structured.
Dividing the Problem:
মূল সমস্যাকে ছোট ছোট sub-problem-এ ভাগ করা হয়। সাধারণত recursion ব্যবহার করে ভাগ করা হয় যতক্ষণ না সহজভাবে solve করা যায়।
Independence of Subproblems:
প্রতিটি sub-problem একে অপরের উপর নির্ভরশীল নয়। তাই আলাদাভাবে বা parallel ভাবে solve করা যায়
Conquering Each Subproblem:
প্রতিটি sub-problem আলাদাভাবে solve করা হয়। ছোট হলে সরাসরি সমাধান করা হয়, না হলে আবার divide করা হয়।
Combining Solutions:
সব sub-problem-এর solution একত্র করে মূল সমস্যার final solution তৈরি করা হয়। এই ধাপটি সহজ ও efficient হওয়া উচিত।
Binary Search:
Binary Search is used to find an element in a sorted array. It repeatedly divides the array into two halves and compares the middle element with the target value. If the target is smaller, it searches the left half; otherwise, it searches the right half. This reduces the search space significantly and works in O(log n) time.
Quick Sort:
Quick Sort is a popular sorting algorithm that selects a pivot element and partitions the array such that elements smaller than the pivot go to the left and larger elements go to the right. It then recursively applies the same process to subarrays. It is efficient with average time complexity O(n log n).
Merge Sort:
Merge Sort divides the array into two halves, recursively sorts each half, and then merges the sorted halves into a final sorted array. It guarantees O(n log n) time complexity and is stable, making it useful in many applications.
Closest Pair of Points:
This problem finds the closest pair among a set of points in a 2D plane. A brute-force approach takes O(n²) time, but using Divide and Conquer reduces it to O(n log n) by dividing points and efficiently checking distances.
Strassen’s Algorithm:
Strassen’s algorithm is used for matrix multiplication. Unlike the traditional method (O(n³)), it uses divide and conquer to reduce the number of multiplications, achieving approximately O(n².8974) time complexity.
Fast Fourier Transform (FFT):
FFT is used in signal processing and data analysis to convert signals between time and frequency domains. The Cooley–Tukey algorithm applies divide and conquer to achieve O(n log n) time.
Karatsuba Algorithm:
Karatsuba algorithm is used for fast multiplication of large numbers. It splits numbers into smaller parts and reduces multiplication operations, achieving time complexity of approximately O(n1.59).
Binary Search:
Binary Search একটি sorted array-এ element খুঁজতে ব্যবহৃত হয়। এটি বারবার array-কে দুই ভাগে ভাগ করে এবং middle element-এর সাথে compare করে। এতে search space কমে যায় এবং এর time complexity O(log n)।
Quick Sort:
Quick Sort একটি sorting algorithm যেখানে একটি pivot element নেওয়া হয় এবং array-কে এমনভাবে ভাগ করা হয় যাতে ছোট element বামে এবং বড় element ডানে থাকে। এরপর sub-array-গুলোর উপর একই পদ্ধতি প্রয়োগ করা হয়।
Merge Sort:
Merge Sort array-কে দুই ভাগে ভাগ করে, প্রতিটি ভাগকে আলাদাভাবে sort করে এবং শেষে merge করে। এটি সব সময় O(n log n) time-এ কাজ করে এবং stable sorting algorithm।
Closest Pair of Points:
এই problem-এ 2D plane-এ সবচেয়ে কাছাকাছি দুটি point বের করা হয়। সাধারণ পদ্ধতিতে O(n²) লাগে, কিন্তু Divide and Conquer ব্যবহার করলে O(n log n) সময়ে সমাধান করা যায়।
Strassen’s Algorithm:
Matrix multiplication দ্রুত করার জন্য ব্যবহৃত হয়। সাধারণ পদ্ধতি O(n³), কিন্তু Strassen algorithm প্রায় O(n².8974) সময়ে কাজ করে।
Fast Fourier Transform (FFT):
Signal processing-এ ব্যবহৃত হয় এবং time domain থেকে frequency domain-এ রূপান্তর করে। Divide and Conquer ব্যবহার করে এটি O(n log n) সময়ে কাজ করে।
Karatsuba Algorithm:
বড় সংখ্যা দ্রুত multiply করার জন্য ব্যবহৃত হয়। এটি সংখ্যাকে ছোট অংশে ভাগ করে এবং কম multiplication ব্যবহার করে O(n1.59) সময়ে ফলাফল দেয়।
| Advantages | Disadvantages |
|---|---|
| Solves Complex Problems: Breaks difficult problems into smaller parts (e.g., Tower of Hanoi). | Overhead: Extra time needed for dividing and combining subproblems. |
| Efficient Algorithms: Helps design fast algorithms like Quick Sort, Merge Sort, FFT. | Complexity: Managing many subproblems can make solution complex. |
| Parallel Processing: Subproblems can be solved simultaneously in multi-processor systems. | Difficult Implementation: Some problems are hard to divide properly. |
| Better Memory Usage: Efficient use of cache (cache-friendly algorithms). | Memory Limitation: Large problems may require extra memory for intermediate results. |
Conclusion:
Divide and Conquer improves efficiency and performance but may increase complexity and overhead in some cases.
Divide and Conquer Algorithm-এর সুবিধা ও অসুবিধা
| সুবিধা | অসুবিধা |
|---|---|
| জটিল সমস্যা সমাধান: বড় সমস্যা ছোট অংশে ভাগ করে সহজে সমাধান করা যায়। | Overhead: ভাগ ও combine করতে অতিরিক্ত সময় লাগে। |
| দ্রুত Algorithm: Quick Sort, Merge Sort-এর মতো efficient algorithm তৈরি করা যায়। | Complexity: অনেক sub-problem থাকার কারণে জটিলতা বাড়ে। |
| Parallel Processing: একাধিক processor-এ একসাথে কাজ করা যায়। | Implementation কঠিন: কিছু সমস্যা ভাগ করা কঠিন। |
| Memory Efficiency: cache ভালোভাবে ব্যবহার করা যায়। | Memory Limit: intermediate result রাখতে বেশি memory লাগতে পারে। |
উপসংহার:
Divide and Conquer দ্রুত ও কার্যকর, তবে কিছু ক্ষেত্রে overhead ও complexity বেশি হয়।
Dynamic Programming
Dynamic Programming (DP) is an algorithmic technique used to solve complex problems by breaking them into smaller overlapping subproblems and storing their results to avoid repeated computation.
It improves efficiency by converting recursive solutions (often exponential time) into faster polynomial time solutions.
Key Idea:
- Solve each subproblem only once
- Store results using memoization (top-down) or tabulation (bottom-up)
When to Use Dynamic Programming
Dynamic Programming is used when a problem has the following two properties:
1. Optimal Substructure:
The optimal solution of a problem can be built from the optimal solutions of its subproblems.
Example: Finding minimum cost path in a graph using results of smaller paths.
2. Overlapping Subproblems:
The same subproblems are solved multiple times in recursion.
Example: Fibonacci series where same values are repeatedly calculated.
Dynamic Programming (DP) হলো একটি algorithmic technique যেখানে বড় সমস্যাকে ছোট overlapping subproblem-এ ভাগ করে এবং তাদের ফলাফল সংরক্ষণ করে পুনরাবৃত্তি এড়ানো হয়।
এটি recursive solution-কে optimize করে এবং exponential time থেকে polynomial time-এ নিয়ে আসে।
মূল ধারণা:
- প্রতিটি subproblem একবারই solve করা
- Result সংরক্ষণ করা memoization (top-down) বা tabulation (bottom-up) ব্যবহার করে
কখন Dynamic Programming ব্যবহার করা হয়
যখন problem-এ নিচের দুটি বৈশিষ্ট্য থাকে তখন DP ব্যবহার করা হয়:
1. Optimal Substructure:
বড় সমস্যার সমাধান ছোট subproblem-এর optimal সমাধান থেকে তৈরি করা যায়।
উদাহরণ: graph-এ minimum cost path নির্ণয়।
2. Overlapping Subproblems:
একই subproblem বারবার solve করতে হয়।
উদাহরণ: Fibonacci series-এ একই মান বারবার গণনা করা হয়।
Fibonacci Series:
Computes Fibonacci numbers by storing previously calculated values to avoid repetition.
0/1 Knapsack Problem:
Selects items to maximize value without exceeding weight limit using stored subproblem results.
Longest Common Subsequence (LCS):
Finds the longest subsequence common in two strings using a table to store intermediate results.
Matrix Chain Multiplication:
Determines the most efficient way to multiply matrices by minimizing multiplication cost.
Coin Change Problem:
Finds minimum number of coins needed to make a given amount using stored solutions.
Dynamic Programming is an optimization technique used to solve problems by breaking them into smaller overlapping subproblems and storing the result of each subproblem so that it is not calculated again.
In the Fibonacci Series, each term is defined as:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2), for n ≥ 2
So, to find F(5), we need F(4) and F(3). Again, to find F(4), we need F(3) and F(2). In this way, the same values are calculated many times. This is why Fibonacci is a very common example of Dynamic Programming.
If we solve Fibonacci using simple recursion, the same subproblems are solved repeatedly, which wastes time.
For example, the recursion tree of F(5) is:
F(5)
/ \
F(4) F(3)
/ \ / \
F(3) F(2) F(2) F(1)
/ \ / \ / \
F(2) F(1)F(1)F(0)F(1)F(0)
/ \
F(1) F(0)
In this tree, F(3) and F(2) are calculated more than once. This repeated calculation is called overlapping subproblems.
Dynamic Programming avoids this repetition by storing already computed values.
Two Approaches of Dynamic Programming
- 1. Memoization (Top-Down): Uses recursion + stores previously calculated results.
- 2. Tabulation (Bottom-Up): Starts from base values and builds the answer step by step.
1. Fibonacci using Memoization (Top-Down)
In this method, we use recursion, but before calculating any Fibonacci value, we check whether it has already been computed.
Idea:
- If value is already stored, return it
- Otherwise calculate and store it
#include <stdio.h>
int dp[1000];
int fib(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (dp[n] != -1)
return dp[n];
dp[n] = fib(n-1) + fib(n-2);
return dp[n];
}
int main() {
int n, i;
printf("Enter n: ");
scanf("%d", &n);
for(i = 0; i < 1000; i++)
dp[i] = -1;
printf("Fibonacci number = %d", fib(n));
return 0;
}Explanation:
Here, dp[] array stores previously calculated Fibonacci values. If fib(4) is already found once, next time it will be taken directly from the array instead of recalculating.
2. Fibonacci using Tabulation (Bottom-Up)
In this method, we start from the smallest values and build the final answer step by step.
#include <stdio.h>
int main() {
int n, i;
int dp[1000];
printf("Enter n: ");
scanf("%d", &n);
dp[0] = 0;
dp[1] = 1;
for(i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
printf("Fibonacci number = %d", dp[n]);
return 0;
}
Explanation:
Here, we first store F(0)=0 and F(1)=1. Then we calculate F(2), F(3), F(4) … up to F(n). So each value is calculated only once.
Example Calculation:
To find F(6):
F(0) = 0
F(1) = 1
F(2) = F(1) + F(0) = 1
F(3) = F(2) + F(1) = 2
F(4) = F(3) + F(2) = 3
F(5) = F(4) + F(3) = 5
F(6) = F(5) + F(4) = 8
So, F(6) = 8
Time Complexity Comparison
| Method | Time Complexity | Space Complexity |
|---|---|---|
| Simple Recursion | O(2n) | O(n) |
| Memoization | O(n) | O(n) |
| Tabulation | O(n) | O(n) |
Why DP is Better than Simple Recursion?
- It avoids repeated calculation
- It saves execution time
- It gives efficient solution for large n
- It converts exponential solution into polynomial solution
The Fibonacci Series using Dynamic Programming is a classic example of solving overlapping subproblems efficiently. Instead of computing the same Fibonacci values again and again, DP stores previous results and reuses them. This makes the solution much faster and more efficient than ordinary recursion.
Dynamic Programming হলো একটি optimization technique যেখানে বড় problem-কে ছোট ছোট overlapping subproblem-এ ভাগ করে তাদের result store করা হয়, যাতে একই হিসাব বারবার করতে না হয়।
Fibonacci Series-এ প্রতিটি term-এর নিয়ম হলো:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2), যেখানে n ≥ 2
অর্থাৎ, F(5) বের করতে F(4) এবং F(3) লাগে। আবার F(4) বের করতে F(3) এবং F(2) লাগে। এভাবে একই মান বারবার calculate হয়। তাই Fibonacci হলো Dynamic Programming বোঝার খুব পরিচিত example।
Fibonacci-তে Dynamic Programming কেন ব্যবহার করা হয়?
যদি আমরা simple recursion ব্যবহার করি, তাহলে একই subproblem বারবার solve হয়, ফলে অনেক সময় নষ্ট হয়।
যেমন, F(5)-এর recursion tree হলো:
F(5)
/ \
F(4) F(3)
/ \ / \
F(3) F(2) F(2) F(1)
/ \ / \ / \
F(2) F(1)F(1)F(0)F(1)F(0)
/ \
F(1) F(0)
এই tree-তে দেখা যাচ্ছে F(3) এবং F(2) একাধিকবার calculate হচ্ছে। এই repeated calculation-কেই overlapping subproblems বলা হয়।
Dynamic Programming এই সমস্যার সমাধান করে, কারণ এটি একবার calculate করা result store করে রাখে।
Dynamic Programming-এর দুইটি পদ্ধতি
- 1. Memoization (Top-Down): recursion ব্যবহার করে এবং আগের result store করে।
- 2. Tabulation (Bottom-Up): base value থেকে শুরু করে ধাপে ধাপে answer তৈরি করে।
1. Memoization (Top-Down) দিয়ে Fibonacci
এই পদ্ধতিতে recursion ব্যবহার করা হয়, কিন্তু কোনো Fibonacci value calculate করার আগে দেখা হয় এটি আগে calculate করা হয়েছে কিনা।
মূল ধারণা:
- যদি value আগে store করা থাকে, তাহলে সেটি return করা হবে
- না থাকলে calculate করে store করা হবে
#include <stdio.h>
int dp[1000];
int fib(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (dp[n] != -1)
return dp[n];
dp[n] = fib(n-1) + fib(n-2);
return dp[n];
}
int main() {
int n, i;
printf("Enter n: ");
scanf("%d", &n);
for(i = 0; i < 1000; i++)
dp[i] = -1;
printf("Fibonacci number = %d", fib(n));
return 0;
}
ব্যাখ্যা:
এখানে dp[] array-এ আগের calculate করা Fibonacci value store থাকে। যেমন fib(4) একবার বের হলে পরের বার আর calculate করতে হয় না, array থেকে সরাসরি নেওয়া যায়।
2. Tabulation (Bottom-Up) দিয়ে Fibonacci
এই পদ্ধতিতে ছোট value থেকে শুরু করে ধাপে ধাপে বড় value বের করা হয়।
#include <stdio.h>
int main() {
int n, i;
int dp[1000];
printf("Enter n: ");
scanf("%d", &n);
dp[0] = 0;
dp[1] = 1;
for(i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
printf("Fibonacci number = %d", dp[n]);
return 0;
}
ব্যাখ্যা:
এখানে প্রথমে F(0)=0 এবং F(1)=1 store করা হয়। তারপর F(2), F(3), F(4) … এভাবে F(n) পর্যন্ত বের করা হয়। তাই প্রতিটি value একবারই calculate হয়।
উদাহরণ:
F(6) বের করতে:
F(0) = 0
F(1) = 1
F(2) = F(1) + F(0) = 1
F(3) = F(2) + F(1) = 2
F(4) = F(3) + F(2) = 3
F(5) = F(4) + F(3) = 5
F(6) = F(5) + F(4) = 8
অতএব, F(6) = 8
Time Complexity তুলনা
| Method | Time Complexity | Space Complexity |
|---|---|---|
| Simple Recursion | O(2n) | O(n) |
| Memoization | O(n) | O(n) |
| Tabulation | O(n) | O(n) |
Simple Recursion-এর চেয়ে DP কেন ভালো?
- একই হিসাব বারবার করে না
- Execution time কম লাগে
- বড় n-এর জন্য efficient solution দেয়
- Exponential solution-কে polynomial solution-এ রূপান্তর করে
Dynamic Programming ব্যবহার করে Fibonacci Series হলো overlapping subproblem সমাধানের একটি classic example। সাধারণ recursion-এ একই Fibonacci value বারবার calculate হয়, কিন্তু DP আগের result store করে reuse করে। ফলে solution অনেক দ্রুত এবং efficient হয়।
Greedy Algorithm
A Greedy Algorithm is a problem-solving approach that makes the best choice at the current step without considering the future consequences.
It follows a top-down approach and once a decision is made, it is not changed later.
Important Note:
Greedy algorithms do not always give the optimal solution for every problem because they focus on local optimum instead of global optimum.
Properties of Greedy Algorithm
- Greedy Choice Property:
If a problem can be solved by making the best choice at each step without revisiting previous decisions, then it satisfies greedy choice property.
Example: Selecting the highest value coin first in coin change problem. - Optimal Substructure:
If the optimal solution of the problem can be built from optimal solutions of its subproblems, then it has optimal substructure.
Example: Activity selection problem.
Greedy Algorithm is simple and fast, but it works correctly only for problems that satisfy greedy choice property and optimal substructure.
Greedy Algorithm হলো এমন একটি পদ্ধতি যেখানে প্রতিটি ধাপে বর্তমানের সবচেয়ে ভালো সিদ্ধান্ত নেওয়া হয়, ভবিষ্যতের কথা না ভেবে।
এটি top-down approach অনুসরণ করে এবং একবার কোনো সিদ্ধান্ত নিলে তা আর পরিবর্তন করা হয় না।
গুরুত্বপূর্ণ বিষয়:
Greedy algorithm সব সমস্যায় optimal solution দেয় না, কারণ এটি local optimum নিয়ে কাজ করে, global optimum নয়।
Greedy Algorithm-এর বৈশিষ্ট্য
- Greedy Choice Property:
যদি প্রতিটি ধাপে সেরা সিদ্ধান্ত নিয়ে সমস্যা সমাধান করা যায় এবং আগের সিদ্ধান্ত পরিবর্তন না করতে হয়, তাহলে এই property থাকে।
উদাহরণ: coin change problem-এ বড় coin আগে নেওয়া। - Optimal Substructure:
যদি বড় সমস্যার optimal solution ছোট subproblem-এর optimal solution থেকে তৈরি হয়।
উদাহরণ: activity selection problem।
Greedy Algorithm সহজ ও দ্রুত, তবে সব সমস্যায় কাজ করে না—শুধু নির্দিষ্ট condition-এ কার্যকর।
Travelling Salesman Problem (TSP):
In some versions, greedy approach selects the nearest city at each step to minimize travel cost.
Prim’s Minimum Spanning Tree:
Builds a tree by selecting the smallest edge that connects a new vertex.
Kruskal’s Minimum Spanning Tree:
Selects edges in increasing order of weight to form a minimum spanning tree.
Dijkstra’s Algorithm:
Finds the shortest path from a source vertex by always choosing the nearest unvisited vertex.
Graph Coloring:
Assigns colors to vertices such that no adjacent vertices have the same color using minimum colors.
Vertex Cover:
Selects a minimum set of vertices such that every edge is covered.
Fractional Knapsack:
Selects items based on highest value/weight ratio to maximize profit.
Job Sequencing with Deadline:
Schedules jobs to maximize profit within given deadlines.
Optimal Merge Pattern:
Combines files in such a way that total merging cost is minimized.
The main drawback of the Greedy Algorithm is that it does not always produce the optimal (best) solution.
It makes decisions based on the current best (local optimum) without considering future consequences, which may lead to a wrong final result.
Example
Consider finding the maximum sum path from root to leaf in the following tree:
5
/ \
9 6
/ \ / \
4 2 1 7
Greedy Choice:
At root (5), greedy chooses 9 (larger than 6).
Then from 9, it chooses 4 (larger than 2).
So greedy path = 5 → 9 → 4 = 18
But Optimal Path:
5 → 6 → 7 = 18 (same here, but consider variation below)
Now change value:
5
/ \
9 6
/ \ / \
1 2 1 20
Greedy path = 5 → 9 → 2 = 16
Optimal path = 5 → 6 → 20 = 31
Greedy fails because it chooses the locally best option (9), but misses the globally best solution (6 → 20).
Greedy Algorithm-এর প্রধান সমস্যা হলো এটি সব সময় optimal (সর্বোত্তম) solution দেয় না।
এটি শুধুমাত্র বর্তমানের সেরা (local optimum) সিদ্ধান্ত নেয়, ভবিষ্যতের ফলাফল বিবেচনা করে না।
উদাহরণ:
ধরা যাক, root থেকে leaf পর্যন্ত maximum sum path বের করতে হবে:
5
/ \
9 6
/ \ / \
4 2 1 7
Greedy সিদ্ধান্ত:
প্রথমে 5 থেকে 9 নেওয়া হবে (কারণ 9 > 6)।
তারপর 9 থেকে 4 নেওয়া হবে (কারণ 4 > 2)।
Greedy path = 5 → 9 → 4 = 18
কিন্তু নিচের পরিবর্তিত tree দেখো:
5
/ \
9 6
/ \ / \
1 2 1 20
Greedy path = 5 → 9 → 2 = 16
Optimal path = 5 → 6 → 20 = 31
Greedy local best নির্বাচন করে, কিন্তু global best solution মিস করতে পারে।
[urcr_restrict]
A spanning tree is a subgraph of a graph G that includes all vertices with the minimum number of edges and contains no cycles.
- No Cycles: A spanning tree does not contain any cycles.
- Connected: It connects all vertices of the graph, so it cannot be disconnected.
- Edges: It has exactly n − 1 edges where n is the number of vertices.
- Existence: Every connected undirected graph has at least one spanning tree.
- Disconnected Graph: A disconnected graph cannot have a spanning tree.
- Number of Spanning Trees: A complete graph can have up to nn−2 spanning trees.
A Minimum Spanning Tree (MST) is a spanning tree of a weighted graph that has the minimum total edge weight compared to all other spanning trees.
- Definition: It connects all vertices with the least possible total weight.
- Weight Meaning: The weight can represent distance, cost, traffic, or any measurable value on edges.
- Property: MST has no cycles and contains n − 1 edges.
Spanning tree হলো একটি graph G-এর subgraph যা সব vertex-কে cover করে, সর্বনিম্ন edge ব্যবহার করে এবং কোনো cycle থাকে না।
- No Cycles: Spanning tree-এ কোনো cycle থাকে না।
- Connected: এটি graph-এর সব vertex-কে সংযুক্ত করে, তাই এটি disconnected হতে পারে না।
- Edges: এতে ঠিক n − 1টি edge থাকে, যেখানে n হলো vertex-এর সংখ্যা।
- Existence: প্রতিটি connected undirected graph-এর অন্তত একটি spanning tree থাকে।
- Disconnected Graph: কোনো disconnected graph-এর spanning tree থাকে না।
- Number of Spanning Trees: একটি complete graph-এ সর্বোচ্চ nn−2 টি spanning tree থাকতে পারে।
Minimum Spanning Tree (MST) হলো একটি weighted graph-এর spanning tree যার মোট edge weight সবচেয়ে কম হয়।
- Definition: এটি সব vertex-কে সর্বনিম্ন মোট weight দিয়ে সংযুক্ত করে।
- Weight Meaning: Weight distance, cost, traffic বা অন্য কোনো মান নির্দেশ করতে পারে।
- Property: MST-এ কোনো cycle থাকে না এবং এতে n − 1টি edge থাকে।
Example:

[/urcr_restrict]
Prim’s Algorithm is used to find the Minimum Spanning Tree (MST) of a graph.
Algorithm Steps:
- Take a graph G(V, E) and select a source vertex S.
- Create a visited[] array and mark the source vertex as visited.
- Check all adjacent vertices of the visited vertices.
- Select the edge with minimum cost that connects a visited vertex to an unvisited vertex.
- Add that edge to the Minimum Spanning Tree.
- Add the new vertex to the visited[] array.
- Repeat the process until all vertices are visited.
- Finally, calculate the total cost of the MST.
Prim’s Algorithm ব্যবহার করা হয় একটি graph-এর Minimum Spanning Tree (MST) বের করার জন্য।
Algorithm Steps:
- একটি graph G(V, E) নেওয়া হয় এবং একটি source vertex S নির্বাচন করা হয়।
- visited[] array তৈরি করে source vertex-কে visited হিসেবে mark করা হয়।
- visited vertex-এর adjacent vertices গুলো check করা হয়।
- visited থেকে unvisited vertex-এর মধ্যে minimum cost edge নির্বাচন করা হয়।
- এই edge-টি Minimum Spanning Tree-তে যোগ করা হয়।
- নতুন vertex-টিকে visited[] array-তে যোগ করা হয়।
- সব vertex visited না হওয়া পর্যন্ত process টি repeat করা হয়।
- শেষে MST-এর total cost নির্ণয় করা হয়।



Kruskal’s Algorithm is used to find the Minimum Spanning Tree (MST) of a graph. It selects edges with the smallest weight while avoiding cycles.
Algorithm Steps:
- Take a graph G(V, E).
- Sort all edges in ascending order of weight and store in edge[].
- Create a forest (each vertex is a separate tree).
- Select the minimum cost edge from edge[].
- Add the edge to the forest if it does not form a cycle.
- Mark the connected vertices as visited.
- Repeat the process until all vertices are connected.
- Finally, the Minimum Spanning Tree is formed.
- Calculate the total cost of the MST.
Kruskal’s Algorithm ব্যবহার করা হয় একটি graph-এর Minimum Spanning Tree (MST) বের করার জন্য। এটি ছোট weight-এর edge বেছে নিয়ে cycle এড়িয়ে MST তৈরি করে।
Algorithm Steps:
- একটি graph G(V, E) নেওয়া হয়।
- সব edge-কে ascending order অনুযায়ী sort করে edge[]-এ রাখা হয়।
- একটি forest তৈরি করা হয় (প্রতিটি vertex আলাদা tree হিসেবে থাকে)।
- edge[] থেকে minimum cost edge নির্বাচন করা হয়।
- যদি edge যোগ করলে cycle না হয়, তাহলে forest-এ যোগ করা হয়।
- সংযুক্ত vertex গুলোকে visited হিসেবে ধরা হয়।
- সব vertex যুক্ত না হওয়া পর্যন্ত process টি repeat করা হয়।
- শেষে Minimum Spanning Tree তৈরি হয়।
- MST-এর total cost নির্ণয় করা হয়।

As the first step, sort all the edges in the given graph in an ascending order and store the values in an array.
Edge B→D A→B C→F F→E B→C G→F A→G C→D D→E C→G Cost 5 6 9 10 11 12 15 17 22 25
Then, construct a forest of the given graph on a single plane.
From the list of sorted edge costs, select the least cost edge and add it onto the forest in output graph.


Dijkstra’s Algorithm is used to find the shortest path from a single source vertex to all other vertices in a graph. It is a greedy algorithm and works for both directed and undirected graphs.
Algorithm Steps:
- Take a graph G(V, E) and select a source vertex S.
- Create two arrays: distance[] and visited[].
- Initialize distance[S] = 0 and all other distances = ∞.
- Select the unvisited vertex with minimum distance.
- Mark the selected vertex as visited.
- Update the distance of its adjacent vertices if a shorter path is found.
- Repeat the process until all vertices are visited.
- Finally, the shortest path spanning tree is obtained.
Dijkstra’s Algorithm ব্যবহার করা হয় একটি graph-এ একটি source vertex থেকে সব vertex-এর shortest path বের করার জন্য। এটি একটি greedy algorithm এবং directed ও undirected graph-এ কাজ করে।
Algorithm Steps:
- একটি graph G(V, E) নেওয়া হয় এবং একটি source vertex S নির্বাচন করা হয়।
- দুটি array তৈরি করা হয়: distance[] এবং visited[]।
- distance[S] = 0 এবং বাকি সব vertex-এর distance = ∞ সেট করা হয়।
- যে unvisited vertex-এর distance সবচেয়ে কম, সেটি নির্বাচন করা হয়।
- নির্বাচিত vertex-টিকে visited হিসেবে mark করা হয়।
- এর adjacent vertices-গুলোর distance update করা হয় (যদি ছোট path পাওয়া যায়)।
- সব vertex visited না হওয়া পর্যন্ত process টি repeat করা হয়।
- শেষে shortest path spanning tree পাওয়া যায়।

Step 1:
Visit Node A
Neighbors of A are B (weight 4) and C (weight 5).
Update B: 0+4=4
Update C: 0+5=5
Current shortest unvisited node: B (4).
Step 2:
Visit Node B
Neighbors of B are C, D, E.
Update C: min(5,4+11)=5 (No change)
Update D: 4+9=13
Update E: 4+7=11
Current shortest unvisited node: C (5).
Step 3:
Visit Node C
Neighbor of C is E.
Update E: min(11,5+3)=8
Current shortest unvisited node: E (8).
Step 4:
Visit Node E
Neighbors of E are D and F.
Update D: min(13,8+13)=13 (No change)
Update F: 8+6=14
Current shortest unvisited node: D (13).
Step 5:
Visit Node D
Neighbor of D is F.
Update F: min(14,13+2)=14 (No change)
Current shortest unvisited node: F (14).
Step 6:
Visit Node F
All nodes visited.
Reason:
- Dijkstra’s Algorithm follows a greedy approach.
- Once a vertex is marked as visited, its shortest distance is considered final.
- It does not reconsider that vertex again.
- If there is a negative weight edge, a shorter path may appear later.
- But Dijkstra cannot update it, so it gives wrong result.
Example:

- Graph edges:
- A → B = 4
- A → C = 1
- C → B = -3
- Start from source A
- Step 1: distance(A→B) = 4, distance(A→C) = 1
- Step 2: Pick C (smallest distance = 1)
- Update B through C → distance = 1 + (-3) = -2
- But if B was already marked visited earlier, Dijkstra would keep distance = 4
- So the correct shortest path is -2, but Dijkstra may give 4 ❌
- Dijkstra fails when graph has negative weight edges.
- Bellman-Ford Algorithm is used instead in such cases.
Dijkstra’s Algorithm negative weight-এ কেন fail করে
কারণ:
- Dijkstra একটি greedy algorithm।
- একবার কোনো vertex visited হলে সেটির distance final ধরা হয়।
- পরে আর সেই vertex update করা হয় না।
- কিন্তু যদি negative weight edge থাকে, পরে আরও ছোট path পাওয়া যেতে পারে।
- Dijkstra সেটা update করতে পারে না, তাই ভুল result দেয়।
উদাহরণ:
- Graph edges:
- A → B = 4
- A → C = 1
- C → B = -3
- Source = A
- Step 1: A→B = 4, A→C = 1
- Step 2: C নির্বাচন করা হয় (distance কম = 1)
- C → B দিয়ে নতুন distance = 1 + (-3) = -2
- কিন্তু যদি B আগে visited হয়ে যায়, তাহলে Dijkstra 4-ই রেখে দেয়
- সঠিক shortest path = -2, কিন্তু Dijkstra দেয় 4 ❌
উপসংহার:
- Negative weight থাকলে Dijkstra কাজ করে না।
- এই ক্ষেত্রে Bellman-Ford Algorithm ব্যবহার করা হয়।
Previous Job Question on Design and Analysis of Algorithm
- Suppose you have a list of items: 41, 25, 87, 57, 80, 79, 19, 36, 42, 7. Using Quick Sort algorithm sort this list. You should explain each step.[Ministry of Food, Network/Website Manager(ICT),2024]
- What are the rules for calculating the n-th Fibonacci number, and what is the recurrence relation that defines this sequence?[BPSC, AME, 2024]
- Why is Merge Sort preferred over Quick Sort in certain applications, despite having higher space complexity?[Ministry of Food, Network/Website Manager(ICT),2024]
- Describe step-by-step how Binary Search locates a target value in a sorted array. Why does it fail if the array is unsorted? CB, AP, 2026
- Explain the logic of “Bubble Sort”. Why is it considered inefficient for large datasets compared to Merge Sort? CB, AP, 2026
Previous Question with ANSWER on Design & Analysis of Algorithm
Logic of Bubble Sort
Bubble Sort is a simple comparison-based sorting algorithm. It works by repeatedly comparing adjacent elements and swapping them if they are in the wrong order. After each pass, the largest unsorted element “bubbles up” to its correct position at the end of the array.
Steps
1) Compare the first two elements.
2) Swap them if the first is greater than the second.
3) Move to the next pair and repeat until the end of the array.
4) Repeat the entire process for the remaining unsorted portion until the array is sorted.
Time Complexity
Worst and average case: O(n²)
Why Bubble Sort is Inefficient for Large Datasets
Bubble Sort makes a large number of comparisons and swaps even when the array is nearly sorted. As the input size grows, the number of operations increases quadratically, making it very slow for large datasets.
Why Merge Sort is Better
Merge Sort uses the divide and conquer approach. It divides the array into smaller parts, sorts them, and then merges them efficiently. Its time complexity is O(n log n), which is much faster than Bubble Sort for large datasets.
Bubble Sort এর Logic
Bubble Sort হলো একটি সহজ sorting algorithm যেখানে বারবার পাশাপাশি থাকা element গুলো compare করা হয় এবং ভুল order এ থাকলে swap করা হয়। প্রতিটি pass শেষে সবচেয়ে বড় element টি তার সঠিক জায়গায় চলে যায়, ঠিক যেন bubble উপরে উঠে আসে।
Steps
1) প্রথম দুইটি element compare করা হয়।
2) ভুল order হলে swap করা হয়।
3) পরবর্তী pair এ গিয়ে একই কাজ করা হয়।
4) পুরো array sorted না হওয়া পর্যন্ত process টি repeat করা হয়।
Time Complexity
Worst এবং average case: O(n²)
বড় Dataset এ কেন Bubble Sort অকার্যকর
Bubble Sort অনেক বেশি comparison ও swap করে, এমনকি array প্রায় sorted হলেও। Dataset বড় হলে operation সংখ্যা খুব দ্রুত বেড়ে যায়, তাই এটি ধীরগতির হয়ে যায়।
Merge Sort কেন ভালো
Merge Sort divide and conquer পদ্ধতি ব্যবহার করে। এটি array কে ছোট অংশে ভাগ করে sort করে তারপর merge করে। এর time complexity O(n log n), যা বড় dataset এর জন্য Bubble Sort এর চেয়ে অনেক বেশি কার্যকর।
How Binary Search Works (Step-by-Step)
Binary Search is a searching technique used to find a target value in a sorted array by repeatedly dividing the search range into half.
Steps
1) Set two pointers: low = first index, high = last index.
2) Find the middle index: mid = (low + high) / 2.
3) Compare the target with array[mid]:
• If target == array[mid] → target found.
• If target < array[mid] → search left half (high = mid − 1).
• If target > array[mid] → search right half (low = mid + 1).
4) Repeat steps 2–3 until the target is found or low > high (target not present).
Example
Array: [10, 20, 30, 40, 50], Target = 40
low=0, high=4 → mid=2 → array[2]=30 (40>30) → search right
low=3, high=4 → mid=3 → array[3]=40 → found
Why it fails if the array is unsorted
Binary Search depends on the sorted order to decide whether to go left or right. If the array is unsorted, the comparisons (target < or > mid value) do not correctly indicate which half contains the target, so the algorithm may skip the target and give wrong results.
Binary Search কীভাবে কাজ করে (Step-by-Step)
Binary Search হলো একটি searching technique যা sorted array তে target value খুঁজতে search range কে বারবার half করে।
Steps
1) দুইটি pointer সেট করি: low = প্রথম index, high = শেষ index।
2) middle index বের করি: mid = (low + high) / 2।
3) target এর সাথে array[mid] compare করি:
• যদি target == array[mid] → target পাওয়া গেছে।
• যদি target < array[mid] → left half এ যাব (high = mid − 1)।
• যদি target > array[mid] → right half এ যাব (low = mid + 1)।
4) target না পাওয়া পর্যন্ত অথবা low > high হওয়া পর্যন্ত step গুলো repeat করি।
Example
Array: [10, 20, 30, 40, 50], Target = 40
low=0, high=4 → mid=2 → array[2]=30 (40>30) → right half এ search
low=3, high=4 → mid=3 → array[3]=40 → found
Array unsorted হলে কেন fail করে
Binary Search এর মূল শর্ত হলো array টি sorted হতে হবে, কারণ mid এর value দেখে সিদ্ধান্ত নেওয়া হয় target left এ নাকি right এ। Array unsorted হলে এই সিদ্ধান্ত ভুল হয়, ফলে target থাকা সত্ত্বেও algorithm সেটিকে skip করে ভুল result দিতে পারে।
Merge Sort Algorithm
Merge Sort is a divide and conquer algorithm. It recursively splits an array into two halves, sorts each half, and then merges the sorted halves back together.
Merge Sort Function
void MergeSort(int A[], int p, int r) {
if (p < r) {
int q = (p + r) / 2; // Find the middle index
MergeSort(A, p, q); // Recursively sort the left half
MergeSort(A, q + 1, r); // Recursively sort the right half
merge(A, p, q, r); // Merge the sorted halves
}
}
Merge Function
void merge(int A[], int p, int q, int r) {
int n1 = q - p + 1;
int n2 = r - q;
int L[n1], R[n2];
// Copy data into temporary arrays
for (int i = 0; i < n1; i++) L[i] = A[p + i];
for (int j = 0; j < n2; j++) R[j] = A[q + 1 + j];
int i = 0, j = 0, k = p;
// Merge the two subarrays
while (i < n1 && j < n2) {
if (L[i] <= R[j]) A[k++] = L[i++];
else A[k++] = R[j++];
}
// Copy remaining elements of L[]
while (i < n1) A[k++] = L[i++];
// Copy remaining elements of R[]
while (j < n2) A[k++] = R[j++];
}
Dijkstra(G, s)
for each vertex v in G
dist[v] = infinity
visited[v] = false
dist[s] = 0
for i = 1 to number of vertices
u = vertex with minimum dist[u] among unvisited vertices
visited[u] = true
for each neighbor v of u
if visited[v] == false and dist[u] + w(u,v) < dist[v]
dist[v] = dist[u] + w(u,v)
return dist
Sonali Bank, O(it), 21
Binary Search Algorithm
Binary Search is used to find a key element in a sorted array by repeatedly dividing the search interval into two halves.
Pseudocode:
BINARY_SEARCH(A, n, key) 1. low ← 0 2. high ← n - 1 3. while low ≤ high do 4. mid ← (low + high) / 2 5. if A[mid] = key then 6. return mid 7. else if A[mid] < key then 8. low ← mid + 1 9. else 10. high ← mid - 1 11. return -1
Explanation:
- If the key equals the middle element, return its index.
- If the key is greater, search the right half.
- If the key is smaller, search the left half.
- If not found, return -1.
Time Complexity: O(log n)
Space Complexity: O(1)
Binary Search অ্যালগরিদম
Binary Search একটি sorted array-এ কোনো key খুঁজতে ব্যবহৃত হয়। এটি বারবার array-কে দুই ভাগে বিভক্ত করে অনুসন্ধান করে।
Pseudocode:
BINARY_SEARCH(A, n, key) 1. low ← 0 2. high ← n - 1 3. while low ≤ high do 4. mid ← (low + high) / 2 5. if A[mid] = key then 6. return mid 7. else if A[mid] < key then 8. low ← mid + 1 9. else 10. high ← mid - 1 11. return -1
ব্যাখ্যা:
- key যদি মধ্যম উপাদানের সমান হয়, index ফেরত দেয়।
- key বড় হলে ডান অংশে খোঁজে।
- key ছোট হলে বাম অংশে খোঁজে।
- না পেলে -1 ফেরত দেয়।
Time Complexity: O(log n)
Space Complexity: O(1)
Worst-Case Time and Space Complexity of Quicksort
- Time Complexity (Worst Case): O(n2)
- Space Complexity (Worst Case): O(n) due to recursive calls
How Worst-Case Behavior Occurs
Quicksort works by selecting a pivot element and partitioning the array into two subarrays. In the worst case:
- The pivot is always the smallest or largest element.
- This happens when the array is already sorted (ascending or descending) and the first or last element is chosen as pivot.
- As a result, one subarray has n−1 elements and the other has 0 elements, leading to maximum recursive calls and O(n2) comparisons.
Proper pivot selection (like median-of-three) can help avoid this worst-case behavior.
Quicksort-এর Worst-Case Time এবং Space Complexity
- Time Complexity (Worst Case): O(n2)
- Space Complexity (Worst Case): O(n), কারণ recursive calls এর জন্য stack memory লাগে
Worst-Case কিভাবে ঘটে
Quicksort একটি pivot নির্বাচন করে array-কে দুটি subarray-তে ভাগ করে। Worst case ঘটে যখন:
- Pivot সবসময় সবচেয়ে ছোট বা সবচেয়ে বড় element হয়।
- উদাহরণস্বরূপ, array আগে থেকেই sorted থাকে (ascending বা descending) এবং প্রথম বা শেষ element pivot হিসেবে নেওয়া হয়।
- ফলে, একটি subarray-তে থাকে n−1 element এবং অন্যটির size 0, যার কারণে recursive calls সর্বাধিক হয় এবং O(n2) comparisons লাগে।
Pivot-কে median-of-three বা random selection করলে এই worst-case এড়ানো যায়।
