Bubble, Insertion, and Selection Sort
Sorting algorithms[1] represent a foundational category of computational procedures designed to arrange elements of a list or array into a specific order, most commonly numerical or lexicographical. The National Institute of Standards and Technology[2] formally defines a sort operation in terms of an initial array of items and a final array, where the final array is a permutation of the initial array and all items are placed in a monotonically increasing or decreasing sequence. Among the dozens of sorting techniques developed since the dawn of computer science, bubble sort, insertion sort, and selection sort are universally classified as the three elementary comparison-based sorting algorithms.[37][38] These algorithms share a quadratic worst-case time complexity[3], denoted as O(n^2) in Big O notation[4]. This mathematical scaling limit makes them generally inefficient for processing large datasets compared to advanced divide-and-conquer algorithms[5] like quicksort or mergesort. Despite their shared asymptotic boundaries, each of the three possesses distinct mechanical properties, historical origins, and memory access patterns.
The primary problem these algorithms solve is the canonical sorting problem. Given a sequence of n elements, the algorithm must produce a permutation of those elements such that each element is less than or equal to the subsequent element. While the mathematical definition of the problem remains constant, the hardware constraints of early computing machines dictated different approaches to solving it. Early programmers had to balance processor cycles, memory capacity, and the physical movement of data on media such as magnetic tape or punched cards. The elementary sorts emerged from these constraints, offering different tradeoffs between the number of comparisons performed and the number of write operations executed.
Today, these three algorithms are primarily studied for pedagogical purposes, as they introduce fundamental computer science concepts such as loop invariants, algorithmic stability, in-place[6] memory manipulation, and adaptive sorting[7].[39] An in-place sort is an algorithm in which the sorted items occupy the same storage as the original items, requiring at most a constant amount of auxiliary memory. Algorithmic stability refers to the preservation of the relative order of records with equal keys. However, the utility of these elementary algorithms is not strictly historical or educational. Specific edge cases in modern software engineering and hardware design still rely on the unique characteristics of insertion sort and selection sort. By examining their mechanisms, history, and variants, one can understand the evolution of algorithmic efficiency and the specific conditions under which these elementary techniques outperform their mathematically superior successors.
Bubble Sort: Mechanism and Complexity
Bubble sort, also known as the sinking sort, operates on a highly intuitive, albeit mathematically inefficient, mechanism of sequential adjacent comparisons. The algorithm passes through the target array from the beginning to the end, comparing each adjacent pair of elements. If a pair is found to be out of the desired order, the algorithm swaps them. This process is repeated until a complete pass through the array occurs without a single swap being executed, which mathematically guarantees that the array is sorted.
During the first pass, the largest element in the array is swapped continuously until it reaches the final position in the sequence. This behavior gives the algorithm its name, as large elements "bubble" to the top of the array (or the end of the list) while smaller elements slowly sink to the bottom. Because the largest unsorted element is guaranteed to reach its correct final position at the end of each pass, subsequent passes can technically ignore the elements at the end of the array that have already been sorted.
The time complexity of bubble sort[8] is O(n^2) in both the worst-case and average-case scenarios. In the worst case, where the array is sorted in reverse order, the algorithm must perform n-1 comparisons in the first pass, n-2 in the second, and so on. This results in exactly (n^2 - n) / 2 comparisons and an equal number of swaps. The average-case complexity is equally poor. The average number of inversions (pairs of elements that are out of order) in a randomly ordered array of n elements is n(n-1)/4. Because each swap in a standard bubble sort removes exactly one inversion, the average number of swaps required is exactly n(n-1)/4. This strictly quadratic behavior ensures that bubble sort scales poorly as the dataset grows. The space complexity[9] is O(1), as it is an in-place sorting algorithm that requires only a single temporary variable to facilitate the swapping of two elements.[39]
Bubble sort is an adaptive algorithm, meaning its performance improves if the input array is already partially sorted. In the best-case scenario, where the array is already perfectly sorted, bubble sort requires only a single pass of n-1 comparisons to verify the order, resulting in a best-case time complexity of O(n). It is also a stable sort[10], meaning that two elements with identical keys will retain their original relative order. The strict greater-than condition required for a swap prevents identical elements from crossing paths.
Origins and History of Bubble Sort
The formalization of bubble sort dates back to the mid-1950s, a period when computer scientists were first attempting to categorize and mathematically analyze the ad hoc sorting methods used on early mainframe computers. The earliest known academic publication detailing the algorithm was written by Edward H. Friend[11] in 1956. Published in the Journal of the ACM under the title "Sorting on Electronic Computer Systems", Friend referred to the technique as the "exchanging sort". Friend noted its simplicity but immediately recognized its severe performance limitations compared to other methods available at the time.
The specific term "bubble sort" was coined several years later by Canadian computer scientist Kenneth E. Iverson[12]. In 1962, Iverson published his foundational textbook A Programming Language[13], which introduced the mathematical notation that would later become the APL programming language. On page 217 of the text, Iverson described the exchanging sort and noted that the process causes each item to "bubble" upward in the sequence until it encounters a larger item. This evocative terminology quickly replaced Friend's nomenclature and became the standard identifier in computer science literature.
Despite its widespread recognition, bubble sort has been heavily criticized by prominent computer scientists for its extreme inefficiency. In 1973, Donald Knuth[14] published the third volume of his seminal work, The Art of Computer Programming, which focused entirely on sorting and searching.[40] After conducting a rigorous mathematical analysis of bubble sort's average-case behavior, Knuth concluded that the algorithm "seems to have nothing to recommend it, except a catchy name and the fact that it leads to some interesting theoretical problems."
The algorithm's endurance in the face of such authoritative dismissal was the subject of a 2003 paper by Owen Astrachan[15] titled "Bubble Sort: An Archaeological Algorithmic Analysis". Astrachan traced the history of the algorithm and argued that its continued presence in computer science curricula is largely due to historical inertia and its utility as a foil for teaching more efficient algorithms. Astrachan confirmed Knuth's assessment, demonstrating that bubble sort is consistently slower than both insertion sort and selection sort on nearly all hardware architectures due to the high volume of memory write operations caused by constant swapping.
Variants of Bubble Sort
The primary mechanical flaw of bubble sort is the "turtle and hare" problem. While large elements (hares) move rapidly to their correct positions at the end of the array, small elements (turtles) located near the end of the array move toward the beginning by only one position per pass. A turtle at the very end of an array of n elements will require n-1 passes to reach the front, forcing the algorithm into its worst-case performance even if all other elements are sorted.
Several variants of bubble sort were developed to mitigate this specific inefficiency. The most prominent variant is cocktail shaker sort[16], also known as bidirectional bubble sort. Instead of passing through the array in only one direction, cocktail shaker sort alternates directions. After bubbling the largest element to the end of the array, the algorithm reverses course, passing from right to left to bubble the smallest element to the beginning. This bidirectional approach effectively eliminates the turtle problem, as small elements at the end of the array are immediately carried to the front. While cocktail shaker sort improves the constant factors of the execution time by reducing the total number of passes, it does not change the asymptotic worst-case time complexity, which remains O(n^2).
Another notable variant is odd-even sort[17], which was developed to adapt the bubble sort mechanism for parallel processing architectures. In odd-even sort, the algorithm alternates between two phases. In the odd phase, elements at odd indices are compared with their immediate right neighbors. In the even phase, elements at even indices are compared with their right neighbors. Because the comparisons in each phase are entirely independent of one another, they can be executed simultaneously by multiple processors. While odd-even sort remains O(n^2) on a single processor, its time complexity can be reduced to O(n) on a parallel computing system with O(n) processors.
Insertion Sort: Mechanism and Complexity
Insertion sort[18] operates by conceptually dividing the target array into two distinct sections: a sorted prefix and an unsorted suffix. Initially, the sorted prefix consists only of the first element in the array, which is trivially sorted by itself. The algorithm then iterates through the unsorted suffix one element at a time. For each element, the algorithm scans backward through the sorted prefix, shifting larger elements one position to the right until it finds the correct sequential location for the new element. The new element is then inserted into this vacated space.
This mechanism closely mirrors the way a human being might sort a hand of playing cards. As each new card is picked up, the player scans their existing hand from right to left and inserts the new card into its proper sequential position.
function insertionSort(array)
for i from 1 to length(array) - 1
key = array[i]
j = i - 1
while j >= 0 and array[j] > key
array[j + 1] = array[j]
j = j - 1
array[j + 1] = key
The worst-case time complexity of insertion sort[19] is O(n^2), which occurs when the input array is sorted in reverse order.[41] In this scenario, every new element must be compared against every element in the sorted prefix, and every element in the prefix must be shifted to the right. The average-case time complexity is also O(n^2). The space complexity is O(1), as the shifting and insertion operations are performed in-place.
Insertion sort is highly adaptive. If the input array is already sorted, the algorithm only needs to perform one comparison per element to verify that it is greater than the last element in the sorted prefix. No shifting is required. This results in a best-case time complexity of O(n). Furthermore, insertion sort is highly efficient for nearly sorted arrays. The time complexity is formally O(n + d), where d is the number of inversions. If the number of inversions is small, the algorithm runs in nearly linear time. Like bubble sort, insertion sort is a stable algorithm, preserving the relative order of equal elements.
Origins and History of Insertion Sort
The conceptual basis of insertion sort predates electronic computers, originating in the manual sorting of punched cards and physical files. In the context of electronic computing, the algorithm was one of the very first to be formalized and analyzed.
The earliest documented discussion of insertion sort in a computational context occurred in 1946 during the Moore School Lectures[20] at the Moore School of Electrical Engineering[21] at the University of Pennsylvania. These lectures, formally titled "Theory and Techniques for Design of Electronic Digital Computers", were the first ever course on computer science. During the lectures, John Mauchly[22], the co-inventor of the ENIAC, described a method for sorting data that perfectly matches the modern definition of insertion sort. Mauchly detailed how a machine could read a new value, compare it against a previously sorted list, and shift the existing values to make room for the new entry.
John von Neumann[23] also analyzed insertion sort during this period. Von Neumann analyzed insertion sort and invented mergesort in a separate 1945 unpublished manuscript, not in the First Draft of a Report on the EDVAC. This recognition directly motivated his invention of the mergesort algorithm, which he designed to overcome the quadratic scaling limits of insertion sort by applying a divide-and-conquer strategy.
Despite the invention of mathematically faster algorithms in the 1940s and 1950s, insertion sort remained a subject of intense study because of its low overhead. In early computing, the constant factors hidden by Big O notation were highly significant. The machine code required to implement insertion sort is extremely compact, requiring very few instructions and no recursive function calls. This made it highly attractive for systems with severe memory constraints. Donald Knuth extensively documented the exact machine-level instruction cycles required for insertion sort in The Art of Computer Programming, proving that for very small values of n, insertion sort executes faster than quicksort or mergesort. This threshold, where insertion sort overtakes more complex algorithms, typically falls between 10 and 43 elements depending on the specific hardware architecture and compiler optimizations.
Variants of Insertion Sort
The most historically significant variant of insertion sort is Shellsort[24], invented by Donald Shell[25] and published in the Communications of the ACM in 1959. Shell recognized that insertion sort's primary weakness is that elements move only one position at a time. If a small element is located at the far right of the array, it requires a massive number of shifts to reach the front. Shellsort modifies the algorithm by comparing and sorting elements that are separated by a specific gap distance. The algorithm performs multiple passes, gradually reducing the gap distance until it reaches 1. The final pass with a gap of 1 is identical to a standard insertion sort, but because the earlier passes have moved elements over large distances, the array is nearly sorted, allowing the final insertion sort to run in near-linear time.
The efficiency of Shellsort is entirely dependent on the gap sequence used. Shell's original sequence (n/2, n/4, ..., 1) was later proven to be suboptimal because elements in odd and even positions do not interact until the final pass, resulting in a worst-case complexity of O(n^2). Subsequent researchers developed mathematically optimized gap sequences to improve this bound. The Hibbard sequence, which uses gaps of the form 2^k - 1, reduces the worst-case time complexity to O(n^(3/2)). The Pratt sequence, which uses numbers of the form 2^p 3^q, further reduces the worst-case time complexity to O(n log^2 n), making Shellsort a highly competitive algorithm for medium-sized datasets.
Another common variant is binary insertion sort. In the standard algorithm, finding the correct insertion point requires a linear scan backward through the sorted prefix. Binary insertion sort optimizes this by using a binary search to find the correct location in O(log n) time. While this drastically reduces the number of comparisons, it does not change the overall O(n^2) time complexity. The algorithm must still physically shift all the elements to the right of the insertion point, which requires O(n) memory writes per element.
Selection Sort: Mechanism and Complexity
Selection sort[26] operates by dividing the array into a sorted prefix and an unsorted suffix, similar to insertion sort. However, the mechanism for growing the sorted prefix is entirely different. Instead of taking the first unsorted element and scanning backward to find its place, selection sort scans the entire unsorted suffix to find the absolute smallest element. Once the minimum element is found, it is swapped with the first element of the unsorted suffix. The boundary between the sorted and unsorted sections then moves one position to the right.
To illustrate, during the first pass, the algorithm examines all n elements to find the minimum value and swaps it into index 0. On the second pass, it examines the remaining n-1 elements to find the second-smallest value and swaps it into index 1. This process repeats until the entire array is sorted.
The time complexity of selection sort[27] is strictly O(n^2) in all cases (best, average, and worst).[41] Because the algorithm must scan the entire remaining unsorted section to guarantee it has found the minimum element, it cannot adapt to partially sorted data. The total number of comparisons is always (n^2 - n) / 2. The space complexity is O(1), as it operates in-place.
The defining mechanical advantage of selection sort is its memory write pattern. While it performs O(n^2) comparisons, it performs a maximum of exactly n-1 swaps. Every swap places an element into its final, permanent sorted position. This makes selection sort the optimal comparison-based algorithm in scenarios where read operations are cheap but write operations are exceptionally expensive.
Standard selection sort is not a stable sort. When the minimum element is swapped into its correct position, it can jump over elements with equal keys, destroying their original relative order. It is possible to implement a stable version of selection sort by shifting the intermediate elements rather than swapping, but this destroys the algorithm's primary advantage of minimizing memory writes.
Origins and History of Selection Sort
Like insertion sort, the conceptual roots of selection sort lie in the manual processing of data. In the era of electromechanical tabulating machines, operators would often sort a deck of punched cards by manually scanning the deck for the lowest value, pulling it out, and placing it in a new stack. This minimum-selection technique was translated directly into early computer programs.
The formalization of selection sort in computer science literature occurred in the 1950s alongside bubble sort. Edward H. Friend discussed the minimum-selection principle in his 1956 paper, and D. A. Bell further analyzed it in 1958. Because the algorithm is a direct translation of human manual sorting, it is not attributed to a single inventor. Instead, it emerged organically as one of the most obvious ways to instruct a machine to order data.[42]
In the 1960s, selection sort was heavily analyzed for its mathematical properties. Donald Knuth documented the exact probability distributions of the number of swaps required by selection sort.[40] Knuth proved that while the worst-case number of swaps is n-1, the average number of swaps in selection sort is n - H_n (or exactly n-1 if self-swaps are included), whereas the harmonic number H_n bounds the average number of times the minimum is updated during a single pass (the sum of the reciprocals 1 + 1/2 + 1/3 ... + 1/n). This mathematical property confirmed that selection sort was highly efficient for systems with slow magnetic tape drives, where writing data took significantly longer than reading it. Because the harmonic number grows logarithmically, the average number of swaps for large datasets is exceptionally small compared to the quadratic number of comparisons.
Variants of Selection Sort
The most important evolution of selection sort is heapsort[28], invented by J. W. J. Williams[29] in 1964. Williams recognized that the O(n^2) bottleneck in selection sort is the linear scan required to find the minimum element during every pass. To solve this, Williams introduced the binary heap data structure. Heapsort is fundamentally a selection sort, but instead of keeping the unsorted region as a flat array, it organizes the unsorted region into a max-heap. The heap data structure allows the algorithm to find the maximum element in O(1) time and extract it in O(log n) time. By replacing the linear scan with a logarithmic heap extraction, heapsort reduces the worst-case time complexity from O(n^2) to O(n log n), transforming the elementary selection sort into an optimal comparison-based sorting algorithm.
Another variant is bingo sort, designed for arrays containing a high number of duplicate values. In a standard selection sort, the algorithm scans for the minimum value and moves it to the front. Bingo sort modifies this by finding the minimum value, and then moving all instances of that value to the front in the same pass. If the array contains many duplicates, bingo sort significantly reduces the number of passes required, though its worst-case complexity remains O(n^2) for arrays with all unique elements.
Double selection sort is a minor optimization that finds both the minimum and maximum elements during a single pass through the unsorted region. The minimum is placed at the beginning of the unsorted section, and the maximum is placed at the end. This reduces the total number of passes by half, though it does not alter the asymptotic O(n^2) time complexity.
Real-World Applications and Tradeoffs
While the O(n^2) time complexity of bubble, insertion, and selection sort makes them unsuitable as general-purpose sorting algorithms for large datasets, their specific mechanical properties ensure they remain actively used in modern computing environments.
Insertion sort is the most practically useful of the three. Because it has extremely low overhead and runs in O(n) time on nearly sorted data, it is heavily utilized as a hybrid component in modern sorting libraries. The most famous example is Timsort[30], the default sorting algorithm in Python and Java. Timsort was developed by Tim Peters in 2002, drawing heavily on the theoretical frameworks established by Peter McIlroy in his 1993 paper Optimistic Sorting and Information Theoretic Complexity[31]. McIlroy demonstrated that algorithms could be optimized by detecting existing order within the data. Timsort divides large arrays into small chunks called runs. It uses insertion sort to sort these small runs (typically 32 to 64 elements in size) before merging them together, as detailed in the Python source code[32]. The algorithm dynamically adjusts the run size to ensure that the merges remain balanced. The V8 JavaScript engine[33] also historically used insertion sort for arrays smaller than 10 elements before transitioning to Timsort. Similarly, the C++ Standard Template Library implementation of std::sort typically uses Introsort[34], which relies on quicksort for large partitions but switches to insertion sort when the partition size falls below a specific threshold (usually 16 elements).
Selection sort has a highly specific niche in embedded systems and hardware engineering. In storage mediums like EEPROM[35] (Electrically Erasable Programmable Read-Only Memory) and flash memory[36], write operations are physically destructive. Each memory cell can only endure a limited number of write cycles before it degrades and fails. Write operations on these mediums consume significantly more time and electrical power than read operations. Because selection sort guarantees a maximum of exactly n-1 writes, it is sometimes preferred over O(n log n) algorithms like quicksort or mergesort, which perform O(n log n) writes and can prematurely wear out the memory cells on embedded microcontrollers. While cycle sort theoretically improves upon selection sort by guaranteeing exactly one write per item moved, cycle sort requires complex logic and additional overhead that is often impractical for simple microcontrollers, leaving selection sort as the standard choice for write-constrained environments.
Bubble sort is almost entirely absent from modern production software. Its high number of write operations and poor average-case performance make it inferior to insertion sort in every practical scenario. Its only lingering application outside of education is in highly specific computer graphics algorithms, such as polygon filling, where an array of coordinates is almost perfectly sorted and only requires one or two adjacent swaps to correct minor inversions. Even in these cases, insertion sort is generally equivalent or superior. Consequently, bubble sort remains primarily a subject of historical interest, serving as the baseline against which all other sorting algorithms are measured.[43]
Sources (43)
- Wikipedia: Sorting algorithms
- NIST
- Wikipedia: time complexity
- Wikipedia: Big O notation
- Wikipedia: divide-and-conquer algorithms
- Wikipedia: in-place
- Wikipedia: adaptive sorting
- Wikipedia: bubble sort
- Wikipedia: space complexity
- Wikipedia: stable sort
- ACM Digital Library
- Wikipedia: Kenneth E. Iverson
- Internet Archive
- Wikipedia: Donald Knuth
- ACM Digital Library
- Wikipedia: cocktail shaker sort
- Wikipedia: odd-even sort
- NIST
- Wikipedia: insertion sort
- IEEE
- Wikipedia: Moore School of Electrical Engineering
- Wikipedia: John Mauchly
- Wikipedia: John von Neumann
- Wikipedia: Shellsort
- ACM Digital Library
- NIST
- Wikipedia: selection sort
- Wikipedia: heapsort
- ACM Digital Library
- Wikipedia: Timsort
- ACM Digital Library
- Python source code
- Wikipedia: V8 JavaScript engine
- Wikipedia: Introsort
- Wikipedia: EEPROM
- Wikipedia: flash memory
- Apu Kumar Saha, Harish Sharma, Mukesh Prasad, Proceedings of International Conference on Intelligent Vision and Computing (ICIVC 2023) (Springer Nature, 2024)
- Derick Wood, Data Structures, Algorithms, and Performance (Addison-Wesley Longman, 1993)
- Xinyu Feng, Sungwoo Park, Programming Languages and Systems (Springer, 2015)
- Donald E. Knuth, The Art of Computer Programming (Addison-Wesley Professional, 1998)
- David Liben-Nowell, Connecting Discrete Mathematics and Computer Science (Cambridge University Press, 2022)
- Edwin D. Reilly, Concise Encyclopedia of Computer Science (John Wiley & Sons, 2004)
- Sushabhan Choudhury, Ranjan Mishra, Raj Gaurav Mishra, Intelligent Communication, Control and Devices (Springer Nature, 2019)