mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 15:39:42 +08:00

* chore: Switch to Node 20 + Vitest * chore: migrate to vitest mock functions * chore: code style (switch to prettier) * test: re-enable long-running test Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime! see #1193 * chore: code style * chore: fix failing tests * Updated Documentation in README.md * Update contribution guidelines to state usage of Prettier * fix: set prettier printWidth back to 80 * chore: apply updated code style automatically * fix: set prettier line endings to lf again * chore: apply updated code style automatically --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
310 lines
8.5 KiB
JavaScript
310 lines
8.5 KiB
JavaScript
/**
|
|
* @function Introsort (As implemented in STD C++ Lib)
|
|
* The function performs introsort which is used in
|
|
* C++ Standard LIbrary, the implementation is inspired from]
|
|
* library routine itself.
|
|
* ALGORITHM:
|
|
* 1) It performs quicksort on array until the recursion depth
|
|
* exceeds a pre determined limit.
|
|
* 2) If the limit is reached it switches to heapsort
|
|
* 3) For array size less than a threshold(16) directly
|
|
* does insertion sort on array
|
|
* @param {Array} array the array to be sorted
|
|
* @param {Function} compare the comparison function
|
|
*
|
|
* @see [Introsort](https://en.wikipedia.org/wiki/Introsort)
|
|
* @author [Lakhan Nad](https://github.com/Lakhan-Nad)
|
|
*/
|
|
function introsort(array, compare) {
|
|
/**
|
|
* @function Default Comparison Function
|
|
* This function is same as implemented by
|
|
* Array.sort method
|
|
* @see [StackOverflow](https://stackoverflow.com/questions/47334234/how-to-implement-array-prototype-sort-default-compare-function)
|
|
* @param {*} a variable 1
|
|
* @param {*} b variable 2
|
|
* @returns {Number}
|
|
* -1 if a is less than b
|
|
* 0 if a is equal to b
|
|
* 1 if a greater than b
|
|
*/
|
|
const defaultComparator = function (x, y) {
|
|
if (x === undefined && y === undefined) return 0
|
|
if (x === undefined) return 1
|
|
if (y === undefined) return -1
|
|
const xString = toString(x)
|
|
const yString = toString(y)
|
|
if (xString < yString) return -1
|
|
if (xString > yString) return 1
|
|
return 0
|
|
}
|
|
/**
|
|
* @function helper function for defaultComparator
|
|
* Converts a given object to String
|
|
* @throws TypeError()
|
|
* @param {Object} obj
|
|
* @returns {String} String representation of given object
|
|
*/
|
|
const toString = function (obj) {
|
|
if (obj === null) return 'null'
|
|
if (typeof obj === 'boolean' || typeof obj === 'number') {
|
|
return obj.toString()
|
|
}
|
|
if (typeof obj === 'string') return obj
|
|
if (typeof obj === 'symbol') throw new TypeError()
|
|
return obj.toString()
|
|
}
|
|
/**
|
|
* Checks if the value passed is an array
|
|
* or not
|
|
*/
|
|
if (Array.isArray(array) === false) {
|
|
return
|
|
}
|
|
/**
|
|
* If the compare parameter is not a function
|
|
* or not passed at all use default comparator
|
|
* function
|
|
*/
|
|
if (typeof compare !== 'function') {
|
|
compare = defaultComparator // If compare is not a comparator function
|
|
}
|
|
/**
|
|
* Use a closure to define the whole sort
|
|
* implementation this is done through
|
|
* [IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression)
|
|
*/
|
|
return (function (array, comparator) {
|
|
const swap = function (index1, index2) {
|
|
const temp = array[index1]
|
|
array[index1] = array[index2]
|
|
array[index2] = temp
|
|
}
|
|
/**
|
|
* @constant THRESHOLD
|
|
* If the length of array is less than
|
|
* this then we simply perform insertion sort
|
|
*/
|
|
const THRESHOLD = 16
|
|
/**
|
|
* @constant TUNEMAXDEPTH
|
|
* Constant used to increase or decrease value
|
|
* of maxDepth
|
|
*/
|
|
const TUNEMAXDEPTH = 1
|
|
const len = array.length
|
|
/**
|
|
* Return if array is only of length 1
|
|
* Array of size 1 is always sorted
|
|
*/
|
|
if (len === 1) {
|
|
return
|
|
}
|
|
/**
|
|
* Calculate maxDepth = log2(len)
|
|
* Taken from implementation in stdc++
|
|
*/
|
|
const maxDepth = Math.floor(Math.log2(len)) * TUNEMAXDEPTH
|
|
/**
|
|
* The very first call to quicksort
|
|
* this initiates sort routine
|
|
*/
|
|
quickSort(0, len, maxDepth)
|
|
/**
|
|
* A final check call to insertion sort
|
|
* on sorted array
|
|
*/
|
|
insertionSort(0, len)
|
|
/** ********************* Implementation of various routines **************************/
|
|
/**
|
|
* @function
|
|
* This is recursive quicksort implementation in array
|
|
* of segment [start,last-1]
|
|
* [QuickSort](https://en.wikipedia.org/wiki/Quicksort)
|
|
* @param {Number} start the start index of array segment to be sorted
|
|
* @param {Number} last one more than the last index of array segment
|
|
* @param {Number} depth this measures how many recursive calls are done
|
|
*/
|
|
function quickSort(start, last, depth) {
|
|
if (last - start <= THRESHOLD) {
|
|
insertionSort(start, last)
|
|
return
|
|
} else if (depth <= 0) {
|
|
heapSort(start, last)
|
|
return
|
|
}
|
|
let pivot = (last + start) >> 1
|
|
pivot = partition(start, last, pivot)
|
|
quickSort(start, pivot, depth - 1)
|
|
quickSort(pivot + 1, last, depth - 1)
|
|
}
|
|
/**
|
|
* @function Helper function to quicksort
|
|
* @param {Number} start the start of array segment to partition
|
|
* @param {Number} last one more than last index of the array segment
|
|
* @param {Number} pivot the index of pivot to be used
|
|
* @returns {Number} the index of pivot after partition
|
|
*/
|
|
function partition(start, last, pivot) {
|
|
swap(start, pivot)
|
|
pivot = start
|
|
let lo = start
|
|
let hi = last
|
|
while (true) {
|
|
lo++
|
|
while (comparator(array[lo], array[pivot]) <= 0 && lo !== last) {
|
|
lo++
|
|
}
|
|
hi--
|
|
while (comparator(array[hi], array[pivot]) > 0 && hi !== start) {
|
|
hi--
|
|
}
|
|
if (lo >= hi) {
|
|
break
|
|
}
|
|
swap(lo, hi)
|
|
}
|
|
swap(start, hi)
|
|
return hi
|
|
}
|
|
/**
|
|
* @function
|
|
* Performs insertion sort on array of range
|
|
* [start, last-1]
|
|
* @param {Number} start the first index of array segment to be sorted
|
|
* @param {Number} last one more than last index of array to be sorted
|
|
*/
|
|
function insertionSort(start, last) {
|
|
let i, j
|
|
for (i = start + 1; i < last; i++) {
|
|
j = i - 1
|
|
while (j >= 0 && comparator(array[j], array[j + 1]) > 0) {
|
|
swap(j, j + 1)
|
|
j--
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* @function
|
|
* Performs heapsort in array segment of range [start, last-1]
|
|
* [HeapSort](https://en.wikipedia.org/wiki/Heapsort)
|
|
* @param {Number} start the first index of array segment to be sorted
|
|
* @param {Number} last one more than last index of array to be sorted
|
|
*/
|
|
function heapSort(start, last) {
|
|
let x = (last + start) >> 1
|
|
while (x - start >= 0) {
|
|
heapify(x, start, last)
|
|
x--
|
|
}
|
|
x = last - 1
|
|
while (x - start > 0) {
|
|
swap(start, x)
|
|
heapify(start, start, x)
|
|
x--
|
|
}
|
|
}
|
|
/**
|
|
* @function Helper function to heapsort routine
|
|
* @param {Number} cur the index we need to heapify
|
|
* @param {Number} start the start index of array segment that cur belongs to
|
|
* @param {Number} last one more than last index of segment that cur belongs to
|
|
*/
|
|
function heapify(cur, start, last) {
|
|
const size = last - start
|
|
let max, lt, rt
|
|
cur = cur - start
|
|
while (true) {
|
|
max = cur
|
|
lt = 2 * max + 1
|
|
rt = 2 * max + 2
|
|
if (
|
|
lt < size &&
|
|
comparator(array[start + max], array[start + lt]) < 0
|
|
) {
|
|
max = lt
|
|
}
|
|
if (
|
|
rt < size &&
|
|
comparator(array[start + max], array[start + rt]) < 0
|
|
) {
|
|
max = rt
|
|
}
|
|
if (max !== cur) {
|
|
swap(start + cur, start + max)
|
|
cur = max
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})(array, compare)
|
|
}
|
|
|
|
/**
|
|
* @example Demo run of the sort routine
|
|
* The data is randomly generated
|
|
* Returns 'RIGHT:)' if the sort routine worked as expected,
|
|
* 'WRONG!!' otherwise
|
|
*/
|
|
function demo1() {
|
|
const data = []
|
|
const size = 1000000
|
|
let i = 0
|
|
let temp
|
|
const c = function (a, b) {
|
|
return a - b
|
|
}
|
|
for (i = 0; i < size; i++) {
|
|
temp = Math.random() * Number.MAX_SAFE_INTEGER
|
|
data.push(temp)
|
|
}
|
|
introsort(data, c)
|
|
let faulty = false
|
|
for (i = 1; i < size; i++) {
|
|
if (data[i] < data[i - 1]) {
|
|
faulty = true
|
|
break
|
|
}
|
|
}
|
|
if (faulty) {
|
|
return 'WRONG!!'
|
|
} else {
|
|
return 'RIGHT:)'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @example Demo run of the sort routine
|
|
* using the default compare function and
|
|
* comparing the results with Array.sort
|
|
*/
|
|
function demo2() {
|
|
const data = []
|
|
const data2 = []
|
|
const size = 1000000
|
|
let i = 0
|
|
let temp
|
|
for (i = 0; i < size; i++) {
|
|
temp = Math.random() * Number.MAX_SAFE_INTEGER
|
|
data.push(temp)
|
|
data2.push(temp)
|
|
}
|
|
introsort(data)
|
|
data2.sort()
|
|
let faulty = false
|
|
for (i = 1; i < size; i++) {
|
|
if (data[i] !== data2[i]) {
|
|
faulty = true
|
|
break
|
|
}
|
|
}
|
|
if (faulty) {
|
|
return 'WRONG Implemented Comparator!!'
|
|
} else {
|
|
return 'Comparator Works Fine:)'
|
|
}
|
|
}
|
|
|
|
export { introsort, demo1, demo2 }
|