Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 15

Sorting Algorithms: Selection, Bubble, and Insertion

Recommended resources

Hello! Welcome to the "Sorting & Searching Algorithms" module. In our previous module on arrays and hashing, we focused on using clever data structures like HashMap to achieve linear time complexity. We concluded by seeing how the prefix sum technique, combined with a hash map, could elegantly solve the "Subarray Sum Equals K" problem.

Today, we shift our focus to a new, fundamental topic: sorting. Sorting is the process of arranging items in a specific order, and it's one of the most studied problems in computer science. While modern programming languages provide highly optimized built-in sorting functions, understanding the underlying algorithms is crucial for any software engineer. It builds your problem-solving intuition and is a frequent topic in technical interviews.

In this lesson, we will implement three foundational sorting algorithms: Selection Sort, Bubble Sort, and Insertion Sort. These algorithms are considered "simple" sorts and generally have a time complexity of . While not the most efficient for large datasets, they are the essential first step to understanding how sorting works and provide the groundwork for the more advanced algorithms we'll cover later.

1. Selection Sort

The core idea of Selection Sort is straightforward and intuitive. For an array of n elements, you make n-1 passes. In each pass, you select the smallest remaining element from the unsorted part of the array and swap it with the element at the beginning of the unsorted part.

The process is as follows:

  1. Pass 1: Find the minimum element in the entire array (arr[0] to arr[n-1]) and swap it with the element at index 0. After this pass, the first element is sorted.
  2. Pass 2: Find the minimum element in the rest of the array (arr[1] to arr[n-1]) and swap it with the element at index 1. Now the first two elements are sorted.
  3. Continue: Repeat this process for n-1 passes. After the i-th pass, the first i elements of the array will be sorted.

Let's start by watching a detailed explanation and walkthrough of the algorithm.

Sorting - Part 1 | Selection Sort, Bubble Sort, Insertion Sort | Strivers A2Z DSA Course

The video from 'take U forward' provides an excellent visual and conceptual breakdown of Selection Sort. Striver explains the "select the minimum and swap" logic step by step.

Watch the segment explaining the core logic, where the algorithm is demonstrated on an example array. Then, follow along with the pseudocode derivation to understand how this logic translates into code.

The takeUforward article below provides a written version of this logic, including a clear dry run with diagrams that you can refer to.

Selection Sort Algorithm - Tutorial

This article breaks down the algorithm into clear steps and provides a visual dry run.

Focus on the Algorithm section and trace the visual dry run to solidify your understanding of how the sorted portion of the array grows with each pass.

Implementation in Java

Here is the Java implementation of Selection Sort. Notice the two nested loops. The outer loop (i) iterates through the passes, marking the boundary between the sorted and unsorted parts. The inner loop (j) iterates through the unsorted part to find the index of the minimum element.

public class SortingAlgorithms {
    public static void selectionSort(int[] arr) {
        int n = arr.length;

        // One by one, move the boundary of the unsorted subarray
        for (int i = 0; i < n - 1; i++) {
            // Find the minimum element in the unsorted array
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }

            // Swap the found minimum element with the first element
            // of the unsorted part.
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }
}

Complexity Analysis

  • Time Complexity:
    The algorithm consists of two nested loops. The outer loop runs n-1 times. The inner loop runs n-1 times in the first pass, n-2 in the second, and so on. The total number of comparisons is approximately , which is . This is true for the best, average, and worst cases because the algorithm always makes the same number of comparisons, regardless of the initial order of the elements.
  • Space Complexity:
    The sorting is done "in-place," meaning it does not require any significant extra memory besides a temporary variable for swapping.

2. Bubble Sort

Bubble Sort is another simple sorting algorithm, famous for its name. It works by repeatedly stepping through the list, comparing adjacent elements, and swapping them if they are in the wrong order. With each pass, the largest unsorted element "bubbles" up to its correct position at the end of the unsorted section.

The process is:

  1. Pass 1: Iterate from the first element to the second-to-last. Compare arr[j] with arr[j+1] and swap if arr[j] > arr[j+1]. After this pass, the largest element will be at the last position.
  2. Pass 2: Repeat the process, but this time, you only need to iterate up to the third-to-last element, as the last one is already in place. After this pass, the second-largest element is in its correct position.
  3. Continue: Repeat for n-1 passes.

The following image illustrates one pass of Bubble Sort.

A single pass of Bubble Sort. Adjacent elements are compared, and swapped if the left is greater than the right. This process effectively moves the largest element ('8' in this case) to the end of the array.

The take U forward video provides a great walkthrough of this process.

Sorting - Part 1 | Selection Sort, Bubble Sort, Insertion Sort | Strivers A2Z DSA Course

This part of the video explains how Bubble Sort pushes the maximum element to the end through adjacent swaps.

Watch the section from this timestamp. Pay close attention to how the range of the inner loop shrinks after each pass, as the largest elements get locked into place at the end of the array.

Implementation and Optimization

