6 Sorting Algorithms Every Developer Learns — But Only 3 Really Stick

Bubble Sort, Selection Sort, Insertion Sort, Merge Sort, Quick Sort, and Heap Sort — what I actually remember after learning all of them.

Sorting Algorithms

When I first started learning DSA, sorting felt unnecessarily complicated.

Why do I need to learn Bubble Sort, Selection Sort, Insertion Sort, Merge Sort, Quick Sort, Heap Sort, and Counting sort.

Six different ways to solve what looks like one simple problem:

[5, 2, 8, 1, 3] => [1, 2, 3, 5, 8]

And honestly, when you’re writing JavaScript, you can just do: arr.sort((a, b) => a - b);

So why spend so much time learning sorting algorithms?

After years of coding, I’ve realized it’s not about memorizing six implementations. It’s about understanding how different algorithms approach the same problem.

And out of all six, only a few ideas really stick with me.

Let’s see which ones — and why.


First, what are we actually trying to do?

We have to sort the numbers: 

Given: [7, 2, 9, 4, 1]

We have to do: [1, 2, 4, 7, 9]

Its simple, How much work are we willing to do to get there? 

Since its 5 numbers, it doesn’t matter here but what if its 1,000,00 numbers or more?

Now the algorithms matter?


1. Bubble Sort

The udea behind bubble sort is very simple:

Compare two neighboring elements. If they’re in the wrong order, swap them.

Example: [5, 2, 8, 1]

Compare 5 and 2: 5 > 2 so swap: [2, 5, 8, 1]

Compare 5 and 8: 5 < 8 do nothing: [2, 5, 8, 1]

Compare 8 and 1: 8 > 1 swap: [2, 5, 1, 8]

Notice in 1 pass, The biggest element, 8, has slowly moved toward the end. So we can say 1 element is sorted in 1 pass so if we run n-1 pass we can move biggest number to the end in each pass.

That’s why it’s called Bubble Sort. The bigger elements keep “bubbling” toward the right.

Implementation:

function bubbleSort(arr) {
    for (let i = 0; i < arr.length; i++) {

        for (let j = 0; j < arr.length - i - 1; j++) {

            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] =
                [arr[j + 1], arr[j]];
            }
        }
    }

    return arr;
}

Complexity

Average: O(n²)
Worst:   O(n²)
Space:   O(1)

So would I use Bubble Sort in a real application? No, but its worth learning once, Because it teaches a very important idea:

Repeatedly fixing local problems can eventually produce a globally sorted result.


2. Selection Sort

Now let’s change the way we think. Instead of repeatedly swapping neighboring elements, we can say:

“I’ll find the smallest element and put it where it belongs.”

Consider: [5, 2, 8, 1, 3]

  • Find the smallest: 1

  • Put it at the beginning: [1, 2, 8, 5, 3]

  • Now look at the remaining elements: [2, 8, 5, 3]

  • The smallest is 2. It’s already in the right place.

Implementation

function selectionSort(arr) {
    for (let i = 0; i < arr.length; i++) {

        let minIndex = i;

        for (let j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }

        [arr[i], arr[minIndex]] =
        [arr[minIndex], arr[i]];
    }

    return arr;
}

Complexity

Time:  O(n²)
Space: O(1)

So we have two algorithms already. 

  • Both are relatively simple. 

  • Both are generally too slow for large inputs.

But they’re teaching us something.

Bubble Sort says: “Fix neighboring elements.”

Selection Sort says: “Find the element that belongs here.”

Next is the first algorithm that I think is actually worth remembering.


3. Insertion Sort

Insertion Sort is one of those algorithms that becomes very easy once you connect it to real life.

Imagine you’re holding a set of playing cards.

Example:

  • You get: 5

  • Then someone gives you: 3

  • You put 3 before 5.

  • Now you get: 8

  • You put it after 5. [3,5,8]

  • Then you get: 4

  • Where does 4 go? Between 3 and 5. 3 4 5 8

That’s basically Insertion Sort.

The important idea is: Keep one part of the array sorted and insert the next element into the correct position.

Here’s the code:

function insertionSort(arr) {

    for (let i = 1; i < arr.length; i++) {

        let current = arr[i];
        let j = i - 1;

        while (j >= 0 && arr[j] > current) {
            arr[j + 1] = arr[j];
            j--;
        }

        arr[j + 1] = current;
    }

    return arr;
}

Something interesting about Insertion Sort is that its worst-case complexity is still: O(n²)

So why remember it? Because it behaves really well when the data is already mostly sorted.

Ex- [1, 2, 3, 5, 4]

Only one element is out of place.

