Computer >> Computer tutorials >  >> Programming >> Javascript

Merge sort vs quick sort in Javascript


Merge sort is a sorting technique based on divide and conquer technique. It has a worst-case time complexity of Ο(n log n). But this comes with an added cost in terms of space as this algorithm takes an extra O(n) memory.

Now let's look at how we're going to implement this algorithm. We'll create 2 function, mergeSort and merge.

merge − this function takes 2 arguments, these are 2 partial arrays which this then concatenates into one by inserting elements in the correct order.

mergeSort − This function recursively calls mergeSort on left and right halves of the array and then uses merge to combine these array parts together. This will be much more clear once we look at the implementation.

Lets start with the merge function and dive straight away in the code −

Example

function merge(left, right) {
   let mergedArr = [];
   let i = 0;
   let j = 0;
   // Try merging till we reach end of either one of the arrays.
   while (i < left.length && j < right.length) {
      if (compare(left[i], right[j])) {
         mergedArr.push(left[i]);
         i++;
      } else {
         mergedArr.push(right[j]);
         j++;
      }
   }
   // If left was 1, 2, 3, 5 and right was 4, 6, 7, 9
   // the for loop above would stop once it reaches the end of
   // The left array leaving 3 elements in the right array
   // In order to add the remaining elements from either array,
   // We need to add elements from i to end in left and j to end
   // in the right array to the merged array.
   return mergedArr.concat(left.slice(i)).concat(right.slice(j));
}

This function will take 2 sorted arrays and merge them in O(n) time such that they remain sorted as the bigger array. Refer to the code comments for a in depth explaination of the method.You can test this using −

Example

let a1 = [1, 2, 3, 5]
let a2 = [4, 6, 8, 9]
console.log(merge(a1, a2))

Output

[1, 2, 3, 4, 5, 8, 9]

Now we'll use this function in our mergesort function to actually sort the whole array.

We'll create a inner function for mergeSort which we'll wrap with an outer function to make our comparator available so that our function is extensible. Let's have a look at this inner function −

Example

function mergeSortInner(arr) {
   if (arr.length < 2) {
      return arr;
   }
   let mid = Math.floor(arr.length / 2);
   // Create an array from 0 to mid - 1 elements
   let left = arr.slice(0, mid);
   // Create an array from mid to the last element
   let right = arr.slice(mid);
   // Sort the left half, sort the right half,
   // merge the sorted arrays and return
   return merge(mergeSortInner(left), mergeSortInner(right));
}

This function takes an array breaks it in two, sorts them individually and then returns a merged array back.

Let us look at the complete code with a test −

Example

function mergeSort(arr, compare = (a, b) => a < b) {
   function merge(left, right) {
      let mergedArr = [];
      let i = 0;
      let j = 0;
      // Try merging till we reach end of either one of the arrays.
      while (i < left.length && j < right.length) {
         if (compare(left[i], right[j])) {
            mergedArr.push(left[i]);
            i++;
         } else {
            mergedArr.push(right[j]);
            j++;
         }
      }
      // If left was 1, 2, 3, 5 and right was 4, 6, 7, 9
      // the for loop above would stop once it reaches the end of
      // The left array leaving 3 elements in the right array
      // In order to add the remaining elements from either array,
      // We need to add elements from i to end in left and j to end
      // in the right array to the merged array.
      return mergedArr.concat(left.slice(i)).concat(right.slice(j));
   }
   function mergeSortInner(arr) {
      if (arr.length < 2) {
         return arr;
      }
      let mid = Math.floor(arr.length / 2);
      let left = arr.slice(0, mid);
      let right = arr.slice(mid);
      return merge(mergeSortInner(left), mergeSortInner(right));
   }
   // Call inner mergesort to sort and return the array for us.
   return mergeSortInner(arr);
}
You can test this using:
let arr = [5, 8, 9, 12, -8, 31, 2];
// Sort Array elements in increasing order
arr = mergeSort(arr);
console.log(arr);
// Sort Array elements in decreasing order
arr = mergeSort(arr, (a, b) => a > b);
console.log(arr);
arr = [
   {
      name: "Harry",
      age: 20
   },
   {
      name: "Jacob",
      age: 25
   },
   {
      name: "Mary",
      age: 12
   }
];
// Sort Array elements in increasing order alphabetically by names
arr = mergeSort(arr, (a, b) => a.name < b.name);
console.log(arr);
// Sort Array elements in decreasing order of ages
arr = mergeSort(arr, (a, b) => a.age < b.age);
console.log(arr);

