Sorting Problem: Bubble Sort
We will learn to write programs with bubble sorting strategies, and where we should apply them.
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:
#include<iostream>using namespace std;void swap(int & ra, int & rb){int t = ra; // saving ra in tra = 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 rbswap(c,d); // c will be received in ra and d will be received in rbcout << "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!
#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 spellingint t = ra; // other then all small lettersra = 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 rbcout << "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 rbcout << "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 rbcout <<"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:
- We compare the value at
A[i]
with its immediate next neighborA[i]>A[i+1]
. - If the first number is greater than the second number, we exchange their positions (by using the
swap()
function). - 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.
for(int i=0;i+1<size;// as i+1 will reach the end first hence we must put restriction of overflowi++){// Compare two neighbors and shifting the greater to the rightif(A[i]>A[i+1])swap(A[i],A[i+1]);}
Look at the above animation and think about the following questions.
- Where is the maximum element when this bubbling ends?
- Where is the 2nd maximum element?
- 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 neighborsA[i]>A[i+1]
, the value ati+1
will reach the boundary first; hence that should be checked for the boundary that isi+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:
- The maximum will be at the last index of the array.
- 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 the0
th location. Therefore no more bubbling is needed.
So we have the following implementation:
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 rightif(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: 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?
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:
-
In every iteration, we maintain a
bool
variablechangeHappen
that is initialized withfalse
before every bubbling iteration; -
If any change happens(swapping occurs) during the iteration, then the program sets that
changeHappen
withtrue
. This is the bubbling step. -
We repeat the above two steps until
changeHappen
isfalse
.
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.
void bubble_sortEfficient(int A[ ], int size){bool changeHappen;do{changeHappen = false; // changeHappen is reset to falsefor(int i=0;i+1<size;i++){if(A[i]>A[i+1]){swap(A[i],A[i+1]); // SWAPPING happenschangeHappen = 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
Quiz–Bubble sort
A deeper look into the implementation details of the bubble sort algorithm.
Why does calling bubble_up()
size-1
times suffice?
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]);
}
}
}