Insertion Sort doesn’t need to completely rethink the array. It can simply move 4 into its correct position.

This is an important lesson:

Big-O doesn’t tell the entire story about how an algorithm behaves on every input.


4. Merge Sort

Now we move into a completely different way of thinking.

Instead of trying to sort the entire array at once:

Break the problem into smaller problems.

Suppose we have:

[8, 3, 5, 4, 7, 6, 1, 2]

Split it:

[8, 3, 5, 4]    [7, 6, 1, 2]

Split again:

[8, 3] [5, 4]    [7, 6] [1, 2]

And again:

[8] [3] [5] [4] [7] [6] [1] [2]

Now we start merging them back in sorted order.

[3, 8] [4, 5]    [6, 7] [1, 2]

Then:

[3, 4, 5, 8]    [1, 2, 6, 7]

Finally:

[1, 2, 3, 4, 5, 6, 7, 8]

The pattern is:

           [8 3 5 4 7 6 1 2]
                 |
              Divide
                 ↓
       [8 3 5 4]   [7 6 1 2]
          |             |
       Divide        Divide
          ↓             ↓
      smaller       smaller
       arrays        arrays
          \             /
           \           /
              Merge
                ↓
        [1 2 3 4 5 6 7 8]

That’s the big idea behind Divide and Conquer.

The complexity is:

Time:  O(n log n)
Space: O(n)

And this is one of the first sorting algorithms where you can really see the power of breaking a big problem into smaller ones.


5. Quick Sort

Quick Sort is another algorithm based on divide and conquer.

But instead of splitting the array into equal halves, we choose something called a pivot.

For example:

[7, 2, 1, 6, 8, 5, 3, 4]

Suppose we choose:

pivot = 4

Now we try to put elements into two groups:

smaller than 4    |    greater than 4
-------------------------------------
2, 1, 3           |    7, 6, 8, 5

Now 4 is conceptually in its correct position.

Then we repeat the same process for the left and right sides.

[2, 1, 3]   4   [7, 6, 8, 5]

And eventually everything becomes sorted.

The average complexity is:

O(n log n)

But there is an important catch.

In the worst case:

O(n²)

For example, if we consistently choose a terrible pivot.

That’s one reason why implementation details and pivot selection can matter.

And this is another lesson I took from sorting:

An algorithm can have a great average case without having a great worst case.


6. Heap Sort

The last one is Heap Sort.

This one can feel confusing at first because it introduces another data structure:

Heap.

Instead of directly thinking:

“How do I move these numbers around?”

we think:

“Can I build a structure that lets me repeatedly find the largest or smallest element efficiently?”

For example, in a Max Heap, the largest element stays at the top.

So we can repeatedly:

  1. Take the largest element.

  2. Put it at the end.

  3. Fix the heap.

  4. Repeat.

Its complexity is:

Time:  O(n log n)
Space: O(1)

Heap Sort is definitely worth understanding if you’re learning heaps and DSA.

But personally, I don’t find myself remembering the complete implementation as strongly as Merge Sort or Quick Sort.

And that’s actually okay.

Not every algorithm needs to occupy the same amount of space in your brain.


So what actually sticks?

After going through all six, here’s how I mentally group them.

                   SORTING
                       |
          ┌────────────┴────────────┐
          |                         |
       Simple                    Efficient
          |                         |
   ┌──────┼──────┐             ┌────┼────┐
   |      |      |             |    |    |
 Bubble Selection Insertion   Merge Quick Heap
   |      |        |            |     |    |
  O(n²)  O(n²)    O(n²)       O(nlogn)...

But I don’t think the goal should be:

“I need to memorize six pieces of code.”

I’d rather remember the mental model behind each one.

Algorithm and Mental Model

That’s much easier to recall during an interview.


One JavaScript surprise you should know

Here’s something that has caught many beginners.

What do you expect this to return?

[10, 2, 5, 1].sort();

Maybe:

[1, 2, 5, 10]

But that’s not how JavaScript’s default sort() comparison works.

Without a comparator, values are compared as strings.

So you can get:

[1, 10, 2, 5]

Because, as strings:

"10" comes before "2"

For numbers, you normally want:

[10, 2, 5, 1].sort((a, b) => a - b);

Now:

[1, 2, 5, 10]

That tiny comparator:

(a, b) => a - b

is something every JavaScript developer should know.


But what happens in real projects?

Here’s where DSA and real-world development start to feel different.

In most applications, I’m not sitting there thinking:

“Hmm… should I implement Quick Sort today?”

Usually, I’ll use the language’s built-in sorting functionality.

For example:

users.sort((a, b) => a.age - b.age);

The runtime handles the actual sorting algorithm.

