Skip to main content

Welcome to a Grasp lesson

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

Lesson 42

Introduction to Bubble Sort and Insertion Sort

Recommended resources

Hello! Welcome to your next lesson in our module on "Algorithms: Sorting, Searching, and Design."

Introduction

In our previous lesson, we explored linear and binary search algorithms. A key takeaway was that binary search is incredibly fast, but it only works on sorted data. This raises a crucial question: how do we get data sorted in the first place?

Today, we begin to answer that question by diving into the world of sorting algorithms. Sorting is a fundamental operation in computer science, and understanding how different sorting methods work is essential for your GATE preparation.

Your learning outcome for this lesson is to: Implement and analyze simple sorting algorithms: bubble sort and insertion sort.

We will cover:

  • The logic, implementation, and analysis of Bubble Sort.
  • The logic, implementation, and analysis of Insertion Sort.
  • How to analyze their efficiency (time and space complexity) using Big-O notation for best, average, and worst-case scenarios.
  • A comparison of their performance to understand their practical trade-offs.

Let's get started with one of the most well-known sorting algorithms.


1. Bubble Sort: The "Sinking" Stone Approach

Bubble sort is a simple sorting algorithm that works by repeatedly stepping through the list, comparing each pair of adjacent items, and swapping them if they are in the wrong order. With each pass through the list, the next largest element "bubbles up" (or sinks) to its correct position at the end of the unsorted portion.

Imagine a series of stones of different weights in water; the heaviest ones will sink to the bottom first. Bubble sort works in a similar way.

Bubble Sort Algorithm Step-by-Step Illustration
This image illustrates the Bubble Sort process. In each pass, the largest remaining element is compared with its neighbors and moved to the right, eventually "sinking" to its final sorted position.

To see the mechanics of this process in detail, let's watch a step-by-step walkthrough.

L-3.6: How Bubble Sort Works | Performance of Bubble Sort | All Imp Points with Example | Algorithm

This video from Gate Smashers provides a methodical demonstration of how Bubble Sort works. Pay close attention to the concept of 'passes' and how the largest unsorted element finds its final position in each pass.

Watch from 00:21 to 04:52. This section explains the core algorithm, showing how elements are compared and swapped to sort an array. Follow the state of the array after each pass.

Python Implementation

Now that you've seen how it works conceptually, let's implement it in Python. A common implementation uses two nested loops: the outer loop controls the number of passes, and the inner loop performs the adjacent comparisons and swaps.

For a comprehensive look at the code and its explanation, we'll turn to a detailed article.

Sorting Algorithms in Python

The article 'Sorting Algorithms in Python' from Real Python provides a clean, well-commented implementation of Bubble Sort. It also includes a useful optimization.

Read the section titled 'The Bubble Sort Algorithm in Python'. Focus on the subsection 'Implementing Bubble Sort in Python' to understand the code. Note the use of the already_sorted flag, which allows the algorithm to terminate early if the list becomes sorted before all passes are complete.

Here is the code from the article for your reference:

def bubble_sort(array):
    n = len(array)

    for i in range(n):
        # Create a flag that will allow the function to terminate early
        already_sorted = True

        # The inner loop performs the comparisons. The range shrinks
        # with each pass because the largest elements are already sorted.
        for j in range(n - i - 1):
            if array[j] > array[j + 1]:
                # Swap the elements
                array[j], array[j + 1] = array[j + 1], array[j]

                # If a swap occurred, the list is not yet sorted
                already_sorted = False

        # If no swaps occurred in a pass, the array is sorted
        if already_sorted:
            break

    return array

# --- Example Usage ---
my_list = [10, 9, 11, 6, 15, 2]
print(f"Original list: {my_list}")

sorted_list = bubble_sort(my_list)
print(f"Sorted list: {sorted_list}")

Analysis of Bubble Sort

