UNPKG

ibowankenobi-mergesort

Version:

Merge Sort algorithm implementation without recursion, using cached binary trees

359 lines (295 loc) 9.18 kB
/** * Mergesort module * @module Mergesort */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Mergesort = factory()); }(this, (function () { 'use strict'; var temp = []; function sort(arr, l1, l2, f) { var o = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var i = 0, j = 0, s1 = o, e1 = o + l1 - 1, s2 = e1 + 1, e2 = s2 + l2 - 1, state = "both not traversed"; if (f(arr[e1], arr[s2]) <= 0) { return; } else if (f(arr[s1], arr[e2]) >= 0) { while (l2--) { temp[i++] = arr[s2++]; } while (l1--) { temp[i++] = arr[s1++]; } state = "skip"; } outer: while (state) { switch (state) { case "both not traversed": if (f(arr[s1], arr[s2]) > 0) { temp[i++] = arr[s2++]; } else { temp[i++] = arr[s1++]; } if (s1 > e1) { state = "left traversed"; continue outer; } else if (s2 > e2) { state = "right traversed"; continue outer; } continue outer; case "left traversed": temp[i++] = arr[s2++]; if (s2 > e2) { break outer; } continue outer; case "right traversed": temp[i++] = arr[s1++]; if (s1 > e1) { break outer; } continue outer; case "skip": break outer; } } while (i--) { arr[o++] = temp[j++]; } } function insertionSort(arr, l, f) { var o = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var i = 0, j, u, temp; --l; while (i < l) { j = i; u = o + j; temp = arr[u + 1]; while (~j && f(arr[u], temp) > 0) { arr[u + 1] = arr[u]; --j, --u; } arr[u + 1] = temp; i++; } return arr; } function walk(arr, tree, f) { var hThreshold = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 8; var backtrack = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; outer: while (tree) { var leaf = tree.firstChild; if (!leaf) { if (tree.p) { tree = tree.p; continue; } tree = null; continue; } while (leaf.firstChild) { leaf = leaf.firstChild; } if (leaf.nextSibling.firstChild) { tree = leaf.nextSibling; continue; } if (leaf.l <= hThreshold) { insertionSort(arr, leaf.p.l, f, leaf.o); } else { sort(arr, leaf.l, leaf.nextSibling.l, f, leaf.o); } leaf = leaf.p; backtrack && (leaf.lastChild = leaf.firstChild.nextSibling); leaf.firstChild = null; while (!leaf.nextSibling) { leaf = leaf.p || leaf; if (!leaf.p) { tree = leaf; continue outer; } } tree = leaf.nextSibling; } } function tree(l) { var node = { o: 0, l: l, p: null, nextSibling: null, prevSibling: null }, state = "down"; outer: while (state) { switch (state) { case "down": if (node.l <= 1) { state = "right"; continue outer; } var nL = node.l / 2 | 0; node.firstChild = { o: node.o, l: nL, p: node, nextSibling: null, prevSibling: null }; node.firstChild.nextSibling = node.lastChild = { o: node.o + nL, l: node.l - nL, p: node, nextSibling: null, prevSibling: node.firstChild }; node = node.firstChild; state = "down"; continue outer; case "right": if (!node.nextSibling) { state = "up"; continue outer; } node = node.nextSibling; state = "down"; continue outer; case "up": if (!node.p) { return node; } node = node.p; if (node.nextSibling) { state = "right"; continue outer; } state = "up"; continue outer; } } } function regen(tree) { outer: while (tree) { var leaf = tree.lastChild; if (!leaf) { if (tree.p) { tree = tree.p; continue; } tree = null; continue; } while (leaf.lastChild) { leaf = leaf.lastChild; } if (leaf.prevSibling.lastChild) { tree = leaf.prevSibling; continue; } leaf = leaf.p; leaf.firstChild = leaf.lastChild.prevSibling; leaf.lastChild = null; while (!leaf.prevSibling) { leaf = leaf.p || leaf; if (!leaf.p) { tree = leaf; continue outer; } } tree = leaf.prevSibling; } } /** * An optinal configuration object that can be passed to the `Mergesort` factory function * @typedef {Object} module:Mergesort~config * @property {number} threshold The threshold below where the algorithm temporarily switches over to insertion sort * @property {number} size Use this if you are going to consistently sort arrays of fixed size. * Refers to size of the array to be sorted, where a binary tree is precalculated. * The tree will be reused for each call of the returned `instance`. * For each use, the tree is *walked* by setting `firstChild` properties of leaf nodes to null * and then regenerated by setting them again from the `lastChild` property of their parent. * This option results in a performance gain for large (> 1M) arrays, where cost of creating * the tree is greater than the cost of walking + regenerating the tree. For small arrays, * it has the reverse effect. It is set to off by default. */ var undef = void 0; /** * Returns a `Mergesort` `instance` * * ```javascript * * let instance = Mergesort(); //switches to insertion sort for array fragments < 16 * * instance = Mergesort({threshold: 64}); //will switch to insertion sort for fragments < 64 * * instance = Mergesort({size:999}); //will throw an error if you try to sort arrays with length other than 999 * * ``` * * @alias module:Mergesort * * @param {module:Mergesort~config} config a configuration object with optional parameters (default 16) * @returns {module:Mergesort~instance} * */ function index () { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { threshold: 16, size: undef }, _ref$threshold = _ref.threshold, threshold = _ref$threshold === void 0 ? 16 : _ref$threshold, _ref$size = _ref.size, size = _ref$size === void 0 ? undef : _ref$size; if (size && typeof size !== "number") { throw new Error("Size must be of type number"); } var hThreshold = threshold / 2, _tree = size ? tree(size) : undef, /** * * ```javascript * let instance = Mergesort(), * * inputArray = [{value:10},{value:1},{value:5}], * * compare = (a, b) => a.value - b.value; * * instance(inputArray, compare); //[{value:1}, ...] * * @namespace * @param {Array} ArrayToBeSorted - Input array to be sorted * @param {Function} Compare - compare function, same as in [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) * @returns {Array} input array */ instance = function instance(arr, f) { var _length = arr.length; if (_tree) { if (_length !== _tree.l) { throw new Error("Array length must be " + size); } temp.length = _length; walk(arr, _tree, f, hThreshold, true); regen(_tree); } else { temp.length = _length; walk(arr, tree(_length), f, hThreshold); } temp.length = 0; return arr; }; return instance; } index.version = "0.0.3"; return index; })));