A key optimization for Bubble Sort is to recognize that if a full pass is completed with no swaps, the array must be sorted, and the algorithm can terminate early. This makes Bubble Sort efficient for arrays that are already nearly sorted.

The workat.tech article provides a good summary and code.

Sorting Algorithms (Bubble Sort, Insertion Sort, Selection ...

This article gives a concise explanation of Bubble Sort, including the optimized version.

Read the Bubble Sort section. Note the swapped boolean flag in the Java code, which implements the optimization we just discussed.

For a Java-specific coding demonstration, the following video is very helpful. It walks through building the code from scratch and addresses a common off-by-one error.

Bubble Sort Code in Java | DSA

This video from Telusko focuses specifically on implementing Bubble Sort in Java.

Watch the initial setup from the beginning. Follow the core logic implementation in the next segment. Pay attention to how a common ArrayIndexOutOfBoundsException is identified and fixed at this timestamp. Finally, observe the optimization that reduces unnecessary comparisons in later passes, explained from this point.

Complexity Analysis

  • Time Complexity:
    • Worst and Average Case: . This occurs when the array is in reverse order.
    • Best Case: . This occurs when the array is already sorted. The optimized version will make a single pass, find that no swaps are needed, and terminate.
  • Space Complexity: . It is an in-place sorting algorithm.

3. Insertion Sort

Insertion Sort builds the final sorted array one item at a time. It's much like how many people sort a hand of playing cards. You maintain a sorted sub-array at the beginning of the list. You then take the first element from the unsorted part and insert it into its correct position within the sorted part by shifting other elements to the right.

The process is:

  1. Start with the second element (i=1). Consider the first element (arr[0]) to be a sorted sub-array of size 1.
  2. Take the current element arr[i] (the "key") and compare it with elements in the sorted sub-array to its left (arr[j] where j < i).
  3. Shift all elements in the sorted sub-array that are greater than the key one position to the right.
  4. Insert the key into the newly created space.
  5. Repeat for all elements up to n-1.

This image provides a great visual for the step-by-step execution.

This visualization of Insertion Sort shows how an element (in red) is picked from the unsorted part and inserted into its correct position in the sorted part (in green) by shifting larger elements to the right.

Let's watch the video explanation to see it in action.

Sorting - Part 1 | Selection Sort, Bubble Sort, Insertion Sort | Strivers A2Z DSA Course

The final section of the video covers Insertion Sort, drawing the analogy to sorting cards.

Watch the explanation from this timestamp. Focus on the "take an element and place it in its correct position" mantra and how the inner loop works backward to find the correct insertion spot.

Implementation in Java

Here is a typical Java implementation. The outer loop picks the element to be inserted, and the inner while loop finds the correct position by shifting elements.

public static void insertionSort(int arr[]) {
    int n = arr.length;
    for (int i = 1; i < n; ++i) {
        int key = arr[i];
        int j = i - 1;

        /* Move elements of arr[0..i-1], that are
           greater than key, to one position ahead
           of their current position */
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}

Complexity Analysis

  • Time Complexity:
    • Worst and Average Case: . The worst case occurs when the array is sorted in reverse order.
    • Best Case: . The best case occurs when the array is already sorted. The inner while loop's condition will always be false, so it only performs n-1 comparisons.
  • Space Complexity: . It is an in-place sorting algorithm.

Insertion sort is particularly efficient for small datasets and for datasets that are already substantially sorted. For this reason, it's often used as a component in more sophisticated hybrid algorithms like Timsort (used in Java's Arrays.sort() for objects) and Introsort.


Summary of Simple Sorts

Here is a quick summary of the three algorithms we've covered today. While all have an average time complexity of , their best-case performance and behavior differ.

Algorithm Best Case Time Average Case Time Worst Case Time Space Complexity Stable? Notes
Selection Sort No Number of swaps is minimized ().
Bubble Sort Yes Can terminate early if array is sorted.
Insertion Sort Yes Efficient for small or nearly sorted data.

(We will cover the concept of "stability" in a future lesson, but it relates to how algorithms handle equal elements.)


Conclusion

In this lesson, you've learned to implement three fundamental sorting algorithms: Selection Sort, Bubble Sort, and Insertion Sort. You've seen how they work, analyzed their performance, and written their Java code.

Key Takeaways:

  • Selection Sort: Works by repeatedly selecting the minimum element. Its performance is consistent across all cases at .
  • Bubble Sort: Works by repeatedly swapping adjacent elements. It's notable for its ability to terminate early, giving it a best-case performance of .
  • Insertion Sort: Works by building a sorted sub-array. Like Bubble Sort, it has a best-case time of and is very effective on data that is already mostly sorted.

These algorithms provide a crucial foundation. However, for the large datasets you'd encounter in real-world applications or competitive programming, they are too slow.

In our next lesson, we will make a significant leap in efficiency by studying Merge Sort. It is a powerful algorithm that uses a "divide and conquer" strategy to achieve a much better time complexity of .