File Update
File Update
Bubble sort , Selection sort , Count Sort, Heap sort ,Quick sort
WEEK 4
Prepared by Ali Hussein Hameed & Ali Odeh Khudhair
2025-2026
Sorting algorithms
Introduction
Sorting algorithms are fundamental in computer science and data
structures & algorithms (DSA). These play a crucial role in organizing
data efficiently.
1. Based on Comparison:
1
Figure 1: Classification of sorting algorithms
2. Based on Stability
2
1) Bubble Sort
Bubble sort is a simple sorting algorithm. This sorting algorithm is
comparison-based algorithm in which each pair of adjacent elements is
compared and the elements are swapped if they are not in order. This
algorithm is not suitable for large data sets as its average and worst case
complexity are of O(n 2 ) where n is the number of items.
Step 1 − Check if the first element in the input array is greater than the
next element in the array.
Step 4 − Check if the elements are sorted; if not, repeat the same process
(Step 1 to Step 3) from the last element of the array to the first.
Example 1:
We take an unsorted array for our example. Bubble sort takes Ο(n2) time
so we're keeping it short and precise.
Bubble sort starts with very first two elements, comparing them to
check which one is greater.
3
In this case, value 33 is greater than 14, so it is already in sorted
locations. Next, we compare 33 with 27.
Next we compare 33 and 35. We find that both are in already sorted
positions.
4
Hence they are not sorted. We swap these values. We find that we have
reached the end of the array. After one iteration, the array should look
like this:
we are now showing how an array should look like after each iteration.
After the second iteration, it should look like this:
5
And when there's no swap required, bubble sort learns that an array is
completely sorted.
Example 2:
First pass:
Second pass:
Third pass:
Fourth pass:
6
Pseudo-code:
• No swaps occur, so the algorithm stops after the first pass → O(n)
7
2. Worst Case Time Complexity Analysis of Bubble Sort: O(N2)
The worst-case condition for bubble sort occurs when elements of the
array are arranged in decreasing order.
Example: [6, 5, 4, 3, 2, 1]
• Many swaps in every pass → O(n²).
8
4. Space Complexity Analysis of Bubble Sort:
The space complexity of Bubble Sort is O(1). This means that the amount
of extra space (memory) required by the algorithm remains constant
regardless of the size of the input array being sorted.
Advantages of Bubble Sort:
• Simple to Implement: Easy to understand and code.
9
2) Selection Sort
❖ This method is used for sorting arrays in ascending or in descending
order.
❖ Selection Sort is a comparison-based sorting algorithm.
❖ Selection sort arrange n elements of array by placing the smallest
element in proper position in case of ascending order.
❖ If an array has n elements, n-1 iterations are required to sort the
array.
1. Start from the first element and find the smallest element in the
entire array by iterating over it.
3. Now, move to the second element find the next smallest in the
remaining unsorted portion and swap it with the second position.
Example 1:
Iteration #1
Select the first position element in the list, compare it with all other elements
in the list and whenever we found a smaller element than the element at first
position then swap those two elements.
10
List after 1st iteration
11
Iteration #2
Select the second position element in the list, compare it with all other
elements in the list and whenever we found a smaller element than the
element at first position then swap those two elements.
Iteration #3
Select the third position element in the list, compare it with all other
elements in the list and whenever we found a smaller element than the
element at first position then swap those two elements.
Iteration #4
Select the fourth position element in the list, compare it with all other
elements in the list and whenever we found a smaller element than the
element at first position then swap those two elements.
Iteration #5
Select the fifth position element in the list, compare it with all other elements
in the list and whenever we found a smaller element than the element at first
position then swap those two elements.
12
Iteration #6
Select the sixth position element in the list, compare it with all other
elements in the list and whenever we found a smaller element than the
element at first position then swap those two elements.
Iteration #7
Select the seventh position element in the list, compare it with all other
elements in the list and whenever we found a smaller element than the
element at first position then swap those two elements.
Example 2:
First pass:
• Find the minimum element in [4, 2, 7, 1], which is 1
• Swap 1 with 4 to get [1, 2, 7, 4]
Second pass:
• Find the minimum element in [2, 7, 4], which is 2
• Swap 2 with 2 to get [1, 2, 7, 4] (no change)
Third pass:
• Find the minimum element in [7, 4], which is 4
• Swap 4 with 7 to get [1, 2, 4, 7]
Fourth pass:
• The last element 7 is already in place, no need to swap
• Final sorted list: [1, 2, 4, 7].
13
Pseudo-code:
14
The number of comparisons is the same for any input order.
15
Selection Sort VS Bubble Sort
S. Selection Sort Bubble Sort
No.
2. Its Time complexity in the Best case is Its Time complexity in the Best
O(N^2) case is O(N)
5. This sorting algorithm uses the selection This sorting algorithm uses
method exchanging method
Q: Why are algorithms like Bubble Sort and Selection Sort taught despite being
weak in practice? Explain their value in terms of analytical understanding and time
complexity?
Analytical understanding:
• Bubble Sort: Shows step-by-step progress (largest moves to the end each
pass) and how input order + early-exit can improve best-case performance.
• Selection Sort: Shows a fixed strategy (pick the minimum each step), so it
doesn’t benefit much from sorted input.
Time complexity:
• Bubble Sort: Best O(n), Average O(n2), Worst O(n2) performance depends
strongly on input order.
• Selection Sort: Best/Average/Worst all O(n2) it still scans the remaining
elements to find the minimum each time, regardless of input order.
16
3) Counting Sort
Counting Sort is a non-comparison-based sorting algorithm. It is highly
efficient when the range of input values is small compared to the number
of elements.
The main idea is to count the occurrences of each element and then use
these counts to determine the correct position of each element in the
sorted output.
• Find the Range: First, find the largest number in the list. This tells
us how many different numbers we need to count.
• Count Each Number: Make a list of counters, one for each possible
number up to the largest number. Go through the original list and
increase the counter for each number you see.
• Build the Sorted List: Make a new list for the sorted numbers. Go
through the original list again. For each number, use the counter to
find where it goes in the sorted list, then decrease the counter for
that number.
For example, if you have a list of numbers like [4, 2, 2, 8, 3, 3, 1], you count
how many times each number appears, and then use these counts to sort
the list into [1, 2, 2, 3, 3, 4, 8].
Count sort is especially useful when you have a lot of numbers in a small
range. It’s simple and fast, making it a great tool for sorting.
17
Count Sort Example:
• To store the sorted data, you will now initialize a new count array
with length "max+1" and all elements set to 0.
• Later, as shown in the figure, you will store elements of the given
array with the corresponding index in the count array.
• Now, you will change the count array by adding the previous
counts to produce the cumulative sum of an array, as shown below:
18
• Because the original array has nine inputs, you will create another
empty array with nine places to store the sorted data, place the
elements in their correct positions, and reduce the count by one.
19
Pseudo Code of Count Sort Algorithm
20
1. Best-Case
2. Worst-Case
Counting Sort still runs the same loops the same number of times (based
on n and k), regardless of input order:
21
Advantages of Count Sort
22
Heap Data Structure
A Heap is a special Tree-based data structure in which the tree is a complete
binary tree and satisfies the heap property:
Properties of a Binary Heap
1. They are complete binary trees: This means all levels are totally filled
(except maybe the last level), and the nodes in the last level are as left as
possible.
2. Heaps are typically of two types — max heap and min heap: In a max heap,
the value of a node is always greater than or equal to the value of each of its
children. Conversely, in a min heap, the value of a parent is always <= the value
of each of its children.
23
3. In a max heap, the element at the root will always be the maximum. In a min
heap, the element at the root will always be the minimum
4) Heap sort
Heap sort is a comparison-based sorting technique based on Binary Heap data
structure. It is similar to the selection sort where we first find the minimum
element and place the minimum element at the beginning. Repeat the same
process for the remaining elements.
An Array A that presents a heap with two attribute:
• Length [A]: the number of elements in the array.
• heap-size[A]: the number of elements in the heap stored with array A.
• Length [A] ≥ heap-size[A].
To convert the heap tree into a heap array as shown in the figure below, the root
of the tree A[0] and given index i of a node , the indices of its parent , left child
and right child can be computed as :
• A [0] is the root of the tree
• The parent PARENT(i) is at [ (i-1) / 2] if i≠ 0.
• The left child LEFT(i) is at [2i+1]
• The right child RIGHT(i) is at [2i+2]
24
Heap sort algorithm
Steps on How the heap sort algorithm work
Step 1: Build Heap. Build a heap from the input data. Build a max heap to sort
in increasing order, and build a min heap to sort in decreasing order.
Step 2: Swap Root. Swap the root element with the last item of the heap.
Step 3: Reduce Heap Size. Reduce the heap size by 1.
Step 4: Re-Heapify. Heapify the remaining elements into a heap of the new heap
size by calling heapify on the root node.
Step 5: Call Recursively. Repeat steps 2,3,4 as long as the heap size is greater
than 2.
Algorithm(pseudocode)
1) Heap sort(A)
25
2) BuildMaxHeap(A)
3) MaxHeapify(A,i)
26
Phase 1: Creating the Heap
Example [3, 7, 1, 8, 2, 5, 9, 4, 6]
This tree does not yet represent a max heap. The definition of a max heap is that
parents are always greater than or equal to their children.
27
Invocation No. 2 of the Heapify Method
Second, heapify() is called for the penultimate node: the 1. Its children 5 and 9
are both greater than 1, so the heap condition is violated. To restore the heap
condition, we now swap the larger child with the parent node, i.e., the 9 with
the 1. The heapify() method is now finished again
Since the child node we just swapped has two children itself, the heapify()
method must now check if the heap condition for this child node is still valid. In
this case, the 7 is greater than 4 and 6; the heap condition is fulfilled, and the
heapify() function is finished.
28
Invocation No. 4 of the Heapify Method
Now we have arrived at the root node with element 3. Both child nodes, 8 and
9 are larger, while 9 is the largest child and is, therefore, swapped with the
parent node:
Again, the swapped child node has children itself, so we need to check the heap
condition on this child node. The 5 is greater than the 3, i.e., the heap condition
is not fulfilled. It must be restored by swapping the 5 and the 3:
29
The fourth and last call of the heapify() function has finished. A max heap has
been created:
30
After we've placed the 6 at the root of the tree, it is no longer a max heap.
Therefore, in the next step, we will "repair" the heap.
Phase 2, Step 2: Restoring the Heap Condition
To restore the heap condition, we call the heapify() method known from phase
1 on the root node. This means we compare the 6 with its children, 8 and 5; the
8 is bigger, so we swap it with the 6:
The swapped child node has, in turn, two children, the 7 and the 2. The 7 is
larger than the 6, and we swap these two elements as well:
31
The exchanged child node also has a child, the 4. The 6 is greater than the 4, so
the heap condition is fulfilled at this node. The heapify() function is finished,
and we have a max heap again:
This element is the smallest and remains at the beginning of the array. The
algorithm is finished, the array is sorted:
33
Time and space complexity
This table illustrate the time and space complexity of the heap sort , build max
heap , and max heapify
1- MaxHeapify
This operation maintains the heap property by comparing a node with it
children and swapping if necessary, moving down the tree.
Time complexity
Proof
In a heap of size n, the height is log2n in the worst case the element travels form
the root to a leave , the number of comparison and swaps is proportional to the
height h.
T(n)≤T(2n/3) +Θ(1)
By master Theorem T(n) = O(log n).
Space complexity
O(l) for iterative implementation
O(log n) for recursive implementation du to stack frame.
34
2- BuildMaxHeap
This involves calling Max-Heapify on all non-leaf nodes starting from
index n/2 down to 1.
Time complexity
Proof
An n-element heap has at most [n/2h+1] nodes of height h. the time to heapify a
node at height h is O(h).
Total cost
35
Strengths of Heap Sort
• Heap sort is typically not stable since the operations on the heap can
change the relative order of equal key items. It’s typically an unstable
sorting algorithm.
• If the input array is huge and doesn’t fit into the memory and partitioning
the array is faster than maintaining the heap, heap sort isn’t an option. In
such cases, something like merge sort or bucket sort, where parts of the
array can be processed separately and parallelly, works best.
36
5) Quick sort algorithm
Quick Sort algorithm is a highly efficient, comparison-based sorting algorithm
that follows the divide and conquer approach. It picks an element as a pivot and
partitions the given array around the picked pivot.
37
How Does the Quick Sort Algorithm Work?
Example 1: (Left Pivot)
Let the elements of array are –
In the given array, we consider the leftmost element as pivot. So, in this case,
a[left] = 24, a[right] = 27 and a[pivot] = 24
Since, pivot is at left, so algorithm starts from right and move towards left.
Now, a[pivot] < a[right], so algorithm moves forward one position towards
left, i.e. –
38
Now, a[left] = 19, a[right] = 24, and a[pivot] = 24. Since, pivot is at right, so
algorithm starts from left and moves to right
As a[pivot] > a[left], so algorithm moves one position to right as –
Now, a[left] = 9, a[right] = 24, and a[pivot] = 24. As a[pivot] > a[left], so
algorithm moves one position to right as –
Now, a[left] = 29, a[right] = 24, and a[pivot] = 24. As a[pivot] < a[left], so,
swap a[pivot] and a[left], now pivot is at left, i.e. –
39
Since, pivot is at left, so algorithm starts from right, and move to left. Now,
a[left] = 24, a[right] = 29, and a[pivot] = 24. As a[pivot] < a[right], so
algorithm moves one position to left, as –
Now, a[pivot] = 24, a[left] = 24, and a[right] = 14. As a[pivot] > a[right], so, swap a[pivot]
and a[right], now pivot is at right, i.e. -
Now, a[pivot] = 24, a[left] = 14, and a[right] = 24. Pivot is at right, so the
algorithm starts from left and move to right.
40
Now, a[pivot] = 24, a[left] = 24, and a[right] = 24. So, pivot, left and right are
pointing the same element. It represents the termination of procedure.
Element 24, which is the pivot element is placed at its exact position.
Elements that are right side of element 24 are greater than it, and the
elements that are left side of element 24 are smaller than it.
Now, in a similar manner, quick sort algorithm is separately applied to the left and right
sub-arrays.
In Left sub array now, a[pivot] = 19, a[left] = 19, and a[right] = 14. As, a[pivot]
> a[right], so, algorithm will swap a[pivot] with a[right], and pivot moves to
right.
In Right sub array now, a[pivot] = 29, a[left] = 29, and a[right] = 27. As, a[pivot]
> a[right], so, algorithm will swap a[pivot] with a[right], and pivot moves to
right.
41
now, a[pivot] = 14, a[left] = 14, and a[right] = 9. As, a[pivot] > a[right], so,
algorithm will swap a[pivot] with a[right], and pivot moves to right.
Finally combine the already sorted array. After combine gets done, the array
will be :
9 14 19 24 27 20
A[left]=32 >a[pivot]=14 stop pointer in a[left] ,and compare the pivot with
right, a[right]= 65 >a[pivot]= 14, move right pointer to left, until find number
less than pivot.
a[right]= 6<a[pivot]= 14 then swap(a[left],a[right]).
Move the pointers one more step and find a number greater than the pivot from
the left, then stop when you find it. After that, find a number smaller than the
pivot from the right, and swap the two numbers.
42
Swap a[left]=23, a[right]=7
No swap
Finally combine the already sorted array. After combine gets done, the array
will be :-
43
Divide-and-Conquer in Quick sort
Three-step divide-and-conquer process for sorting a subarray A[p : r]:
Divide by partitioning (rearranging) the array A[p : r] into two (possibly empty)
subarrays A[p : q – 1] (the low side) and A[q + 1 : r] (the high side) such that
each element in the low side of the partition is less than or equal to the
pivot A[q], which is, in turn, less than or equal to each element in the high side.
Compute the index q of the pivot as part of this partitioning procedure.
Conquer by calling quicksort recursively to sort each of the subarrays A[p : q –
1] and A[q + 1 : r].
Combine by doing nothing: because the two subarrays are already sorted, no
work is needed to combine them. All elements in A[p : q 1] are sorted and less
than or equal to A[q], and all elements in A[q + 1 : r] are sorted and greater than
or equal to the pivot A[q]. The entire subarray A[p : r] cannot help but be sorted!
To sort an entire array A, the initial call is QUICKSORT (A, 1, length [A]).
44
Partitioning the array
The key to the algorithm is the PARTITION procedure, which rearranges the
subarray A[p……r] in place.
45
Example Trace
The operation of PARTITION on a sample array. Array entry A[r] becomes the pivot element
x.
(a) The initial array and variable settings. None of the elements have been placed into either
side of the partition.
(b) The value 2 is “swapped with itself” and put into the low side.
(e) The values 1 and 8 are swapped, and the low side grows.
(f) The values 3 and 7 are swapped, and the low side grows.
(g)–(h) The high side of the partition grows to include 5 and 6, and the loop terminates.
(i) Line 7 swaps the pivot element so that it lies between the two sides of the partition, and
line 8 returns the pivot’s new index.
46
Correctness
We need to show that this loop invariant is true prior to the first iteration, that
each iteration of the loop maintains the invariant, that the loop terminates, and
that correctness follows from the invariant when the loop terminates.
1. All entries in A[p .. i] are ≤ pivot.
2. All entries in A[i+1 .. j −1] are > pivot.
3. A[r] = pivot.
Initialization:
Before the loop starts, condition 3 is satisfied because x is assigned the pivot A[r],
and conditions 1 and 2 are trivially satisfied because the subarrays a[p .. i] and
A[i+1 .. j−1] are empty.
Maintenance:
While the loop is running,
• if A[j] ≤ pivot, then i is incremented, A[j] and A[i] are swapped, and j is
incremented. Because of the swap, A[i] ≤ x for condition 1. The item
swapped into A[j-1] > x by the loop invariant, for condition 2.
• If A[j] > pivot, then j is incremented, sustaining condition 2 (the others
are unchanged), as the element added was larger.
Termination:
The loop terminates when j=r, so all elements in A are partitioned into one of
three cases: A[p .. i] ≤ pivot, A[i+1 .. r-1] > pivot, and A[r] = pivot. The last two
lines fix the placement of A[r] by moving it between the two subarrays.
47
Performance of quicksort (Informal Analysis)
The running time of quicksort depends on how balanced each partitioning is,
which in turn depends on which elements are used as pivots. If the two sides of
a partition are about the same size—the partitioning is balanced—then the
algorithm runs asymptotically as fast as merge sort. If the partitioning is
unbalanced, however, it can run asymptotically as slowly as insertion sort. To
allow you to gain some intuition before diving into a formal analysis, this section
informally investigates how quicksort performs under the assumptions of
balanced versus unbalanced partitioning.
48
Recursion tree for worst case
49
3. Effect of Unbalanced Partitioning
It turns out that expected behavior is closer to the best case than the worst
case. Two examples suggest why expected case won't be that bad.
Example: 1-to-9 split
Suppose each call splits the data into 1/10 and 9/10. This is highly
unbalanced: won't it result in horrible performance?
We have log10n full levels and log10/9n levels that are nonempty.
As long as it's constant, the base of the log does not affect asymptotic results.
Any split of constant proportionality will yield a recursion tree of depth
Θ(lg n). In particular (using ≈ to indicate truncation of low order digits),
log10/9n = (log2n) / (log210/9) by formula 3.15
≈ (log2n) / 0.152
= 1/0.152 (log2n)
≈ 6.5788 (log2n)
= Θ(lg n), where c = 6.5788.
So the recurrence and its solution is:
50
4- Intuition for the average case
With random data there will usually be a mix of good and bad splits throughout
the recursion tree.
A mixture of worst case and best case splits is asymptotically the same as best
case:
Both these trees have the same two leaves. The extra level on the left hand side
only increases the height by a factor of 2, even if we iterate the same imbalance
for subsequent levels, and this constant disappears in the Θ analysis.
Both result in O(n lg n), though with a larger constant for the left.
51
with randomized pivoting, the O(n2) case is statistically negligible.
52
4-Operating Environment Preferences
Standard Libraries: Most public libraries (such as std::sort in C++) use a
hybrid algorithm called Introsort. This algorithm starts with Quick Sort for its
speed and then switches to Heap Sort if the call depth exceeds a certain limit,
combining the practical speed of Quick Sort with the theoretical safety of Heap
Sort.
Embedded/Real-Time Systems: Heap Sort is preferred here because the O(n
log n) guarantee is absolute, and memory is often too constrained for the
recursive stack of Quick Sort.
53
References
[1] T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed.
Cambridge, MA, USA: MIT Press, 2022.
[4] T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, Introduction to Algorithms, 4th ed.
Cambridge, MA, USA: MIT Press, 2022.
[6] Dr. A.P.J. Abdul Kalam Technical University, "Heap sort (Data Structures lecture notes),"
2023.
54