Sorting Algorithms in JavaScript

Sorting Algorithms in JavaScript

Commonly used sorting algorithms in Javascript

In this post, I want to cover commonly used sorting algorithms using JavaScript. These algorithms reduce the complexity of a problem. Sorting algorithms are used to organize an array of elements into a specific order.

For example:

                [5, 2, 9] ----- SORT -----> [2, 5, 9]

We’ll have a look at the following commonly used algorithms:

  • Bubble sort
  • Insertion sort
  • Merge sort
  • Quick sort

Then onwards we’ll look at the efficiency of these algorithms.

Bubble sort

It follows a recursion technique. We start with comparing two elements of an array, and swap them if required. We continue till the end of the array. We repeat this process until the array is sorted.

https://www.softwaretestinghelp.com/wp-content/qa/uploads/2019/06/Pass-1.png

Let’s have a look how we can implement this,

function bubbleSort(arr){
    for(let i = 0; i < arr.length; i++){
        for(let j = 0; j < arr.length - i - 1; j++){
            //Value comparison using ascending order
            if(arr[j + 1] < arr[j]){
                //Swapping
                [arr[j + 1],arr[j]] = [arr[j],arr[j + 1]]
            }
        }
    };
    return arr;
};

console.log(bubbleSort([5,3,8,4,6]));
// Output: [ 3, 4, 5, 6, 8 ]

Bubble sort has the following performance cases:

  • Worst-case time complexity: O( n^2 )
  • Average case time complexity: O(n)
  • Best case complexity: O(1)

Insertion sort

It also uses the recursion technique where one portion of the array is sorted and the other is unsorted. So we have to compare elements from the unsorted portions and insert them in sorted order.

  1. We assume the first element is sorted, so we start from the second element.
  2. When comparing the first and second elements, whichever is lowest, get inserted at the first position.
  3. Next, we do the same process for all elements of the array.

https://qawithexperts.com/Images/Upload/05-06-2018/Insertion-sort-example-min.png

Let’s look how we can implement it,

function insertionSort(arr){
    //Start from the second element.
    for(let i = 1; i < arr.length;i++){
        //Go through the elements behind it.
        for(let j = i - 1; j > -1; j--){
            //value comparison using ascending order.
            if(arr[j + 1] < arr[j]){
                //swap
                [arr[j+1],arr[j]] = [arr[j],arr[j + 1]];
            }
        }
    };

  return arr;
}

console.log(insertionSort([23, 1, 10, 5, 2]));
// Output: [ 1, 2, 5, 10, 23 ]

Insertion sort has the following performance cases:

  • Worst-case time complexity: O( n^2 )
  • Average case time complexity: O(n)
  • Space complexity: O(1)

Merge sort

Merge sort uses a divide and conquer algorithm. Unlike other sorting algorithms. Merge sort doesn’t care if the input array is sorted or not. It always performs the same operations regardless.

  1. We divide the original array into smaller arrays until each small array has one position/element left.
  2. These smaller arrays are then merged into a final sorted one.

https://www.gatevidyalay.com/wp-content/uploads/2020/07/Merge-Sort-Example-1.png

function merge(left, right) {
    let arr = []
    // Break out of loop if any one of the array gets empty
    while (left.length && right.length) {
        // Pick the smaller among the smallest element of left and right sub arrays 
        if (left[0] < right[0]) {
            arr.push(left.shift())  
        } else {
            arr.push(right.shift()) 
        }
    }

    // Concatenating the leftover elements
    // (in case we didn't go through the entire left or right array)
    return [ ...arr, ...left, ...right ]
}

function mergeSort(array) {
  const half = array.length / 2

  // Base case or terminating case
  if(array.length < 2){
    return array 
  }

  const left = array.splice(0, half)
  return merge(mergeSort(left),mergeSort(array))
}

In the snippet above, first, we take two sorted subarrays and merge them into a single sorted array. One thing to understand, if there are an odd number of elements, the left one gets a smaller number of elements. We divide until we are left with single-element arrays.

Merge sort has the following performance cases:

  • Worst-case time complexity: O(n log (n))
  • Average case time complexity: O(n log (n))
  • Space complexity: O(n)

Quick sort

This is one of the most used sorting algorithms. Similar to merge sort, we use the divide and conquer approach for this. It is considered the fastest method to sort. It can be implemented recursively or iteratively.

  1. Select the first element of the array, we call it pivot. It can be the first or last item of the array.
  2. Then we rearrange elements of an array in such a way elements to the left of the pivot are smaller than the pivot and righter elements are greater than it.
  3. Repeat this process individually for each side of the pivot.

https://stackabuse.s3.amazonaws.com/media/quicksort-in-javascript-1.jpg

Let’s look at the implementation in JavaScript

function partition(items, left, right) {
  //rem that left and right are pointers.
  let pivot = items[Math.floor((right + left) / 2)],
    i = left, //left pointer
    j = right; //right pointer
  while (i <= j) {
    //increment left pointer if the value is less than the pivot
    while (items[i] < pivot) {
      i++;
    }
    //decrement right pointer if the value is more than the pivot
    while (items[j] > pivot) {
      j--;
    }
    //else we swap.
    if (i <= j) {
      [items[i], items[j]] = [items[j], items[i]];
      i++;
      j--;
    }
  }
  //return the left pointer
  return i;
}

function quickSort(items, left, right) {
  let index;
  if (items.length > 1) {
    index = partition(items, left, right); //get the left pointer returned
    if (left < index - 1) {
      //more elements on the left side
      quickSort(items, left, index - 1);
    }
    if (index < right) {
      //more elements on the right side
      quickSort(items, index, right);
    }
  }
  return items; //return the sorted array
}
let items = [5, 3, 7, 6, 2, 9];

console.log(quickSort(items, 0, items.length - 1));
// Output: [ 2, 3, 5, 6, 7, 9 ]

Quick sort has following performance cases:

  • Worst-case time complexity: O( n^2 )
  • Average/Best case time complexity: O(n log (n))
  • Space complexity: O(log(n))

This algorithm could be used for sorting large arrays. Quick sort is often compared with Merge sort, both have average time complexity of O(n log (n)), this could be ignored if we take the last element as pivot. Merge Sort is quicker when dealing with linked lists.

Conclusion

We’ve covered commonly used algorithms for sorting. With this understanding, we can figure out an appropriate sorting algorithm to use depending on the dataset, time complexity, and space available.