We saw in the last lesson the amazing applications of searching on sorted data. We realized how important structured/sorted data is such that we’re able to apply binary searching on it.

In the upcoming lessons, we will learn three strategies for sorting and their nitty gritty details.

Before we start, let’s review some important elements related to the fundamentals of arrays and their indexing,


Refresher: arrays and functions

Let’s have a refresher on arrays and how the values are passed by reference in functions.

Swap function and playing with arrays’ elements

Here is our standard swap() function:

Press + to interact
#include<iostream>
using namespace std;
void swap(int & ra, int & rb)
{
int t = ra; // saving ra in t
ra = rb; // saving rb in ra (ra's value is replaced, but fortunately we have it in t)
rb = t; // saving t in rb, which is the previous value of ra
}
int main()
{
int a = 10, b = 20, c = 30, d = 40;
cout << "Before Swapping: a="<<a<<" , b = "<<b<<endl;
cout << "Before Swapping: c="<<c<<" , d = "<<d<<endl;
swap(a,b); // a will be received in ra and b will be received in rb
swap(c,d); // c will be received in ra and d will be received in rb
cout << "After Swapping: a="<<a<<" , b = "<<b<<endl;
cout << "After Swapping: c="<<c<<" , d = "<<d<<endl;
swap(a,b);
swap(c,d);
cout << "After 2nd Swapp: a="<<a<<" , b = "<<b<<endl;
cout << "After 2nd Swapp: c="<<c<<" , d = "<<d<<endl;
return 0;
}

Let’s see how the same swap() function will work on elements of arrays now!

Press + to interact
#include <iostream>
using namespace std;
void swap(int & ra, int & rb) // SWAP is capitalized as swap is also a built in
{ // function in std namespace. So pick any spelling
int t = ra; // other then all small letters
ra = rb;
rb = t;
}
void printArray(const char Name[ ], int A[ ], int size)
{
cout << Name << " = { ";
for(int i=0; i<size; i++)
cout << A[i] << " ";
cout << "}"<<endl;
}
int main()
{
int A[5] = {1,2,3,4,5}, size=5;
cout << "Array Before Swapping A[0] and A[4]"<<endl;
printArray("A", A, size);
swap(A[0], A[4]); // A[0] will be received in ra and A[4] in rb
cout << "Array After Swapping A[0] and A[4]"<<endl;
printArray("A", A, size);
swap(A[1], A[3]);//Now A[1] will be received in ra and A[3] in rb
cout << "Array After Swapping A[1] and A[3]"<<endl;
printArray("A", A, size);
int i=3, j=0;
swap(A[i], A[j]);//Now A[1] will be received in ra and A[3] in rb
cout <<"i="<<i<<" "<<"j="<<j<<endl;
cout << "Array After Swapping A["<<i<<"] and A["<<j<<"]"<<endl;
printArray("A", A, size);
return 0;
}

Notice that when we pass any element of an array A[i] as a reference, an element is received while the subsequent processing on that reference changes the element that was passed as an argument.


Let’s learn our first idea of sorting, which is inspired by the bubbling process of boiling a liquid.

Sorting algorithms

Let’s look at the important aspects of the bubble sort algorithm.

Bubble sort

We know that when we boil a liquid, the molecules with higher kinetic energy go up to form bubbles. These bubbles with the highest kinetic energy then rise and escape to form water vapor. This is the inspiration for bubble sorting, whereby the elements that meet a certain condition after comparison are swapped (rise).

Discovering the famous nested loops implementation

Sample program:

       A = {6 7 8 4 3 2 1}
Sorted A = {1 2 3 4 5 6 7}

Before sorting in ascending order, let’s first understand the central strategy of bubbling behind this famous sort algorithm.

The bubbling strategy

Here is the bubbling strategy:

First, we take the start position i = 0. Then:

  1. We compare the value at A[i] with its immediate next neighbor A[i]>A[i+1].
  2. If the first number is greater than the second number, we exchange their positions (by using the swap() function).
  3. We repeat the above two steps for the next two neighbors i = i+1, until we reach the last possible pair.

Look at the code and animation below. Look how it is working.


Press + to interact
for(int i=0;
i+1<size;// as i+1 will reach the end first hence we must put restriction of overflow
i++)
{
// Compare two neighbors and shifting the greater to the right
if(A[i]>A[i+1])
swap(A[i],A[i+1]);
}

