Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Recursion and Divide & Conquer - Most efficient sorting algorithms (Quick Sort, Merge Sort) recursively divide the problem into subproblems
Arrays and In-Place Manipulation - Understanding how to swap elements and modify arrays without extra space
Binary Heap Data Structure - Required for understanding Heap Sort and the heapify operation
Hash Maps - Used in Counting Sort to track frequency of elements
1. Quick Sort
Intuition
Quick sort works by selecting a pivot element and partitioning the array so that elements smaller than the pivot go to its left and larger elements go to its right. This implementation uses median-of-three pivot selection (comparing left, middle, and right elements) to avoid worst-case performance on already sorted arrays. After partitioning, we recursively sort the two halves.
Algorithm
Base case: if the subarray has 0 or 1 elements, handle directly with a simple swap if needed.
Select pivot using median-of-three: compare elements at left, middle, and right positions.
Partition the array:
Move elements smaller than pivot to the left side.
Move elements larger than pivot to the right side.
Place the pivot in its final sorted position.
Recursively apply quick sort to the left and right partitions.
Time complexity: O(nlogn) in average case, O(n2) in worst case.
Space complexity: O(logn) for recursive stack.
2. Merge Sort
Intuition
Merge sort divides the array into two halves, recursively sorts each half, and then merges the sorted halves. The merge step combines two sorted arrays into one by repeatedly picking the smaller element from the front of each array. This divide-and-conquer approach guarantees O(n log n) time regardless of input order.
Algorithm
Base case: if the subarray has one or zero elements, it is already sorted.
Find the middle index and recursively sort the left half (l to mid) and right half (mid + 1 to r).
Merge the two sorted halves:
Create temporary arrays for left and right portions.
Compare elements from both arrays and place the smaller one into the result.
classSolution:defsortArray(self, nums: List[int])-> List[int]:defmerge(arr, L, M, R):
left, right = arr[L:M+1], arr[M+1:R+1]
i, j, k = L,0,0while j <len(left)and k <len(right):if left[j]<= right[k]:
arr[i]= left[j]
j +=1else:
arr[i]= right[k]
k +=1
i +=1while j <len(left):
arr[i]= left[j]
j +=1
i +=1while k <len(right):
arr[i]= right[k]
k +=1
i +=1defmergeSort(arr, l, r):if l >= r:return
m =(l + r)//2
mergeSort(arr, l, m)
mergeSort(arr, m +1, r)
merge(arr, l, m, r)
mergeSort(nums,0,len(nums)-1)return nums
publicclassSolution{publicint[]sortArray(int[] nums){mergeSort(nums,0, nums.length -1);return nums;}privatevoidmergeSort(int[] arr,int l,int r){if(l >= r)return;int m =(l + r)/2;mergeSort(arr, l, m);mergeSort(arr, m +1, r);merge(arr, l, m, r);}privatevoidmerge(int[] arr,int l,int m,int r){ArrayList<Integer> temp =newArrayList<>();int i = l;int j = m +1;while(i <= m && j <= r){if(arr[i]<= arr[j]){
temp.add(arr[i]);
i++;}else{
temp.add(arr[j]);
j++;}}while(i <= m){
temp.add(arr[i]);
i++;}while(j <= r){
temp.add(arr[j]);
j++;}for(i = l; i <= r; i++){
arr[i]= temp.get(i - l);}}}
classSolution{public:
vector<int>sortArray(vector<int>& nums){mergeSort(nums,0, nums.size()-1);return nums;}private:voidmergeSort(vector<int>& arr,int l,int r){if(l >= r)return;int m =(l + r)/2;mergeSort(arr, l, m);mergeSort(arr, m +1, r);merge(arr, l, m, r);}voidmerge(vector<int>& arr,int l,int m,int r){
vector<int> temp;int i = l, j = m +1;while(i <= m && j <= r){if(arr[i]<= arr[j]){
temp.push_back(arr[i++]);}else{
temp.push_back(arr[j++]);}}while(i <= m) temp.push_back(arr[i++]);while(j <= r) temp.push_back(arr[j++]);for(int i = l; i <= r; i++){
arr[i]= temp[i - l];}}};
classSolution{/**
* @param {number[]} nums
* @return {number[]}
*/sortArray(nums){this.mergeSort(nums,0, nums.length -1);return nums;}/**
* @param {number[]} arr
* @param {number} l
* @param {number} r
* @return {void}
*/mergeSort(arr, l, r){if(l >= r)return;let m = Math.floor((l + r)/2);this.mergeSort(arr, l, m);this.mergeSort(arr, m +1, r);this.merge(arr, l, m, r);}/**
* @param {number[]} arr
* @param {number} l
* @param {number} m
* @param {number} r
* @return {void}
*/merge(arr, l, m, r){let temp =[];let i = l,
j = m +1;while(i <= m && j <= r){if(arr[i]<= arr[j]){
temp.push(arr[i++]);}else{
temp.push(arr[j++]);}}while(i <= m) temp.push(arr[i++]);while(j <= r) temp.push(arr[j++]);for(let i = l; i <= r; i++){
arr[i]= temp[i - l];}}}
publicclassSolution{publicint[]SortArray(int[] nums){MergeSort(nums,0, nums.Length -1);return nums;}privatevoidMergeSort(int[] arr,int l,int r){if(l == r)return;int m =(l + r)/2;MergeSort(arr, l, m);MergeSort(arr, m +1, r);Merge(arr, l, m, r);}privatevoidMerge(int[] arr,int L,int M,int R){int[] left = arr[L..(M +1)];int[] right = arr[(M +1)..(R +1)];int i = L, j =0, k =0;while(j < left.Length && k < right.Length){if(left[j]<= right[k]){
arr[i++]= left[j++];}else{
arr[i++]= right[k++];}}while(j < left.Length){
arr[i++]= left[j++];}while(k < right.Length){
arr[i++]= right[k++];}}}
funcsortArray(nums []int)[]int{mergeSort(nums,0,len(nums)-1)return nums
}funcmergeSort(arr []int, l, r int){if l >= r {return}
m :=(l + r)/2mergeSort(arr, l, m)mergeSort(arr, m+1, r)merge(arr, l, m, r)}funcmerge(arr []int, l, m, r int){
left :=make([]int, m-l+1)
right :=make([]int, r-m)copy(left, arr[l:m+1])copy(right, arr[m+1:r+1])
i, j, k :=0,0, l
for i <len(left)&& j <len(right){if left[i]<= right[j]{
arr[k]= left[i]
i++}else{
arr[k]= right[j]
j++}
k++}for i <len(left){
arr[k]= left[i]
i++
k++}for j <len(right){
arr[k]= right[j]
j++
k++}}
class Solution {funsortArray(nums: IntArray): IntArray {mergeSort(nums,0, nums.size -1)return nums
}privatefunmergeSort(arr: IntArray, l: Int, r: Int){if(l >= r)returnval m =(l + r)/2mergeSort(arr, l, m)mergeSort(arr, m +1, r)merge(arr, l, m, r)}privatefunmerge(arr: IntArray, l: Int, m: Int, r: Int){val left = arr.sliceArray(l..m)val right = arr.sliceArray(m +1..r)var i =0var j =0var k = l
while(i < left.size && j < right.size){if(left[i]<= right[j]){
arr[k++]= left[i++]}else{
arr[k++]= right[j++]}}while(i < left.size) arr[k++]= left[i++]while(j < right.size) arr[k++]= right[j++]}}
classSolution{funcsortArray(_ nums:[Int])->[Int]{var nums = nums
mergeSort(&nums,0, nums.count -1)return nums
}privatefuncmergeSort(_ arr:inout[Int],_ l:Int,_ r:Int){if l >= r {return}let m =(l + r)/2mergeSort(&arr, l, m)mergeSort(&arr, m +1, r)merge(&arr, l, m, r)}privatefuncmerge(_ arr:inout[Int],_ l:Int,_ m:Int,_ r:Int){letleft=Array(arr[l...m])letright=Array(arr[m+1...r])var i =0, j =0, k = l
while i <left.count && j <right.count {ifleft[i]<=right[j]{
arr[k]=left[i]
i +=1}else{
arr[k]=right[j]
j +=1}
k +=1}while i <left.count {
arr[k]=left[i]
i +=1
k +=1}while j <right.count {
arr[k]=right[j]
j +=1
k +=1}}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
3. Heap Sort
Intuition
Heap sort uses a binary heap data structure to sort elements. First, we build a max-heap from the array, which places the largest element at the root. Then we repeatedly extract the maximum (swap it to the end) and restore the heap property. This process naturally sorts the array from smallest to largest.
Algorithm
Build a max-heap by calling heapify on all non-leaf nodes from bottom to top.
The largest element is now at index 0.
Swap the root (maximum) with the last unsorted element.
Reduce the heap size by one and heapify the root to restore the max-heap property.
classSolution:defsortArray(self, nums: List[int])-> List[int]:
self.heapSort(nums)return nums
defheapify(self, arr, n, i):
l =(i <<1)+1
r =(i <<1)+2
largestNode = i
if l < n and arr[l]> arr[largestNode]:
largestNode = l
if r < n and arr[r]> arr[largestNode]:
largestNode = r
if largestNode != i:
arr[i], arr[largestNode]= arr[largestNode], arr[i]
self.heapify(arr, n, largestNode)defheapSort(self, arr):
n =len(arr)for i inrange(n //2-1,-1,-1):
self.heapify(arr, n, i)for i inrange(n -1,0,-1):
arr[0], arr[i]= arr[i], arr[0]
self.heapify(arr, i,0)
publicclassSolution{publicint[]sortArray(int[] nums){heapSort(nums);return nums;}privatevoidheapify(int[] arr,int n,int i){int l =(i <<1)+1;int r =(i <<1)+2;int largestNode = i;if(l < n && arr[l]> arr[largestNode]){
largestNode = l;}if(r < n && arr[r]> arr[largestNode]){
largestNode = r;}if(largestNode != i){int temp = arr[i];
arr[i]= arr[largestNode];
arr[largestNode]= temp;heapify(arr, n, largestNode);}}privatevoidheapSort(int[] arr){int n = arr.length;for(int i = n /2-1; i >=0; i--){heapify(arr, n, i);}for(int i = n -1; i >0; i--){int temp = arr[0];
arr[0]= arr[i];
arr[i]= temp;heapify(arr, i,0);}}}
classSolution{public:
vector<int>sortArray(vector<int>& nums){heapSort(nums);return nums;}private:voidheapify(vector<int>& arr,int n,int i){int l =(i <<1)+1;int r =(i <<1)+2;int largestNode = i;if(l < n && arr[l]> arr[largestNode]){
largestNode = l;}if(r < n && arr[r]> arr[largestNode]){
largestNode = r;}if(largestNode != i){swap(arr[i], arr[largestNode]);heapify(arr, n, largestNode);}}voidheapSort(vector<int>& arr){int n = arr.size();for(int i = n /2-1; i >=0; i--){heapify(arr, n, i);}for(int i = n -1; i >0; i--){swap(arr[0], arr[i]);heapify(arr, i,0);}}};
classSolution{/**
* @param {number[]} nums
* @return {number[]}
*/sortArray(nums){this.heapSort(nums);return nums;}/**
* @param {number[]} arr
* @param {number} n
* @param {number} i
* @return {void}
*/heapify(arr, n, i){let l =(i <<1)+1;let r =(i <<1)+2;let largestNode = i;if(l < n && arr[l]> arr[largestNode]){
largestNode = l;}if(r < n && arr[r]> arr[largestNode]){
largestNode = r;}if(largestNode !== i){[arr[i], arr[largestNode]]=[arr[largestNode], arr[i]];this.heapify(arr, n, largestNode);}}/**
* @param {number[]} arr
* @return {void}
*/heapSort(arr){let n = arr.length;for(let i = Math.floor(n /2)-1; i >=0; i--){this.heapify(arr, n, i);}for(let i = n -1; i >0; i--){[arr[0], arr[i]]=[arr[i], arr[0]];this.heapify(arr, i,0);}}}
publicclassSolution{publicint[]SortArray(int[] nums){HeapSort(nums);return nums;}privatevoidHeapify(int[] arr,int n,int i){int l =(i <<1)+1;int r =(i <<1)+2;int largestNode = i;if(l < n && arr[l]> arr[largestNode]){
largestNode = l;}if(r < n && arr[r]> arr[largestNode]){
largestNode = r;}if(largestNode != i){Swap(arr, i, largestNode);Heapify(arr, n, largestNode);}}privatevoidHeapSort(int[] arr){int n = arr.Length;for(int i = n /2-1; i >=0; i--){Heapify(arr, n, i);}for(int i = n -1; i >0; i--){Swap(arr,0, i);Heapify(arr, i,0);}}privatevoidSwap(int[] arr,int i,int j){int temp = arr[i];
arr[i]= arr[j];
arr[j]= temp;}}
funcsortArray(nums []int)[]int{heapSort(nums)return nums
}funcheapify(arr []int, n, i int){
l :=(i <<1)+1
r :=(i <<1)+2
largestNode := i
if l < n && arr[l]> arr[largestNode]{
largestNode = l
}if r < n && arr[r]> arr[largestNode]{
largestNode = r
}if largestNode != i {
arr[i], arr[largestNode]= arr[largestNode], arr[i]heapify(arr, n, largestNode)}}funcheapSort(arr []int){
n :=len(arr)for i := n/2-1; i >=0; i--{heapify(arr, n, i)}for i := n -1; i >0; i--{
arr[0], arr[i]= arr[i], arr[0]heapify(arr, i,0)}}
class Solution {funsortArray(nums: IntArray): IntArray {heapSort(nums)return nums
}privatefunheapify(arr: IntArray, n: Int, i: Int){val l =(i shl1)+1val r =(i shl1)+2var largestNode = i
if(l < n && arr[l]> arr[largestNode]){
largestNode = l
}if(r < n && arr[r]> arr[largestNode]){
largestNode = r
}if(largestNode != i){
arr[i]= arr[largestNode].also{ arr[largestNode]= arr[i]}heapify(arr, n, largestNode)}}privatefunheapSort(arr: IntArray){val n = arr.size
for(i in n /2-1 downTo 0){heapify(arr, n, i)}for(i in n -1 downTo 1){
arr[0]= arr[i].also{ arr[i]= arr[0]}heapify(arr, i,0)}}}
classSolution{funcsortArray(_ nums:[Int])->[Int]{var nums = nums
heapSort(&nums)return nums
}privatefuncheapify(_ arr:inout[Int],_ n:Int,_ i:Int){let l =(i <<1)+1let r =(i <<1)+2var largestNode = i
if l < n && arr[l]> arr[largestNode]{
largestNode = l
}if r < n && arr[r]> arr[largestNode]{
largestNode = r
}if largestNode != i {
arr.swapAt(i, largestNode)heapify(&arr, n, largestNode)}}privatefuncheapSort(_ arr:inout[Int]){let n = arr.count
for i instride(from: n /2-1, through:0, by:-1){heapify(&arr, n, i)}for i instride(from: n -1, through:1, by:-1){
arr.swapAt(0, i)heapify(&arr, i,0)}}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(logn) for recursive stack.
4. Counting Sort
Intuition
Counting sort works by counting the frequency of each distinct value, then reconstructing the sorted array by outputting each value the appropriate number of times. This is efficient when the range of values (max - min) is not significantly larger than the number of elements. It avoids comparisons entirely.
Algorithm
Find the min and max values in the array.
Create a count map to store the frequency of each value.
Iterate through the array, incrementing the count for each value.
Iterate from min to max value:
For each value with a positive count, place it into the array the appropriate number of times.
classSolution:defsortArray(self, nums: List[int])-> List[int]:defcounting_sort():
count = defaultdict(int)
minVal, maxVal =min(nums),max(nums)for val in nums:
count[val]+=1
index =0for val inrange(minVal, maxVal +1):while count[val]>0:
nums[index]= val
index +=1
count[val]-=1
counting_sort()return nums
publicclassSolution{privatevoidcountingSort(int[] arr){HashMap<Integer,Integer> count =newHashMap<>();int minVal = arr[0], maxVal = arr[0];for(int i =0; i < arr.length; i++){
minVal =Math.min(minVal, arr[i]);
maxVal =Math.max(maxVal, arr[i]);
count.put(arr[i], count.getOrDefault(arr[i],0)+1);}int index =0;for(int val = minVal; val <= maxVal;++val){while(count.getOrDefault(val,0)>0){
arr[index]= val;
index +=1;
count.put(val, count.get(val)-1);}}}publicint[]sortArray(int[] nums){countingSort(nums);return nums;}}
classSolution{private:voidcountingSort(vector<int>&arr){
unordered_map<int,int> count;int minVal =*min_element(arr.begin(), arr.end());int maxVal =*max_element(arr.begin(), arr.end());for(auto& val : arr){
count[val]++;}int index =0;for(int val = minVal; val <= maxVal;++val){while(count[val]>0){
arr[index]= val;
index +=1;
count[val]-=1;}}}public:
vector<int>sortArray(vector<int>& nums){countingSort(nums);return nums;}};
classSolution{/**
* @param {number[]} nums
* @return {number[]}
*/sortArray(nums){this.countingSort(nums);return nums;}/**
* @param {number[]} arr
* @return {void}
*/countingSort(arr){let count =newMap();let minVal = Math.min(...nums);let maxVal = Math.max(...nums);
nums.forEach((val)=>{if(!count.has(val)){
count.set(val,0);}
count.set(val, count.get(val)+1);});let index =0;for(let val = minVal; val <= maxVal; val +=1){while(count.get(val)>0){
nums[index]= val;
index +=1;
count.set(val, count.get(val)-1);}}}}
publicclassSolution{publicint[]SortArray(int[] nums){CountingSort(nums);return nums;}privatevoidCountingSort(int[] nums){Dictionary<int,int> count =newDictionary<int,int>();int minVal =int.MaxValue, maxVal =int.MinValue;foreach(int val in nums){if(!count.ContainsKey(val)){
count[val]=0;}
count[val]++;
minVal = Math.Min(minVal, val);
maxVal = Math.Max(maxVal, val);}int index =0;for(int val = minVal; val <= maxVal; val++){if(!count.ContainsKey(val))continue;while(count[val]-->0){
nums[index++]= val;}}}}
funcsortArray(nums []int)[]int{countingSort(nums)return nums
}funccountingSort(nums []int){
count :=make(map[int]int)
minVal, maxVal := nums[0], nums[0]for_, val :=range nums {
count[val]++if val < minVal {
minVal = val
}if val > maxVal {
maxVal = val
}}
index :=0for val := minVal; val <= maxVal; val++{for count[val]>0{
nums[index]= val
index++
count[val]--}}}
class Solution {funsortArray(nums: IntArray): IntArray {countingSort(nums)return nums
}privatefuncountingSort(nums: IntArray){val count = HashMap<Int, Int>()var minVal = Int.MAX_VALUE
var maxVal = Int.MIN_VALUE
for(v in nums){
count[v]= count.getOrDefault(v,0)+1
minVal =minOf(minVal, v)
maxVal =maxOf(maxVal, v)}var index =0for(v in minVal..maxVal){var c = count.getOrDefault(v,0)while(c >0){
nums[index++]= v
c--}}}}
classSolution{funcsortArray(_ nums:[Int])->[Int]{var nums = nums
countingSort(&nums)return nums
}privatefunccountingSort(_ nums:inout[Int]){var count =[Int:Int]()var minVal =Int.max
var maxVal =Int.min
for val in nums {
count[val,default:0]+=1
minVal =min(minVal, val)
maxVal =max(maxVal, val)}var index =0for val in minVal...maxVal {var c = count[val]??0while c >0{
nums[index]= val
index +=1
c -=1}}}}
Time & Space Complexity
Time complexity: O(n+k)
Space complexity: O(n)
Where n is the size of the array nums and k is the range between the minimum and maximum values in the array.
5. Radix Sort
Intuition
Radix sort processes integers digit by digit, from least significant to most significant. For each digit position, we use counting sort as a stable subroutine. Since counting sort is stable, the relative order from previous digit sorts is preserved. To handle negative numbers, we separate them, sort their absolute values in reverse, and concatenate.
Algorithm
Separate numbers into negatives (converted to positive) and positives.
For each group, apply radix sort:
Determine the maximum element to know how many digit passes are needed.
For each digit position (units, tens, hundreds, etc.):
Use counting sort based on the current digit.
Maintain stability by processing from right to left.
Reverse the sorted negatives and negate them back.
Concatenate negatives and positives to form the final sorted array.
classSolution:defsortArray(self, nums: List[int])-> List[int]:defcountSort(arr, n, d):
count =[0]*10for num in arr:
count[(num // d)%10]+=1for i inrange(1,10):
count[i]+= count[i -1]
res =[0]* n
for i inrange(n -1,-1,-1):
idx =(arr[i]// d)%10
res[count[idx]-1]= arr[i]
count[idx]-=1for i inrange(n):
arr[i]= res[i]defradixSort(arr):
n =len(arr)
max_element =max(arr)
d =1while max_element // d >0:
countSort(arr, n, d)
d *=10
negatives =[-num for num in nums if num <0]
positives =[num for num in nums if num >=0]if negatives:
radixSort(negatives)
negatives =[-num for num inreversed(negatives)]if positives:
radixSort(positives)return negatives + positives
publicclassSolution{publicint[]sortArray(int[] nums){ArrayList<Integer> negatives =newArrayList<>();ArrayList<Integer> positives =newArrayList<>();for(int num : nums){if(num <0){
negatives.add(-num);}else{
positives.add(num);}}if(!negatives.isEmpty()){radixSort(negatives);Collections.reverse(negatives);for(int i =0; i < negatives.size(); i++){
negatives.set(i,-negatives.get(i));}}if(!positives.isEmpty()){radixSort(positives);}int index =0;for(int num : negatives){
nums[index++]= num;}for(int num : positives){
nums[index++]= num;}return nums;}privatevoidcountSort(ArrayList<Integer> arr,int n,int d){int[] count =newint[10];for(int num : arr){
count[(num / d)%10]++;}for(int i =1; i <10; i++){
count[i]+= count[i -1];}ArrayList<Integer> res =newArrayList<>(Collections.nCopies(n,0));for(int i = n -1; i >=0; i--){int idx =(arr.get(i)/ d)%10;
res.set(count[idx]-1, arr.get(i));
count[idx]--;}for(int i =0; i < n; i++){
arr.set(i, res.get(i));}}privatevoidradixSort(ArrayList<Integer> arr){int n = arr.size();int maxElement =Collections.max(arr);int d =1;while(maxElement / d >0){countSort(arr, n, d);
d *=10;}}}
classSolution{public:
vector<int>sortArray(vector<int>& nums){
vector<int> negatives, positives;for(int num : nums){if(num <0){
negatives.push_back(-num);}else{
positives.push_back(num);}}if(!negatives.empty()){radixSort(negatives);reverse(negatives.begin(), negatives.end());for(int& num : negatives){
num =-num;}}if(!positives.empty()){radixSort(positives);}int index =0;for(int& num : negatives){
nums[index++]= num;}for(int& num : positives){
nums[index++]= num;}return nums;}private:voidcountSort(vector<int>& arr,int n,int d){
vector<int>count(10,0);for(int num : arr){
count[(num / d)%10]++;}for(int i =1; i <10; i++){
count[i]+= count[i -1];}
vector<int>res(n);for(int i = n -1; i >=0; i--){int idx =(arr[i]/ d)%10;
res[count[idx]-1]= arr[i];
count[idx]--;}for(int i =0; i < n; i++){
arr[i]= res[i];}}voidradixSort(vector<int>& arr){int n = arr.size();int maxElement =*max_element(arr.begin(), arr.end());int d =1;while(maxElement / d >0){countSort(arr, n, d);
d *=10;}}};
classSolution{/**
* @param {number[]} nums
* @return {number[]}
*/sortArray(nums){const negatives = nums.filter((num)=> num <0).map((num)=>-num);const positives = nums.filter((num)=> num >=0);if(negatives.length >0){this.radixSort(negatives);
negatives.reverse();for(let i =0; i < negatives.length; i++){
negatives[i]=-negatives[i];}}if(positives.length >0){this.radixSort(positives);}return[...negatives,...positives];}/**
* @param {number[]} arr
* @return {void}
*/radixSort(arr){const maxElement = Math.max(...arr);let d =1;while(Math.floor(maxElement / d)>0){this.countSort(arr, d);
d *=10;}}/**
* @param {number[]} arr
* @param {number} d
* @return {void}
*/countSort(arr, d){const count =Array(10).fill(0);for(const num of arr){
count[Math.floor(num / d)%10]++;}for(let i =1; i <10; i++){
count[i]+= count[i -1];}const res =Array(arr.length);for(let i = arr.length -1; i >=0; i--){const idx = Math.floor(arr[i]/ d)%10;
res[count[idx]-1]= arr[i];
count[idx]--;}for(let i =0; i < arr.length; i++){
arr[i]= res[i];}}}
publicclassSolution{publicint[]SortArray(int[] nums){List<int> negatives =newList<int>();List<int> positives =newList<int>();foreach(int num in nums){if(num <0) negatives.Add(-num);else positives.Add(num);}if(negatives.Count >0){RadixSort(negatives);
negatives.Reverse();for(int i =0; i < negatives.Count; i++){
negatives[i]=-negatives[i];}}if(positives.Count >0){RadixSort(positives);}List<int> result =newList<int>();
result.AddRange(negatives);
result.AddRange(positives);return result.ToArray();}privatevoidRadixSort(List<int> arr){int n = arr.Count;int maxElement =0;foreach(int num in arr){if(num > maxElement) maxElement = num;}int d =1;while(maxElement / d >0){CountSort(arr, n, d);
d *=10;}}privatevoidCountSort(List<int> arr,int n,int d){int[] count =newint[10];for(int i =0; i < n; i++){int digit =(arr[i]/ d)%10;
count[digit]++;}for(int i =1; i <10; i++){
count[i]+= count[i -1];}int[] res =newint[n];for(int i = n -1; i >=0; i--){int digit =(arr[i]/ d)%10;
res[--count[digit]]= arr[i];}for(int i =0; i < n; i++){
arr[i]= res[i];}}}
funcsortArray(nums []int)[]int{var negatives, positives []intfor_, num :=range nums {if num <0{
negatives =append(negatives,-num)}else{
positives =append(positives, num)}}iflen(negatives)>0{radixSort(negatives)for i, j :=0,len(negatives)-1; i < j; i, j = i+1, j-1{
negatives[i], negatives[j]= negatives[j], negatives[i]}for i :=range negatives {
negatives[i]=-negatives[i]}}iflen(positives)>0{radixSort(positives)}returnappend(negatives, positives...)}funcradixSort(arr []int){
maxElement :=0for_, num :=range arr {if num > maxElement {
maxElement = num
}}
d :=1for maxElement/d >0{countSort(arr, d)
d *=10}}funccountSort(arr []int, d int){
n :=len(arr)
count :=make([]int,10)for_, num :=range arr {
count[(num/d)%10]++}for i :=1; i <10; i++{
count[i]+= count[i-1]}
res :=make([]int, n)for i := n -1; i >=0; i--{
idx :=(arr[i]/ d)%10
res[count[idx]-1]= arr[i]
count[idx]--}copy(arr, res)}
class Solution {funsortArray(nums: IntArray): IntArray {val negatives = mutableListOf<Int>()val positives = mutableListOf<Int>()for(num in nums){if(num <0) negatives.add(-num)else positives.add(num)}if(negatives.isNotEmpty()){radixSort(negatives)
negatives.reverse()for(i in negatives.indices){
negatives[i]=-negatives[i]}}if(positives.isNotEmpty()){radixSort(positives)}return(negatives + positives).toIntArray()}privatefunradixSort(arr: MutableList<Int>){val maxElement = arr.maxOrNull()?:returnvar d =1while(maxElement / d >0){countSort(arr, d)
d *=10}}privatefuncountSort(arr: MutableList<Int>, d: Int){val n = arr.size
val count =IntArray(10)for(num in arr){
count[(num / d)%10]++}for(i in1 until 10){
count[i]+= count[i -1]}val res =IntArray(n)for(i in n -1 downTo 0){val idx =(arr[i]/ d)%10
res[count[idx]-1]= arr[i]
count[idx]--}for(i in0 until n){
arr[i]= res[i]}}}
classSolution{funcsortArray(_ nums:[Int])->[Int]{var negatives = nums.filter {$0<0}.map {-$0}var positives = nums.filter {$0>=0}if!negatives.isEmpty {radixSort(&negatives)
negatives.reverse()
negatives = negatives.map {-$0}}if!positives.isEmpty {radixSort(&positives)}return negatives + positives
}privatefuncradixSort(_ arr:inout[Int]){guardlet maxElement = arr.max()else{return}var d =1while maxElement / d >0{countSort(&arr, d)
d *=10}}privatefunccountSort(_ arr:inout[Int],_ d:Int){let n = arr.count
var count =[Int](repeating:0, count:10)for num in arr {
count[(num / d)%10]+=1}for i in1..<10{
count[i]+= count[i -1]}var res =[Int](repeating:0, count: n)for i instride(from: n -1, through:0, by:-1){let idx =(arr[i]/ d)%10
res[count[idx]-1]= arr[i]
count[idx]-=1}
arr = res
}}
Time & Space Complexity
Time complexity: O(d∗n)
Space complexity: O(n)
Where n is the size of the array nums and d is the number of digits in the maximum element of the array.
6. Shell Sort
Intuition
Shell sort is a generalization of insertion sort that allows exchanges of elements far apart. It starts by sorting elements at a large gap, then progressively reduces the gap. This allows elements to move quickly toward their final positions. When the gap becomes 1, it performs a standard insertion sort on an already nearly-sorted array.
Algorithm
Start with a gap of n / 2.
For each gap value:
Perform a gapped insertion sort: for each element, compare with elements gap positions behind.
Shift elements forward by gap until the correct position is found.
classSolution:defsortArray(self, nums: List[int])-> List[int]:defshell_sort(nums, n):
gap = n //2while gap >=1:for i inrange(gap, n):
tmp = nums[i]
j = i - gap
while j >=0and nums[j]> tmp:
nums[j + gap]= nums[j]
j -= gap
nums[j + gap]= tmp
gap //=2
n =len(nums)if n ==1:return nums
shell_sort(nums, n)return nums
publicclassSolution{privatevoidshellSort(int[] nums,int n){int gap = n /2;while(gap >=1){for(int i = gap; i < n; i++){int tmp = nums[i];int j = i - gap;while(j >=0&& nums[j]> tmp){
nums[j + gap]= nums[j];
j -= gap;}
nums[j + gap]= tmp;}
gap /=2;}}publicint[]sortArray(int[] nums){int n = nums.length;if(n ==1)return nums;shellSort(nums, n);return nums;}}
classSolution{private:voidshellSort(vector<int>& nums,int n){for(int gap = n /2; gap >=1; gap /=2){for(int i = gap; i < n; i++){int tmp = nums[i];int j = i - gap;while(j >=0&& nums[j]> tmp){
nums[j + gap]= nums[j];
j -= gap;}
nums[j + gap]= tmp;}}}public:
vector<int>sortArray(vector<int>& nums){if(nums.size()==1)return nums;shellSort(nums, nums.size());return nums;}};
classSolution{/**
* @param {number[]} nums
* @return {number[]}
*/sortArray(nums){const n = nums.length;if(n ===1)return nums;constshellSort=()=>{let gap = Math.floor(n /2);while(gap >=1){for(let i = gap; i < n; i++){let key = nums[i];let j = i - gap;while(j >=0&& nums[j]> key){
nums[j + gap]= nums[j];
j -= gap;}
nums[j + gap]= key;}
gap = Math.floor(gap /2);}};shellSort();return nums;}}
publicclassSolution{publicint[]SortArray(int[] nums){int n = nums.Length;if(n ==1)return nums;ShellSort(nums, n);return nums;}privatevoidShellSort(int[] nums,int n){int gap = n /2;while(gap >=1){for(int i = gap; i < n; i++){int tmp = nums[i];int j = i - gap;while(j >=0&& nums[j]> tmp){
nums[j + gap]= nums[j];
j -= gap;}
nums[j + gap]= tmp;}
gap /=2;}}}
funcsortArray(nums []int)[]int{
n :=len(nums)if n ==1{return nums
}shellSort(nums, n)return nums
}funcshellSort(nums []int, n int){
gap := n /2for gap >=1{for i := gap; i < n; i++{
tmp := nums[i]
j := i - gap
for j >=0&& nums[j]> tmp {
nums[j+gap]= nums[j]
j -= gap
}
nums[j+gap]= tmp
}
gap /=2}}
class Solution {funsortArray(nums: IntArray): IntArray {val n = nums.size
if(n ==1)return nums
shellSort(nums, n)return nums
}privatefunshellSort(nums: IntArray, n: Int){var gap = n /2while(gap >=1){for(i in gap until n){val tmp = nums[i]var j = i - gap
while(j >=0&& nums[j]> tmp){
nums[j + gap]= nums[j]
j -= gap
}
nums[j + gap]= tmp
}
gap /=2}}}
classSolution{funcsortArray(_ nums:[Int])->[Int]{var nums = nums
let n = nums.count
if n ==1{return nums }shellSort(&nums, n)return nums
}privatefuncshellSort(_ nums:inout[Int],_ n:Int){var gap = n /2while gap >=1{for i in gap..<n {let tmp = nums[i]var j = i - gap
while j >=0&& nums[j]> tmp {
nums[j + gap]= nums[j]
j -= gap
}
nums[j + gap]= tmp
}
gap /=2}}}
Time & Space Complexity
Time complexity: O(nlogn) in average case, O(n2) in worst case.
Space complexity: O(1)
Common Pitfalls
Using Naive Quick Sort Pivot Selection
Choosing the first or last element as the pivot without any randomization or median selection causes O(n^2) time complexity on sorted or nearly-sorted arrays. Always use randomized pivot selection or median-of-three to avoid worst-case performance on common input patterns.
Forgetting to Handle Negative Numbers in Radix Sort
Radix sort only works directly on non-negative integers. If you apply radix sort to an array containing negative numbers without first separating them, the algorithm produces incorrect results. You must handle negatives separately by converting them to positive, sorting, reversing, and negating back.
Stack Overflow in Recursive Implementations
Deep recursion in merge sort or quick sort can cause stack overflow on very large arrays. For production code, consider iterative implementations or ensure tail-call optimization where available. Some languages have strict stack limits that recursive sorting algorithms can exceed.