Output

[ -8, 2, 5, 8, 9, 12, 31 ]
[ 31, 12, 9, 8, 5, 2, -8 ]
[
   { name: 'Harry', age: 20 },
   { name: 'Jacob', age: 25 },
   { name: 'Mary', age: 12 }
]
[
   { name: 'Mary', age: 12 },
   { name: 'Harry', age: 20 },
   { name: 'Jacob', age: 25 }
]

Quick Sort

Quick sort is a highly efficient sorting algorithm and is based on partitioning of array of data into smaller arrays. A large array is partitioned into two arrays one of which holds values smaller than the specified value, say pivot, based on which the partition is made and another array holds values greater than the pivot value.

Quick sort partitions an array and then calls itself recursively twice to sort the two resulting subarrays. This algorithm is quite efficient for large-sized data sets as its average and worst case complexity are of Ο(n2), where n is the number of items.

Partition Process

Following animated representation explains how to find the pivot value in an array.https://www.tutorialspoint.com/data_structures_algorithms/images/quick_sort_partition_animation.gif

The pivot value divides the list into two parts. And recursively, we find the pivot for each sub-lists until all lists contains only one element.

What we do in partitioning is that we select the first element from the array (random elements in a randomized quick sort) and then compare the rest of the array with this element. We then move all the elements less than this element to the left side of what we call the pivot index and values greater than this to the right side of the pivot index. So when we reach the end, the pivot element(first element) is placed in its correct position.

This is because all elements greater than it are to the right and less than it to the left leaving this element to be at the correct place. We extend this partitioning process to help us in sorting by recursively calling partition on left and right sub arrays.

We can implement the partition function as follows −

Example

function partition(arr, low, high, compare) {
   let pivotIndex = low + 1;
   for (let i = pivotIndex; i < high; i++) {
      if (compare(arr[i], arr[low])) {
      // Swap the number less than the pivot
      swap(arr, i, pivotIndex);
      pivotIndex += 1;
      }
   }
   // Place the pivot to its correct position
   swap(arr, pivotIndex - 1, low);
   // Return pivot's position
   return pivotIndex - 1;
}
function swap(arr, i, j) {
   let temp = arr[i];
   arr[i] = arr[j];
   arr[j] = temp;
}
You can test the partition function using:
let arr = [5, 1, 10, 8, 9, 3, 2, 45, -6];
console.log(partition(arr, 0, arr.length, (l, r) => l < r));
console.log(arr);

Output

4
[ -6, 1, 3, 2, 5, 10, 8, 45, 9 ]

Note that elements to left of 5 are less than 5 and to its right are greater than 5. We also see that 5's index is 4.

Now let us see how Quick Sort works −

If we have a window greater than 1 element, we call partition on the array from low to high, take the returned index and use it to call quicksort on left and right halves of the array.

Example

function QuickSort(arr, low, high, compare = (l, r) => l < r) {
   if (high - low > 0) {
      // Partition the array
      let mid = partition(arr, low, high, compare);
      // Recursively sort the left half
      QuickSort(arr, low, mid, compare);
      // Recursively sort the right half
      QuickSort(arr, mid + 1, high, compare);
   }
}
You can test this using:
let arr = [5, 1, 10, 8, 9, 3, 2, 45, -6];
QuickSort(arr, 0, arr.length, (l, r) => l < r);
console.log(arr);

Output

[ -6, 1, 2, 3, 5, 8, 9, 10, 45 ]