Look at the above animation and think about the following questions.

  1. Where is the maximum element when this bubbling ends?
  2. Where is the 2nd maximum element?
  3. Are we done with sorting?

As you must have noticed, the above code shifts the maximum value to the rightmost position. Secondly, notice that the 2nd maximum has been knocked off by the maximum during the bubbling effect!

Of course, we are not done with sorting yet, but we have achieved one crucial step of sorting.

We have devised a bubbling strategy of making the higher values bubble up, i.e., we’ve made the higher values shift towards the right end and knock off the smaller values (in the case of descending order, the greater values will be knocked off).

Question: What will happen if we repeat this bubbling/knocking iteration two times?

Let’s look at the code below:

for(int t=1; t<=2; t++) // Repeat 2 times
{
   for(int i=0;i+1<size;i++)           
   {            // Notice i+1<size
       // Comparing two neighbors and shifting the greater to the right
       if(A[i]>A[i+1])
          swap(A[i],A[i+1]);
    }
} 

We have put the condition of i+1<size. This is because in the condition of comparing two neighbors A[i]>A[i+1], the value at i+1 will reach the boundary first; hence that should be checked for the boundary that is i+1<size.

Look at the animation below for two iterations in bubble sort:

When we repeat the knocking/bubbling procedure two times, two things will certainly happen:

  1. The maximum will be at the last index of the array.
  2. The second maximum will be at the second last index of the array.

That gives us the perfect direction toward bubble sort.

Implementation of bubble sort

What if we want to sort the complete array? How can we achieve that?

  • Repeat the bubbling step size-1 times.

    Because the top size-1 elements are moved to the right, the last one (the minimum) is already at the 0th location. Therefore no more bubbling is needed.

So we have the following implementation:

Press + to interact
void bubble_sortV1(int A[ ], int size)
{
for(int t=1;t<size;t++)
{
for(int i=0;i+1<size;i++) // i+1 < size will bound this array
{ // comparing two neighbors and shifting the greater to the right
if(A[i]>A[i+1])
swap(A[i],A[i+1]);
}
}
}

An efficient implementation of bubble sort

Before discussing the efficient implementation of bubble sort, look at the following question.

Understanding the bubbling effect

Question

Question: What if the data is already sorted? Or what if it becomes sorted during the knocking/bubbling process? What will happen in the bubble_up() step? Will any change (swap) in the array happen?

Show Answer

If data is already sorted or becomes sorted during the bubbling/knocking, is there any way to make bubble sort stop early?

Yes, we can do it! Let’s see what we can do.

Improvement idea:

  1. In every iteration, we maintain a bool variable changeHappen that is initialized with false before every bubbling iteration;

  2. If any change happens(swapping occurs) during the iteration, then the program sets that changeHappen with true. This is the bubbling step.

  3. We repeat the above two steps until changeHappen is false.

If the iteration ends and no change (swapping) happens, it means the array is already sorted.

Let’s update our implementation with the above idea.

Press + to interact
void bubble_sortEfficient(int A[ ], int size)
{
bool changeHappen;
do
{
changeHappen = false; // changeHappen is reset to false
for(int i=0;i+1<size;i++)
{
if(A[i]>A[i+1])
{
swap(A[i],A[i+1]); // SWAPPING happens
changeHappen = true; // hence changeHappen is set
} // to repeat the bubbling loop
}
}
while(changeHappen); // if changeHappens, the bubbling should repeat
}

Here is the final implementation you can experiment with in the following playground!

6 7 8 4 3 2 1
Bubble sort efficient Implementation (using functions)

Quiz–Bubble sort

A deeper look into the implementation details of the bubble sort algorithm.

Question 1

Why does calling bubble_up() size-1 times suffice?

Show Answer
Question 2

Is there any logical error in the following code?

void bubble_sortV1(int A[], int size)
{
    for(int t=1;t<size;t++).   // repeat size-1 times
    {
        for(int i=0;
                i<size;    // HINT: Look here 
                   i++)
        {                                 
            if(A[i]>A[i+1])                 
                Swap(A[i],A[i+1]);          
        }                           
    }
}

Show Answer