dastal
Version:
Data Structures & Algorithms implementations
212 lines • 6.82 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SkewHeap = void 0;
const arrayUtils_1 = require("src/utils/arrayUtils");
const binaryTreeUtils_1 = require("src/tree/binaryTreeUtils");
const binaryHeap_1 = require("./binaryHeap");
const utils_1 = require("./utils");
/**
* A skew heap is a heap implemented as a binary tree
* ([source](https://en.wikipedia.org/wiki/Skew_heap)).
*
* A skew heap is a self-adjusting heap which attempts to maintain balance
* by unconditionally swapping all nodes in the merge path when merging two heaps. Every
* operation that modifies the heap (e.g. push, pop, merge) is considered a merge and is done
* by using a skew heap merge.
*
* Skew heaps can merge more quickly than binary heaps. This can seem contradictory, since
* skew heaps have no structural constraints and no guarantee that the height of the tree is
* logarithmic (i.e. balanced). However, amortized complexity analysis can demonstrate that
* all operations on a skew heap can be done in O(log(n). More specifically, the
* amortized complexity is known to be log<sub>φ</sub>(n) where φ is the golden ratio. This is
* approximately 1.44*log<sub>2</sub>(n).
*
* #### Complexity
*
* | Property | Average | Worst |
* | :------- | :------ | :---- |
* | Space | O(n) | O(n)
* | Push | O(log n) | O(log n)
* | Peek | O(1) | O(1)
* | Pop | O(log n) | O(log n)
* | Search | O(n) | O(n)
*/
class SkewHeap {
/**
* Instantiate a heap.
*
* @param compareFn - The function to determine the order of elements.
* @param elements - A set of elements to initialize the heap with.
*/
constructor(compareFn, elements) {
this.compare = compareFn;
this.length = 0;
this.addAll(elements ?? []);
}
addAll(elements) {
if (arrayUtils_1.isArray(elements)) {
for (let i = 0; i < elements.length; ++i) {
this.push(elements[i]);
}
}
else if (elements instanceof SkewHeap || elements instanceof binaryHeap_1.BinaryHeap) {
this.merge(elements);
}
else {
for (const element of elements) {
this.push(element);
}
}
return this.length;
}
clear() {
this.length = 0;
this.root = undefined;
}
comparator() {
return this.compare;
}
contains(element) {
for (const node of binaryTreeUtils_1.preOrderTraverse(this.root)) {
if (element === node.value) {
return true;
}
}
return false;
}
delete(element) {
if (this.root == null) {
return false;
}
if (this.root.value === element) {
this.pop();
return true;
}
for (const par of binaryTreeUtils_1.preOrderTraverse(this.root)) {
const key = par.left && par.left.value === element
? 'left'
: par.right && par.right.value === element
? 'right'
: undefined;
if (key != null) {
const node = par[key];
par[key] = utils_1.skewMerge(this.compare, [node.left, node.right]);
--this.length;
return true;
}
}
return false;
}
merge(heap) {
if (this.compare !== heap.comparator()) {
this.addAll(heap);
}
else if (heap instanceof SkewHeap) {
this.root = utils_1.skewMerge(this.compare, [this.root, binaryTreeUtils_1.clone(heap.root)]);
this.length += heap.size;
}
else if (heap instanceof binaryHeap_1.BinaryHeap) {
this.root = utils_1.skewMerge(this.compare, [this.root, binaryTreeUtils_1.toBinaryTree(heap['array'])]);
this.length += heap.size;
}
else {
this.addAll(heap);
}
return this;
}
peek() {
return this.root?.value;
}
pop() {
if (this.root == null) {
return undefined;
}
const value = this.root.value;
this.root = utils_1.skewMerge(this.compare, [this.root.left, this.root.right]);
--this.length;
return value;
}
push(value) {
this.root = utils_1.skewMerge(this.compare, [this.root, { value }]);
return ++this.length;
}
pushPop(value) {
this.push(value);
return this.pop();
}
replace(value) {
if (this.root == null) {
this.root = { value };
this.length = 1;
return undefined;
}
const out = this.root.value;
this.root = utils_1.skewMerge(this.compare, [this.root.left, this.root.right, { value }]);
return out;
}
get size() {
return this.length;
}
*sorted() {
if (this.root == null) {
return;
}
const heap = new SkewHeap((a, b) => this.compare(a.value, b.value), [this.root]);
do {
const node = heap.pop();
yield node.value;
node.left && heap.push(node.left);
node.right && heap.push(node.right);
} while (heap.size > 0);
}
/**
* Receive an iterator through the list.
*
* **Note:** Unexpected behavior can occur if the collection is modified during iteration.
*
* @returns An iterator through the list
*/
*[Symbol.iterator]() {
for (const node of binaryTreeUtils_1.preOrderTraverse(this.root)) {
yield node.value;
}
}
update(curElement, newElement) {
if (this.root == null) {
return false;
}
if (this.root.value === curElement) {
this.root = utils_1.skewMerge(this.compare, [
this.root.left,
this.root.right,
{ value: newElement },
]);
return true;
}
let node = undefined;
for (const par of binaryTreeUtils_1.preOrderTraverse(this.root)) {
if (par.left && par.left.value === curElement) {
node = par.left;
par.left = undefined;
break;
}
if (par.right && par.right.value === curElement) {
node = par.right;
par.right = undefined;
break;
}
}
if (node == null) {
return false;
}
this.root = utils_1.skewMerge(this.compare, [
this.root,
node.left,
node.right,
{ value: newElement },
]);
return true;
}
}
exports.SkewHeap = SkewHeap;
//# sourceMappingURL=skewHeap.js.map