In this article, we will explore various approaches to solving this problem. We’ll discuss some of the most popular sorting algorithms, their time and space complexities, and their suitability for this problem. So, let’s roll up our sleeves and get to work!
Problem Statement:
Given an array of integers
nums
, sort the array in ascending order and return it. You must solve the problem without using any built-in functions inO(nlog(n))
time complexity and with the smallest space complexity possible.
We will implement two sorting algorithms, Merge Sort and Quick Sort to sort the input array in O(N \ Log(N)) time and O(N)* Space.
Approach 1: Merge Sort
Merge sort is a divide-and-conquer algorithm that breaks the array into smaller sub-arrays until each sub-array contains only one element. Then, it merges the sub-arrays back together in sorted order, until the entire array is sorted.
Divide the Array - The first step is to divide the array into smaller sub-arrays until each sub-array contains only one element. We do this by recursively dividing the array in half until we can’t divide it any further.
Merge the Sub-Arrays - Once we have divided the array into single-element sub-arrays, we merge them in sorted order. We do this by comparing the first element of each sub-array and placing them in order in a new array. We repeat this process until all the sub-arrays are merged into a single sorted array.
// ES6 Arrow Function
const mergeSort = arr => {
// Base Case - if the array has less than 2 elements, it's already sorted
if(arr.length < 2) return arr;
// Split the array into two halves
let middleIndex = Math.floor(arr.length / 2);
let leftHalf = arr.slice(0, middleIndex);
let rightHalf = arr.slice(middleIndex);
// Recursively sort each half
const sortedLeftHalf = mergeSort(leftHalf);
const sortedRightHalf = mergeSort(rightHalf);
// Merge the sorted halves back
return merge(sortedLeftHalf, sortedRightHalf);
}
const merge = (leftArr, rightArr) => {
let mergedArr = [];
let leftIndex = 0, rightIndex = 0;
// Iterate through both arrays adding the smaller elements in the merged array
while(leftIndex < leftArr.length && rightIndex < rightArr.length) {
if(leftArr[leftIndex] < rightArr[rightIndex]) {
mergedArr.push(leftArr[leftIndex]);
leftIndex++;
} else {
mergedArr.push(rightArr[rightIndex]);
rightIndex++;
}
}
// Add the remaining elements from the left array
while(leftIndex < leftArr.length) {
mergedArr.push(leftArr[leftIndex]);
leftIndex++;
}
// Add the remaining elements from the right array
while(rightIndex < rightArr.length) {
mergedArr.push(rightArr[rightIndex]);
rightIndex++;
}
return mergedArr;
}
Time Complexity: O(N * Log(N))
Space Complexity: O(N)
Approach 2: Quick Sort
Quick sort is also a divide-and-conquer algorithm that picks a “pivot” element and partitions the array around the pivot, such that all elements on the left side of the pivot are less than or equal to the pivot, and all elements on the right side of the pivot are greater than the pivot. Then, it recursively applies the same partitioning process to the left and right sub-arrays until the entire array is sorted.
Choose a Pivot - The first step of the quick sort algorithm is to choose a pivot element from the array. There are many ways to choose a pivot element, but the most common approach is to choose the first or last element of the array.
Partition the Array - Once we have chosen the pivot element, we partition the array around the pivot. We do this by scanning the array from both ends and swapping elements that are on the wrong side of the pivot. We repeat this process until all elements on the left side of the pivot are less than or equal to the pivot, and all elements on the right side of the pivot are greater than the pivot.
Recursively Sort the Sub-Arrays - Once we have partitioned the array, we recursively apply the same partitioning process to the left and right sub-arrays until the entire array is sorted.
// ES6 Arrow Function
const quickSort = arr => {
// Base case: if the array has less than 2 elements then it's already sorted
if(arr.length < 2) return arr;
// Choose a pivot
const pivot = arr[0];
// Divide the array in 2 parts: Elements <= pivot and Elements > pivot
const lessThanPivot = [], greaterThanPivot = [];
for(let i = 1; i < arr.length; i++) {
if(arr[i] <= pivot) lessThanPivot.push(arr[i]);
else greaterThanPivot.push(arr[i]);
}
// Recursively sort the partitions
const sortedLessThanPivot = quickSort(lessThanPivot);
const sortedGreaterThanPivot = quickSort(greaterThanPivot);
// Concatenate the sorted partition and the pivot element
return sortedLessThanPivot.concat([pivot], sortedGreaterThanPivot);
}
Time Complexity: O(N * Log(N))
Space Complexity: O(N)
And there you have it guys! We’ve explored two approaches to solve this problem and hopefully had some fun along the way. I hope this article has provided you with valuable insights and helped you better understand the different approaches to solving this problem. Happy coding!
Problem - Leetcode 912