And that’s completely fine.

Knowing algorithms doesn’t mean you have to implement them every time.

It’s similar to knowing how a database index works.

You don’t manually build a B-tree every time you query a database.

But understanding what’s happening underneath helps you make better decisions.


One more thing: O(n log n) doesn’t mean “the same”

Here’s a question I wish more people asked:

If Merge Sort and Quick Sort are both O(n log n), are they basically the same?

No.

Big-O describes how the algorithm grows as input size increases.

It doesn’t tell you everything.

There can be differences in:

  • Memory usage

  • Cache behavior

  • Constant factors

  • Worst-case behavior

  • Stability

  • Whether the algorithm works in-place

  • Characteristics of the input

So when comparing algorithms, don’t stop at:

O(n log n)

Ask:

What else does my problem require?

That’s a much more useful question.


What about Stability?

Here’s another concept that becomes much easier with an example.

Imagine we have:

A → Sneha, age 25
B → Rahul, age 30
C → Priya, age 25

Now we sort by age.

If the sorting algorithm is stable, people with the same age keep their original relative order.

So:

Sneha 25
Priya 25
Rahul 30

Sneha was before Priya originally, and she remains before Priya.

Why does this matter?

Because in real applications, you often sort data multiple times.

For example:

  1. Sort employees by name.

  2. Then sort by department.

Whether the previous ordering is preserved for equal department values can matter.

It’s a small concept, but once you understand it, you’ll start noticing it in real systems.


The 6 algorithms in one table

Here’s the cheat sheet I’d keep around while learning:

The 6 algorithms in one table

* depends on the implementation and input conditions.

And if you’re preparing for interviews, I wouldn’t try to memorize this table blindly.

Understand why the numbers are what they are.


If I had to learn sorting again

I wouldn’t start by memorizing six implementations.

I’d learn them in this order:

Step 1 — Understand the simple ones

Learn:

Bubble
Selection

Not because you’ll use them every day.

But because they’re easy ways to understand swapping, comparisons, and iteration.

Step 2 — Really understand Insertion Sort

Because it gives you a useful mental model:

“I already have a sorted section. Where does this new element belong?”

Step 3 — Learn Divide and Conquer

That’s where:

Merge Sort
Quick Sort

become much more interesting.

Don’t just memorize their code.

Understand:

Break the problem
↓
Solve smaller problems
↓
Combine the result

Step 4 — Learn Heap Sort when you’re learning Heaps

This makes the algorithm much easier to understand because you already know the data structure behind it.


The biggest thing sorting taught me

When I first learned sorting algorithms, I thought the important thing was remembering the implementation.

Something like:

“What was the exact code for Quick Sort again?”

Now I think the more valuable skill is recognizing the pattern.

When I see a problem, I want to ask:

What does my input look like?
        ↓
How large is it?
        ↓
Is it already partially sorted?
        ↓
Do I have memory constraints?
        ↓
Do I need stable sorting?
        ↓
What kind of time complexity can I afford?
        ↓
Which approach fits?

That’s a much more useful way to think.

Because algorithms aren’t really about memorizing code.

They’re about recognizing:

“I’ve seen a problem like this before. I know the kind of approach that might work.”


So, do you really need to know all 6?

If you’re learning DSA or preparing for interviews:

Yes, understand all six.

But don’t give all six equal importance.

I’d personally make these three stick:

Insertion Sort → because of its simple and useful mental model.

Merge Sort → because it teaches Divide and Conquer extremely well.

Quick Sort → because partitioning and pivot-based thinking show up in many places.

Then understand Bubble, Selection, and Heap Sort well enough that you can explain how they work and their trade-offs.

And once you’ve understood them, don’t feel guilty about using:

array.sort()

in your actual application.

That’s not cheating.

That’s software engineering.


Final thought

The funny thing about learning algorithms is that you often forget the code.

  • You forget the exact loop.

  • You forget which variable was called pivot.

  • You forget whether you used i or j.

But if you really understood the idea, you can usually rebuild it.

That’s probably the biggest lesson I took from sorting.

Don’t try to remember every line.

Remember the question each algorithm is trying to answer.

Bubble Sort asks:

“Are these two neighbors in the right order?”

Insertion Sort asks:

“Where should this element go in my sorted section?”

Merge Sort asks:

“Can I break this big problem into smaller ones?”

Quick Sort asks:

“Can I put one element in its correct position and solve the two sides separately?”

Heap Sort asks:

“Can I efficiently keep track of the next largest or smallest element?”

Once you start thinking this way, sorting algorithms stop looking like six random DSA problems.

They start looking like six different ways of thinking about the same problem.