hector-sorts
Version:
A collection of sorting algorithms implemented in JavaScript.
630 lines (535 loc) • 14.8 kB
JavaScript
// sortingAlgorithms.js
// Bubble Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
// Selection Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function selectionSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
}
return arr;
}
// Insertion Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function insertionSort(arr) {
const len = arr.length;
for (let i = 1; i < len; 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;
}
// Merge Sort
// Time Complexity: O(n log n)
// Space Complexity: O(n)
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}
// Quick Sort
// Time Complexity: O(n log n)
// Space Complexity: O(log n) - average case
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[arr.length - 1];
const left = [];
const right = [];
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
// Heap Sort
// Time Complexity: O(n log n)
// Space Complexity: O(1)
function heapSort(arr) {
function heapify(arr, n, i) {
let largest = i;
let left = 2 * i + 1;
let right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
heapify(arr, n, largest);
}
}
const len = arr.length;
for (let i = Math.floor(len / 2) - 1; i >= 0; i--) {
heapify(arr, len, i);
}
for (let i = len - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
heapify(arr, i, 0);
}
return arr;
}
// Counting Sort
// Time Complexity: O(n + k)
// Space Complexity: O(k)
function countingSort(arr) {
const max = Math.max(...arr);
const min = Math.min(...arr);
const count = Array(max - min + 1).fill(0);
const output = [];
arr.forEach(num => count[num - min]++);
count.forEach((freq, num) => {
for (let i = 0; i < freq; i++) {
output.push(num + min);
}
});
return output;
}
// Radix Sort
// Time Complexity: O(nk)
// Space Complexity: O(n + k)
function radixSort(arr) {
const max = Math.max(...arr);
const maxDigitCount = Math.floor(Math.log10(max)) + 1;
let digitBuckets;
for (let k = 0; k < maxDigitCount; k++) {
digitBuckets = Array.from({ length: 10 }, () => []);
for (let i = 0; i < arr.length; i++) {
const digit = getDigit(arr[i], k);
digitBuckets[digit].push(arr[i]);
}
arr = [].concat(...digitBuckets);
}
return arr;
}
function getDigit(num, place) {
return Math.floor(Math.abs(num) / Math.pow(10, place)) % 10;
}
// Bucket Sort
// Time Complexity: O(n + k)
// Space Complexity: O(n)
function bucketSort(arr) {
const min = Math.min(...arr);
const max = Math.max(...arr);
const bucketSize = 5;
const bucketCount = Math.floor((max - min) / bucketSize) + 1;
const buckets = Array.from({ length: bucketCount }, () => []);
arr.forEach(num => {
const bucketIndex = Math.floor((num - min) / bucketSize);
buckets[bucketIndex].push(num);
});
return buckets.flatMap(bucket => bucket.sort((a, b) => a - b));
}
// Shell Sort
// Time Complexity: O(n log^2 n)
// Space Complexity: O(1)
function shellSort(arr) {
const len = arr.length;
let gap = Math.floor(len / 2);
while (gap > 0) {
for (let i = gap; i < len; i++) {
let temp = arr[i];
let j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap = Math.floor(gap / 2);
}
return arr;
}
// Cocktail Shaker Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function cocktailShakerSort(arr) {
let swapped = true;
let start = 0;
let end = arr.length - 1;
while (swapped) {
swapped = false;
for (let i = start; i < end; i++) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
swapped = true;
}
}
if (!swapped) break;
swapped = false;
end--;
for (let i = end - 1; i >= start; i--) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
swapped = true;
}
}
start++;
}
return arr;
}
// Comb Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function combSort(arr) {
const shrinkFactor = 1.3;
let gap = arr.length;
let swapped = true;
while (gap > 1 || swapped) {
if (gap > 1) gap = Math.floor(gap / shrinkFactor);
let i = 0;
swapped = false;
while (i + gap < arr.length) {
if (arr[i] > arr[i + gap]) {
[arr[i], arr[i + gap]] = [arr[i + gap], arr[i]];
swapped = true;
}
i++;
}
}
return arr;
}
// Gnome Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function gnomeSort(arr) {
let i = 1;
while (i < arr.length) {
if (i === 0 || arr[i] >= arr[i - 1]) {
i++;
} else {
[arr[i], arr[i - 1]] = [arr[i - 1], arr[i]];
i--;
}
}
return arr;
}
// Cycle Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function cycleSort(arr) {
for (let cycleStart = 0; cycleStart < arr.length - 1; cycleStart++) {
let item = arr[cycleStart];
let pos = cycleStart;
for (let i = cycleStart + 1; i < arr.length; i++) {
if (arr[i] < item) pos++;
}
if (pos === cycleStart) continue;
while (item === arr[pos]) pos++;
let temp = arr[pos];
arr[pos] = item;
item = temp;
while (pos !== cycleStart) {
pos = cycleStart;
for (let i = cycleStart + 1; i < arr.length; i++) {
if (arr[i] < item) pos++;
}
while (item === arr[pos]) pos++;
temp = arr[pos];
arr[pos] = item;
item = temp;
}
}
return arr;
}
// Pancake Sort
// Time Complexity: O(n^2)
// Space Complexity: O(1)
function pancakeSort(arr) {
function flip(arr, end) {
let start = 0;
while (start < end) {
[arr[start], arr[end]] = [arr[end], arr[start]];
start++;
end--;
}
}
let n = arr.length;
for (let i = n - 1; i >= 0; i--) {
let maxIndex = arr.indexOf(Math.max(...arr.slice(0, i + 1)));
flip(arr, maxIndex);
flip(arr, i);
}
return arr;
}
// Bogosort
// Time Complexity: O((n+1)!)
// Space Complexity: O(1)
function bogoSort(arr) {
function isSorted(arr) {
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
while (!isSorted(arr)) {
arr = shuffle(arr);
}
return arr;
}
// Stooge Sort
// Time Complexity: O(n^(log 3 / log 1.5)) = O(n^2.7095)
// Space Complexity: O(1)
function stoogeSort(arr, low = 0, high = arr.length - 1) {
if (low >= high) return;
if (arr[low] > arr[high]) {
[arr[low], arr[high]] = [arr[high], arr[low]];
}
if (high - low + 1 > 2) {
const t = Math.floor((high - low + 1) / 3);
stoogeSort(arr, low, high - t);
stoogeSort(arr, low + t, high);
stoogeSort(arr, low, high - t);
}
return arr;
}
// Bitonic Sort
// Time Complexity: O(log^2 n)
// Space Complexity: O(n log n)
function bitonicSort(arr, up = true) {
const merge = (arr, low, count, up) => {
if (count > 1) {
const k = count / 2;
for (let i = low; i < low + k; i++) {
if ((arr[i] > arr[i + k]) === up) {
[arr[i], arr[i + k]] = [arr[i + k], arr[i]];
}
}
merge(arr, low, k, up);
merge(arr, low + k, k, up);
}
};
const bitonicMerge = (arr, low, count, up) => {
if (count > 1) {
const k = count / 2;
bitonicMerge(arr, low, k, true);
bitonicMerge(arr, low + k, k, false);
merge(arr, low, count, up);
}
};
const n = arr.length;
for (let k = 2; k <= n; k *= 2) {
for (let j = k >> 1; j > 0; j >>= 1) {
for (let i = 0; i < n - k; i += k) { // Adjust loop bounds
bitonicMerge(arr, i, k, up);
}
}
}
return arr;
}
// Bozo Sort
// Time Complexity: O((n+1)!)
// Space Complexity: O(1)
function bozoSort(arr) {
const isSorted = arr => {
for (let i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) return false;
}
return true;
};
const shuffle = arr => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
};
while (!isSorted(arr)) {
shuffle(arr);
}
return arr;
}
// Timsort:
// Time Complexity:
// Best Case: O(n)
// Average Case: O(n log n)
// Worst Case: O(n log n)
// Space Complexity: O(n)
function timsort(arr) {
const MIN_MERGE = 32;
const minRunLength = minRun(arr.length);
function minRun(length) {
let r = 0;
while (length >= MIN_MERGE) {
r |= length & 1;
length >>= 1;
}
return length + r;
}
function timSort(arr) {
const n = arr.length;
for (let i = 0; i < n; i += minRunLength) {
insertionSort(arr, i, Math.min(i + minRunLength - 1, n - 1));
}
for (let size = minRunLength; size < n; size = 2 * size) {
for (let left = 0; left < n; left += 2 * size) {
let mid = left + size - 1;
let right = Math.min((left + 2 * size - 1), (n - 1));
if (mid < right) merge(arr, left, mid, right);
}
}
return arr;
}
return timSort(arr);
}
// Introsort:
// Time Complexity:
// Best Case: O(n log n)
// Average Case: O(n log n)
// Worst Case: O(n log n)
// Space Complexity: O(log n)
function introSort(arr) {
const maxDepth = Math.floor(2 * Math.log(arr.length) / Math.log(2));
function insertionSort(arr, start, end) {
for (let i = start + 1; i <= end; i++) {
let key = arr[i];
let j = i - 1;
while (j >= start && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
function partition(arr, low, high) {
const pivot = arr[high];
let i = low - 1;
for (let j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
return i + 1;
}
function quickSortWithPartition(arr, low, high, depth) {
if (low < high) {
if (depth == 0) {
heapSort(arr, low, high);
return;
}
const pi = partition(arr, low, high);
quickSortWithPartition(arr, low, pi - 1, depth - 1);
quickSortWithPartition(arr, pi + 1, high, depth - 1);
}
}
quickSortWithPartition(arr, 0, arr.length - 1, maxDepth);
insertionSort(arr, 0, arr.length - 1);
return arr;
}
// Strand sort:
// Time Complexity:
// Best Case: O(n)
// Average Case: O(n^2)
// Worst Case: O(n^2)
// Space Complexity: O(n)
function strandSort(arr) {
function strand(arr) {
let result = [arr.shift()];
for (let i = 0; i < arr.length; i++) {
if (arr[i] >= result[result.length - 1]) {
result.push(arr.splice(i--, 1)[0]);
}
}
return result;
}
let result = [];
while (arr.length) {
let subArr = strand(arr);
result = merge(result, subArr);
}
return result;
}
// Library sort:
// Time Complexity:
// Best Case: O(n log n)
// Average Case: O(n log n)
// Worst Case: O(n log n)
// Space Complexity: O(1)
function librarySort(arr) {
arr.sort((a, b) => a - b);
return arr;
}
module.exports = {
bubbleSort,
selectionSort,
insertionSort,
mergeSort,
quickSort,
heapSort,
countingSort,
radixSort,
bucketSort,
shellSort,
cocktailShakerSort,
combSort,
gnomeSort,
cycleSort,
pancakeSort,
bogoSort,
stoogeSort,
bitonicSort,
bozoSort,
timsort,
introSort,
strandSort,
librarySort
};