UNPKG

@clarketm/superavltree

Version:
1,096 lines (931 loc) • 25 kB
/** * Copyright (c) 2018, Travis Clarke * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.AVLTree = {}))); }(this, (function (exports) { 'use strict'; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; /** * * @module super/binarytree */ /** * * TreeNode * * @public * */ var TreeNode = function () { /** @private */ /** @private */ /** @private */ /** * @public * * @desc Construct a TreeNode * * @param {Item} value - node value */ function TreeNode(value) { classCallCheck(this, TreeNode); this._value = value; this._left = null; this._right = null; } /** * @public * * @desc Get the value of the node * * @returns {Item} node value */ createClass(TreeNode, [{ key: "value", get: function get$$1() { return this._value; } /** * @public * * @desc Set the value of the node * */ , set: function set$$1(value) { this._value = value; } /** * @public * * @desc Get the left child node * * @returns {TreeNode} left child node */ }, { key: "left", get: function get$$1() { return this._left; } /** * @public * * @desc Set the left child node * */ , set: function set$$1(left) { this._left = left; } /** * @public * * @desc Get the right child node * * @returns {TreeNode} right child node */ }, { key: "right", get: function get$$1() { return this._right; } /** * @public * * @desc Set the right child node * */ , set: function set$$1(right) { this._right = right; } }]); return TreeNode; }(); /** * * @module super/queue */ /** * * Queue with superpowers! 💪 * * @public * */ var Queue = function () { /** @private */ /** * @public * * @desc Construct a Queue * * @param {Array<Item>} iterable */ function Queue() { var iterable = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; classCallCheck(this, Queue); this._queue = [].concat(toConsumableArray(iterable)); } /** * @public * * @desc Get the current size of the queue * * @returns {number} size of the queue */ createClass(Queue, [{ key: "isEmpty", /** * @public * * @desc Check if queue is empty * * @returns {boolean} is queue empty */ value: function isEmpty() { return this._queue.length === 0; } /** * @public * * @desc Clear the items from the queue * * @returns {void} */ }, { key: "clear", value: function clear() { this._queue.length = 0; } /** * @public * * @desc Enqueue an item into the queue * * @param {Item} item - item to enqueue * @returns {number} size after enqueue */ }, { key: "enqueue", value: function enqueue(item) { return this._queue.push(item); } /** * @public * * @desc Dequeue an item from the queue * * @returns {Item} dequeued item */ }, { key: "dequeue", value: function dequeue() { return this._queue.shift(); } /** * @public * * @desc Convert the queue to an array * * @returns {Array<Item>} array representation of the queue */ }, { key: "toArray", value: function toArray$$1() { return this._queue.slice(0); } }, { key: "size", get: function get$$1() { return this._queue.length; } /** * @public * * @desc Get the front item in the queue * * @returns {Item} front item */ }, { key: "front", get: function get$$1() { return this._queue[0]; } /** * @public * * @desc Get the rear item in the queue * * @returns {Item} rear item */ }, { key: "rear", get: function get$$1() { return this._queue[this._queue.length - 1]; } }]); return Queue; }(); // function _defaultComparator(a, b) { return a - b; } /** * if a == b , then return `true` */ function _compareEqual(comparator) { return function (a, b) { return comparator(a, b) === 0; }; } /** * if a < b , then return `true` */ function _compareLessThan(comparator) { return function (a, b) { return comparator(a, b) < 0; }; } /** * if a > b , then return `true` */ function _compareGreaterThan(comparator) { return function (a, b) { return comparator(a, b) > 0; }; } /** * * @module super/binarytree */ var TraversalType = { PRE_ORDER: "pre", IN_ORDER: "in", POST_ORDER: "post", LEVEL_ORDER: "level" }; /** * * BinaryTree with superpowers! 💪 * * @public * */ var BinaryTree = function () { /** @private */ /** @private */ /** * @public * * @desc Construct a BinaryTree * * @param {Array<number>} iterable * @param {Comparator} comparator */ function BinaryTree() { var iterable = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var comparator = arguments[1]; classCallCheck(this, BinaryTree); this._root = null; this._compareEqual = _compareEqual(comparator || _defaultComparator); this._compareLessThan = _compareLessThan(comparator || _defaultComparator); this._compareGreaterThan = _compareGreaterThan(comparator || _defaultComparator); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = iterable[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var item = _step.value; this.insert(item); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } /** * @private * * @desc Default comparator function to sort from: * highest priority (max) -> lowest priority (min) * * @returns {number} comparison */ createClass(BinaryTree, [{ key: "getHeight", /** * @public * * @desc Get the height of the tree at node * * @param {TreeNode} node - root node * @returns {number} height of tree */ value: function getHeight() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; /** * @private * * @desc Height helper * * @param {TreeNode} node * @returns {number} height of tree */ var _height = function _height(node) { if (!node) return 0; return Math.max(_height(node.left), _height(node.right)) + 1; }; return _height(node); } /** * @public * * @desc Find minimum value in tree * * @param {TreeNode} node - root node * @returns {TreeNode} node */ }, { key: "findMin", value: function findMin() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; // $FlowFixMe if (!node.left) return node;else return this.findMin(node.left); } /** * @public * * @desc Find maximum value in tree * * @param {TreeNode} node - root node * @returns {TreeNode} node */ }, { key: "findMax", value: function findMax() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; // $FlowFixMe if (!node.right) return node;else return this.findMax(node.right); } /** * @public * * @desc Insert a value into the tree * * @param {Item} value - value to insert into the tree */ }, { key: "insert", value: function insert(value) { var _this = this; var node = new TreeNode(value); /** * @private * * @desc Insert helper * * @param {TreeNode} root */ var _insert = function _insert(root) { if (!root) return node; if (_this._compareLessThan(node.value, root.value)) { root.left = _insert(root.left); } else { root.right = _insert(root.right); } return root; }; this._root = _insert(this.root); } /** * @public * * @desc Search and retrieve a value from the tree * * @param {Item} value - value to search * @return {TreeNode} match node, or null if not found */ }, { key: "search", value: function search(value) { var _this2 = this; if (!value) return null; /** * @private * * @desc Search helper * * @param {TreeNode} node * @return {TreeNode} match node */ var _search = function _search(node) { if (!node) return null; if (_this2._compareEqual(value, node.value)) { return node; } else if (_this2._compareLessThan(value, node.value)) { return _search(node.left); } else if (_this2._compareGreaterThan(value, node.value)) { return _search(node.right); } }; return _search(this.root); } /** * @public * * @desc Remove a value from the tree * * @param {Item} value - value to remove */ }, { key: "remove", value: function remove(value) { var _this3 = this; /** * @private * * @desc Remove helper * * @param {TreeNode} node * @param {Item} value */ var _remove = function _remove(node, value) { if (!node) return null; if (_this3._compareEqual(node.value, value)) { if (!node.left && !node.right) { return null; } else if (!node.left) { return node.right; } else if (!node.right) { return node.left; } else { var aux = _this3.findMin(node.right); // $FlowFixMe node.value = aux.value; // $FlowFixMe node.right = _remove(node.right, aux.value); return node; } } else if (_this3._compareLessThan(value, node.value)) { node.left = _remove(node.left, value); return node; } else if (_this3._compareGreaterThan(value, node.value)) { node.right = _remove(node.right, value); return node; } }; // $FlowFixMe this._root = _remove(this.root, value); } /** * @public * * @desc Traverse the tree in preOrder traversal ordering * * @param {TreeNode} node - root node * @returns {Array<TreeNode>} array of nodes or values */ }, { key: "preOrder", value: function preOrder() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; var nodes = []; /** * @private * * @desc PreOrder helper * * @param {TreeNode} node */ function _preOrder(node) { if (node) { nodes = [].concat(toConsumableArray(nodes), [node]); _preOrder(node.left); _preOrder(node.right); } } _preOrder(node); return nodes; } /** * @public * * @desc Traverse the tree in inOrder traversal ordering * * @param {TreeNode} node - root node * @returns {Array<TreeNode>} array of nodes or values */ }, { key: "inOrder", value: function inOrder() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; var nodes = []; /** * @private * * @desc inOrder helper * * @param {TreeNode} node */ function _inOrder(node) { if (node) { _inOrder(node.left); nodes = [].concat(toConsumableArray(nodes), [node]); _inOrder(node.right); } } _inOrder(node); return nodes; } /** * @public * * @desc Traverse the tree in postOrder traversal ordering * * @param {TreeNode} node - root node * @returns {Array<TreeNode>} array of nodes or values */ }, { key: "postOrder", value: function postOrder() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; var nodes = []; /** * @private * * @desc PreOrder helper * * @param {TreeNode} node */ function _postOrder(node) { if (node) { _postOrder(node.left); _postOrder(node.right); nodes = [].concat(toConsumableArray(nodes), [node]); } } _postOrder(node); return nodes; } /** * @public * * @desc Traverse the tree in levelOrder traversal ordering * * @param {TreeNode} node - root node * @returns {Array<TreeNode>} array of nodes or values */ }, { key: "levelOrder", value: function levelOrder() { var node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; var nodes = []; var q = new Queue(); q.enqueue(node); while (!q.isEmpty()) { var _node = q.dequeue(); nodes.push(_node); if (_node.left) { q.enqueue(_node.left); } if (_node.right) { q.enqueue(_node.right); } } return nodes; } /** * @public * * @desc Convert the tree to an array * * @param {Traversal} traversal - method of traversal * @param {boolean} flatten - if false return nodes; if true return only values * @returns {Array<TreeNode | Item> } array representation of the list */ }, { key: "toArray", value: function toArray$$1(traversal) { var flatten = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var nodes = void 0; switch (traversal) { default: case TraversalType.PRE_ORDER: nodes = this.preOrder(this.root); break; case TraversalType.IN_ORDER: nodes = this.inOrder(this.root); break; case TraversalType.POST_ORDER: nodes = this.postOrder(this.root); break; case TraversalType.LEVEL_ORDER: nodes = this.levelOrder(this.root); break; } return flatten ? nodes.map(function (v) { return v.value; }) : nodes; } }, { key: "root", /** * @public * * @desc Get the root of the tree * * @returns {TreeNode} root node */ get: function get$$1() { return this._root; } /** * @public * * @alias getHeight(root) * * @desc Get the height of the tree * * @returns {TreeNode} root node */ }, { key: "height", get: function get$$1() { return this.getHeight(this.root); } }], [{ key: "_defaultComparator", value: function _defaultComparator$$1(a, b) { return a.value > b.value; } }]); return BinaryTree; }(); /** * * @module super/avltree */ /** * * AVLTree with superpowers! 💪 * * @public * */ var AVLTree = function (_BinaryTree) { inherits(AVLTree, _BinaryTree); function AVLTree() { classCallCheck(this, AVLTree); return possibleConstructorReturn(this, (AVLTree.__proto__ || Object.getPrototypeOf(AVLTree)).apply(this, arguments)); } createClass(AVLTree, [{ key: "_getBalance", /** * @private * * @desc Get balance factor * * @param {TreeNode} node - root node * @returns {number} balance factor */ value: function _getBalance(node) { // $FlowFixMe return this.getHeight(node.left) - this.getHeight(node.right); } /** * @private * * @desc Rotate left * * @param {TreeNode} node * @returns {TreeNode} new root */ }, { key: "_rotateLeft", value: function _rotateLeft(node) { // $FlowFixMe var root = node.right; node.right = root.left; root.left = node; return root; } /** * @private * * @desc Rotate right * * @param {TreeNode} node * @returns {TreeNode} new root */ }, { key: "_rotateRight", value: function _rotateRight(node) { // $FlowFixMe var root = node.left; node.left = root.right; root.right = node; return root; } /** * @public * * @desc Insert a value into the tree * * @param {Item} value - value to insert into the tree */ }, { key: "insert", value: function insert(value) { var _this2 = this; var node = new TreeNode(value); /** * @private * * @desc Insert helper * * @param {TreeNode} root */ var _insert = function _insert(root) { if (!root) return node; if (_this2._compareLessThan(node.value, root.value)) { root.left = _insert(root.left); } else { root.right = _insert(root.right); } var balance = _this2._getBalance(root); if (balance > 1) { // Left Rotate if (root.left && _this2._compareLessThan(node.value, root.left.value)) { return _this2._rotateRight(root); } // Left Right Rotate if (root.left && _this2._compareGreaterThan(node.value, root.left.value)) { root.left = _this2._rotateLeft(root.left); return _this2._rotateRight(root); } } if (balance < -1) { // Right Rotate if (root.right && _this2._compareGreaterThan(node.value, root.right.value)) { return _this2._rotateLeft(root); } // Right Left Rotate if (root.right && _this2._compareLessThan(node.value, root.right.value)) { root.right = _this2._rotateRight(root.right); return _this2._rotateLeft(root); } } return root; }; this._root = _insert(this.root); } /** * @public * * @desc Remove a value from the tree * * @param {Item} value - value to remove */ }, { key: "remove", value: function remove(value) { var _this3 = this; /** * @private * * @desc Remove helper * * @param {TreeNode} root * @param {Item} value */ var _remove = function _remove(root, value) { if (!root) return null; if (_this3._compareEqual(root.value, value)) { if (!root.left && !root.right) { return null; } else if (!root.left) { return root.right; } else if (!root.right) { return root.left; } else { var aux = _this3.findMin(root.right); // $FlowFixMe root.value = aux.value; // $FlowFixMe root.right = _remove(root.right, aux.value); } } else if (_this3._compareLessThan(value, root.value)) { root.left = _remove(root.left, value); } else if (_this3._compareGreaterThan(value, root.value)) { root.right = _remove(root.right, value); } var balance = _this3._getBalance(root); if (balance > 1) { // Left Rotate if (root.left && _this3._getBalance(root.left) >= 0) { return _this3._rotateRight(root); } // Left Right Rotate if (root.left && _this3._getBalance(root.left) < 0) { root.left = _this3._rotateLeft(root.left); return _this3._rotateRight(root); } } if (balance < -1) { // Right Rotate if (root.right && _this3._getBalance(root.left) <= 0) { return _this3._rotateLeft(root); } // Right Left Rotate if (root.right && _this3._getBalance(root.left) > 0) { root.right = _this3._rotateRight(root.right); return _this3._rotateLeft(root); } } return root; }; // $FlowFixMe this._root = _remove(this.root, value); } }, { key: "balanced", /** * @public * * @desc Determine if the tree is balanced * * @returns {boolean} balanced */ get: function get$$1() { return Math.abs(this._getBalance(this.root)) <= 1; } }]); return AVLTree; }(BinaryTree); exports.AVLTree = AVLTree; Object.defineProperty(exports, '__esModule', { value: true }); })));