Analyzing an algorithm's efficiency is a critical skill for the GATE exam. We need to understand its time and space complexity.

  • Time Complexity:
    The two nested loops are the key. The algorithm performs approximately comparisons, which, in Big-O notation, simplifies to .

    • Worst Case: . This occurs when the list is in reverse order.
    • Average Case: . For a randomly ordered list.
    • Best Case: . This occurs when the list is already sorted, and we use the already_sorted flag optimization. The algorithm makes a single pass through the list to confirm it's sorted and then terminates.
  • Space Complexity:
    Bubble sort only requires a few variables to keep track of indices and a temporary variable for swapping (though Python's tuple-swapping a, b = b, a hides this). It does not create any new data structures proportional to the input size. This makes its space complexity , or constant space. It's an in-place sorting algorithm.

For a more rigorous mathematical derivation of the time complexity, which is highly relevant for your engineering background and GATE prep, please watch the next segment of the Gate Smashers video.

L-3.6: How Bubble Sort Works | Performance of Bubble Sort | All Imp Points with Example | Algorithm

This segment provides a mathematical breakdown of why Bubble Sort's worst-case time complexity is O(n^2). Understanding this derivation is important.

Watch from 04:52 to 08:20. Follow the logic of counting the comparisons in each pass to arrive at the final complexity.

Test your understanding!

Given the list [7, 3, 2, 5], what is the state of the list after the first two passes of the outer loop (i=0 and i=1) in the bubble_sort function?

Show answer

Initial List: [7, 3, 2, 5]

Pass 1 (i=0):

  • Compare 7 and 3. Swap. List becomes [3, 7, 2, 5].
  • Compare 7 and 2. Swap. List becomes [3, 2, 7, 5].
  • Compare 7 and 5. Swap. List becomes [3, 2, 5, 7].
  • The largest element, 7, is now at the end.

After Pass 1: [3, 2, 5, 7]

Pass 2 (i=1):

  • Compare 3 and 2. Swap. List becomes [2, 3, 5, 7].
  • Compare 3 and 5. No swap. List is [2, 3, 5, 7].
  • The inner loop stops here (range is n-i-1, so 4-1-1=2 iterations for j). The second largest element, 5, is now in its correct place.

After Pass 2: [2, 3, 5, 7]
(Note: The list is already sorted, but the algorithm doesn't know this until it completes another pass without any swaps).


2. Insertion Sort: Sorting Cards in Your Hand

Insertion sort is another simple algorithm that builds the final sorted list one item at a time. It's similar to how many people sort a hand of playing cards. You take one card at a time and insert it into its correct position within the cards you've already sorted.

The algorithm maintains a sorted sublist at the beginning of the array. It takes the first element from the unsorted part and "inserts" it into the correct position in the sorted sublist by shifting larger elements one position to the right.

Sorting: Bubble, Insertion, Selection, Merge, Quick, Counting Sort - DSA Course in Python Lecture 10

This video by Greg Hogg gives a quick and clear animation of how Insertion Sort works. It's a great way to visualize the process of building the sorted sub-array.

Watch from 04:23 to 06:59. Observe how the algorithm iterates through the list, picking a 'key' element and shifting other elements to make space for it in the sorted portion.

Python Implementation and Analysis

Like Bubble Sort, Insertion Sort is straightforward to implement and has a quadratic time complexity in the average case. However, as we'll see, it has some performance advantages.

Sorting Algorithms in Python

Let's return to the Real Python article to study the implementation and analysis of Insertion Sort.

Read the section 'The Insertion Sort Algorithm in Python'. Pay attention to: The Python implementation and how the while loop is used to shift elements. The Big O runtime complexity analysis for best, worst, and average cases. The comparison with Bubble Sort and the practical timing results shown in the article.

Here is the code from the article for your reference:

def insertion_sort(array):
    # Loop from the second element (index 1) to the end
    for i in range(1, len(array)):
        # The element we want to position correctly
        key_item = array[i]
        
        # Initialize j to the index before the key_item
        j = i - 1

        # Run through the sorted portion (left side) and shift
        # all elements greater than key_item to the right.
        while j >= 0 and array[j] > key_item:
            array[j + 1] = array[j]
            j -= 1
        
        # Insert the key_item in its correct location
        array[j + 1] = key_item
        
    return array

# --- Example Usage ---
my_list = [10, 9, 11, 6, 15, 2]
print(f"Original list: {my_list}")

sorted_list = insertion_sort(my_list)
print(f"Sorted list: {sorted_list}")

Analysis of Insertion Sort

  • Time Complexity:

    • Worst Case: . Occurs when the array is sorted in reverse order. The inner while loop has to shift every element in the sorted sublist for each new key.
    • Average Case: . For a random list.
    • Best Case: . Occurs when the array is already sorted. The inner while loop condition (array[j] > key_item) is immediately false for every element, so only the outer loop runs.
  • Space Complexity:
    Like bubble sort, insertion sort is an in-place algorithm that requires only a constant amount of extra memory. Its space complexity is .

Bubble Sort vs. Insertion Sort

Even though both algorithms have an average time complexity of , insertion sort generally performs better in practice. Why?

  1. Fewer Swaps: Bubble sort performs a swap every time two adjacent elements are out of order. Insertion sort only shifts elements and performs a single insertion for each key item.
  2. Adaptive Nature: Insertion sort is highly efficient for data that is already "mostly sorted." If the list is nearly in order, the inner loop rarely runs, and the performance approaches .

The timing experiments in the Real Python article you read clearly demonstrate this practical performance difference.


Conclusion

Congratulations! You have now implemented and analyzed two of the most fundamental sorting algorithms. While they may not be the most efficient for large datasets, they provide an essential foundation for understanding more complex algorithms.

Key Takeaways:

  • Bubble Sort works by repeatedly swapping adjacent elements. It's simple to understand but generally inefficient.
    • Time Complexity: on average.
    • Space Complexity: .
  • Insertion Sort works by building a sorted sublist and inserting elements into it. It's also simple and is generally more efficient than bubble sort in practice.
    • Time Complexity: on average, but in the best case, making it adaptive.
    • Space Complexity: .
  • Analyzing algorithms in terms of their best, average, and worst-case scenarios is crucial for making informed decisions.

Preview of the Next Lesson:

The complexity of bubble and insertion sort makes them impractical for sorting large lists. For your GATE exam and real-world applications, you'll need faster methods. In the next lesson, we will explore advanced sorting algorithms that use a "divide and conquer" strategy to achieve a much better time complexity. You will implement and analyze merge sort and quick sort, two of the most important and efficient sorting algorithms in computer science.