perf: Implemented a high-performance Heap. docs: Software Engineering Standard Form.

This commit is contained in:
Revone 2023-11-17 10:47:13 +08:00
parent 589dcbff35
commit fa92ddfdaa
9 changed files with 222 additions and 211 deletions

View file

@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
## [v1.45.2](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
## [v1.45.3](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
### Changes

136
README.md
View file

@ -71,46 +71,96 @@ const {
} = dataStructureTyped;
```
#### Binary Tree
## Software Engineering Design Standards
<table>
<tr>
<th>Principle</th>
<th>Description</th>
</tr>
<tr>
<td>Practicality</td>
<td>Follows ES6 and ESNext standards, offering unified and considerate optional parameters, and simplifies method names.</td>
</tr>
<tr>
<td>Extensibility</td>
<td>Adheres to OOP (Object-Oriented Programming) principles, allowing inheritance for all data structures.</td>
</tr>
<tr>
<td>Modularization</td>
<td>Includes data structure modularization and independent NPM packages.</td>
</tr>
<tr>
<td>Efficiency</td>
<td>All methods provide time and space complexity, comparable to native JS performance.</td>
</tr>
<tr>
<td>Maintainability</td>
<td>Follows open-source community development standards, complete documentation, continuous integration, and adheres to TDD (Test-Driven Development) patterns.</td>
</tr>
<tr>
<td>Testability</td>
<td>Automated and customized unit testing, performance testing, and integration testing.</td>
</tr>
<tr>
<td>Portability</td>
<td>Plans for porting to Java, Python, and C++, currently achieved to 80%.</td>
</tr>
<tr>
<td>Reusability</td>
<td>Fully decoupled, minimized side effects, and adheres to OOP.</td>
</tr>
<tr>
<td>Security</td>
<td>Carefully designed security for member variables and methods. Data structure software does not need to consider other security aspects.</td>
</tr>
<tr>
<td>Scalability</td>
<td>Data structure software does not involve load issues.</td>
</tr>
</table>
## Vivid Examples
### Binary Tree
[Try it out](https://vivid-algorithm.vercel.app/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/binary-tree-array-to-binary-tree.webp)
#### Binary Tree DFS
### Binary Tree DFS
[Try it out](https://vivid-algorithm.vercel.app/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/binary-tree-dfs-in-order.webp)
#### AVL Tree
### AVL Tree
[Try it out](https://vivid-algorithm.vercel.app/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/avl-tree-test.webp)
#### Tree Multi Map
### Tree Multi Map
[Try it out](https://vivid-algorithm.vercel.app/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/tree-multiset-test.webp)
#### Matrix
### Matrix
[Try it out](https://vivid-algorithm.vercel.app/algorithm/graph/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/matrix-cut-off-tree-for-golf.webp)
#### Directed Graph
### Directed Graph
[Try it out](https://vivid-algorithm.vercel.app/algorithm/graph/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/directed-graph-test.webp)
#### Map Graph
### Map Graph
[Try it out](https://vivid-algorithm.vercel.app/algorithm/graph/), or you can execute your own code using our [visual tool](https://github.com/zrwusa/vivid-algorithm)
![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/map-graph-test.webp)
@ -568,7 +618,7 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', '
### Standard library data structure comparison
## Standard library data structure comparison
<table>
@ -753,9 +803,20 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', '
</table>
## Code design
## Benchmark
### Adhere to ES6 standard naming conventions for APIs.
[//]: # (No deletion!!! Start of Replace Section)
<div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>priority-queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add & pop</td><td>24.00</td><td>41.66</td><td>4.11e-4</td></tr><tr><td>100,000 CPT add & pop</td><td>28.23</td><td>35.43</td><td>8.86e-4</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)
## Codebase design
### Adhere to ES6 and ESNext standard naming conventions for APIs.
Standardize API conventions by using 'add' and 'delete' for element manipulation methods in all data structures.
@ -766,58 +827,3 @@ Opt for concise and clear method names, avoiding excessive length while ensuring
By strictly adhering to object-oriented design (BinaryTree -> BST -> AVLTree -> TreeMultimap), you can seamlessly
inherit the existing data structures to implement the customized ones you need. Object-oriented design stands as the
optimal approach to data structure design.
## Benchmark
[//]: # (No deletion!!! Start of Replace Section)
<div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>comparation</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>SRC 10,000 add</td><td>0.59</td><td>1701.78</td><td>3.28e-5</td></tr><tr><td>CJS 10,000 add</td><td>0.61</td><td>1648.70</td><td>6.98e-5</td></tr><tr><td>MJS 10,000 add</td><td>0.59</td><td>1691.86</td><td>2.44e-5</td></tr><tr><td>SRC PQ 10,000 add & pop</td><td>4.97</td><td>201.19</td><td>1.37e-4</td></tr><tr><td>CJS PQ 10,000 add & pop</td><td>4.93</td><td>202.70</td><td>5.60e-5</td></tr><tr><td>MJS PQ 10,000 add & pop</td><td>4.98</td><td>200.74</td><td>4.39e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>avl-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>32.27</td><td>30.99</td><td>0.00</td></tr><tr><td>10,000 add & delete randomly</td><td>73.67</td><td>13.57</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>41.81</td><td>23.92</td><td>0.00</td></tr><tr><td>10,000 get</td><td>29.21</td><td>34.24</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>binary-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 add randomly</td><td>13.82</td><td>72.38</td><td>0.00</td></tr><tr><td>1,000 add & delete randomly</td><td>16.01</td><td>62.45</td><td>5.80e-4</td></tr><tr><td>1,000 addMany</td><td>12.30</td><td>81.33</td><td>0.01</td></tr><tr><td>1,000 get</td><td>19.75</td><td>50.63</td><td>0.01</td></tr><tr><td>1,000 dfs</td><td>157.12</td><td>6.36</td><td>0.00</td></tr><tr><td>1,000 bfs</td><td>56.72</td><td>17.63</td><td>4.27e-4</td></tr><tr><td>1,000 morris</td><td>334.97</td><td>2.99</td><td>0.03</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>bst</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>28.30</td><td>35.34</td><td>0.00</td></tr><tr><td>10,000 add & delete randomly</td><td>67.47</td><td>14.82</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>29.25</td><td>34.18</td><td>0.00</td></tr><tr><td>10,000 get</td><td>30.53</td><td>32.75</td><td>0.01</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>rb-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>96.67</td><td>10.34</td><td>0.01</td></tr><tr><td>100,000 add & delete randomly</td><td>224.85</td><td>4.45</td><td>0.01</td></tr><tr><td>100,000 getNode</td><td>40.83</td><td>24.49</td><td>2.73e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>directed-graph</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 addVertex</td><td>0.11</td><td>9364.63</td><td>1.25e-5</td></tr><tr><td>1,000 addEdge</td><td>6.18</td><td>161.78</td><td>1.26e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.12e+4</td><td>4.19e-6</td></tr><tr><td>1,000 getEdge</td><td>25.98</td><td>38.50</td><td>0.01</td></tr><tr><td>tarjan</td><td>240.23</td><td>4.16</td><td>0.02</td></tr><tr><td>tarjan all</td><td>227.36</td><td>4.40</td><td>0.00</td></tr><tr><td>topologicalSort</td><td>185.85</td><td>5.38</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>hash-map</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 set</td><td>1.00</td><td>1001.31</td><td>1.82e-5</td></tr><tr><td>10,000 set & get</td><td>1.54</td><td>650.14</td><td>4.87e-5</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>heap</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add & pop</td><td>6.97</td><td>143.49</td><td>5.27e-4</td></tr><tr><td>10,000 fib add & pop</td><td>378.40</td><td>2.64</td><td>0.05</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>doubly-linked-list</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 unshift</td><td>224.55</td><td>4.45</td><td>0.07</td></tr><tr><td>1,000,000 unshift & shift</td><td>182.17</td><td>5.49</td><td>0.06</td></tr><tr><td>1,000,000 insertBefore</td><td>342.63</td><td>2.92</td><td>0.08</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>singly-linked-list</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 push & pop</td><td>222.42</td><td>4.50</td><td>0.01</td></tr><tr><td>10,000 insertBefore</td><td>248.39</td><td>4.03</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>max-priority-queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 refill & poll</td><td>11.83</td><td>84.55</td><td>1.45e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>priority-queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add & pop</td><td>10.79</td><td>92.72</td><td>2.01e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>deque</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>231.33</td><td>4.32</td><td>0.06</td></tr><tr><td>1,000,000 shift</td><td>27.64</td><td>36.17</td><td>0.01</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>46.16</td><td>21.66</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>82.18</td><td>12.17</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>stack</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>45.45</td><td>22.00</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>50.10</td><td>19.96</td><td>0.01</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>trie</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 push</td><td>46.25</td><td>21.62</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>65.17</td><td>15.34</td><td>0.00</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -1,6 +1,6 @@
{
"name": "data-structure-typed",
"version": "1.45.2",
"version": "1.45.3",
"description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.",
"main": "dist/cjs/index.js",
"module": "dist/mjs/index.js",

View file

@ -8,18 +8,18 @@
import type { Comparator, DFSOrderPattern } from '../../types';
export class Heap<E = any> {
constructor(options: { comparator: Comparator<E>; nodes?: E[] }) {
constructor(options: { comparator: Comparator<E>; elements?: E[] }) {
this._comparator = options.comparator;
if (options.nodes && options.nodes.length > 0) {
this._nodes = options.nodes;
if (options.elements && options.elements.length > 0) {
this._elements = options.elements;
this.fix();
}
}
protected _nodes: E[] = [];
protected _elements: E[] = [];
get nodes(): E[] {
return this._nodes;
get elements(): E[] {
return this._elements;
}
protected _comparator: Comparator<E>;
@ -32,7 +32,7 @@ export class Heap<E = any> {
* Get the size (number of elements) of the heap.
*/
get size(): number {
return this.nodes.length;
return this.elements.length;
}
/**
@ -40,25 +40,25 @@ export class Heap<E = any> {
* @returns The last element or undefined if the heap is empty.
*/
get leaf(): E | undefined {
return this.nodes[this.size - 1] ?? undefined;
return this.elements[this.size - 1] ?? undefined;
}
/**
* Static method that creates a binary heap from an array of nodes and a comparison function.
* Static method that creates a binary heap from an array of elements and a comparison function.
* @returns A new Heap instance.
* @param options
*/
static heapify<E>(options: { nodes: E[]; comparator: Comparator<E> }): Heap<E> {
static heapify<E>(options: { elements: E[]; comparator: Comparator<E> }): Heap<E> {
return new Heap<E>(options);
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Insert an element into the heap and maintain the heap properties.
@ -69,56 +69,53 @@ export class Heap<E = any> {
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Insert an element into the heap and maintain the heap properties.
* @param element - The element to be inserted.
*/
push(element: E): Heap<E> {
this.nodes.push(element);
this.bubbleUp(this.nodes.length - 1);
this._elements.push(element);
this._bubbleUp(this.elements.length - 1);
return this;
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
poll(): E | undefined {
if (this.nodes.length === 0) {
return undefined;
if (this.elements.length === 0) return;
const value = this.elements[0];
const last = this.elements.pop()!;
if (this.elements.length) {
this.elements[0] = last;
this._sinkDown(0, this.elements.length >> 1);
}
if (this.nodes.length === 1) {
return this.nodes.pop() as E;
}
const topValue = this.nodes[0];
this.nodes[0] = this.nodes.pop() as E;
this.sinkDown(0);
return topValue;
return value;
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Remove and return the top element (smallest or largest element) from the heap.
@ -133,10 +130,7 @@ export class Heap<E = any> {
* @returns The top element or undefined if the heap is empty.
*/
peek(): E | undefined {
if (this.nodes.length === 0) {
return undefined;
}
return this.nodes[0];
return this.elements[0];
}
/**
@ -148,36 +142,36 @@ export class Heap<E = any> {
}
/**
* Reset the nodes of the heap. Make the nodes empty.
* Reset the elements of the heap. Make the elements empty.
*/
clear() {
this._nodes = [];
this._elements = [];
}
/**
* Time Complexity: O(n), where n is the number of elements in the nodes array.
* Time Complexity: O(n), where n is the number of elements in the elements array.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the nodes array.
* Time Complexity: O(n), where n is the number of elements in the elements array.
* Space Complexity: O(n)
*
* Clear and add nodes of the heap
* @param nodes
* Clear and add elements of the heap
* @param elements
*/
refill(nodes: E[]) {
this._nodes = nodes;
refill(elements: E[]) {
this._elements = elements;
this.fix();
}
/**
* Time Complexity: O(n), where n is the number of nodes in the heap.
* Time Complexity: O(n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n), where n is the number of nodes in the heap.
* Time Complexity: O(n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Use a comparison function to check whether a binary heap contains a specific element.
@ -185,16 +179,47 @@ export class Heap<E = any> {
* @returns Returns true if the specified element is contained; otherwise, returns false.
*/
has(element: E): boolean {
return this.nodes.includes(element);
return this.elements.includes(element);
}
/**
* Time Complexity: O(n), where n is the number of nodes in the heap.
* Time Complexity: O(n). The worst-case O(n), where n is the number of elements in the heap. This is because, in the worst case, the element to be deleted is located at the end of the heap (not the root), and after deletion, we may need to reorganize the elements by performing a sinkDown operation.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n). The worst-case O(n), where n is the number of elements in the heap. This is because, in the worst case, the element to be deleted is located at the end of the heap (not the root), and after deletion, we may need to reorganize the elements by performing a sinkDown operation.
* Space Complexity: O(1)
*
* The `delete` function removes an element from an array-like data structure, maintaining the order
* and structure of the remaining elements.
* @param {E} element - The `element` parameter represents the element that you want to delete from
* the array `this.elements`.
* @returns The `delete` function is returning a boolean value. It returns `true` if the element was
* successfully deleted from the array, and `false` if the element was not found in the array.
*/
delete(element: E) {
const index = this.elements.indexOf(element);
if (index < 0) return false;
if (index === 0) {
this.pop();
} else if (index === this.elements.length - 1) {
this.elements.pop();
} else {
this.elements.splice(index, 1, this.elements.pop()!);
this._bubbleUp(index);
this._sinkDown(index, this.elements.length >> 1);
}
return true;
}
/**
* Time Complexity: O(n), where n is the number of elements in the heap.
* Space Complexity: O(h), where h is the height of the heap.
*/
/**
* Time Complexity: O(n), where n is the number of nodes in the heap.
* Time Complexity: O(n), where n is the number of elements in the heap.
* Space Complexity: O(h), where h is the height of the heap.
*
* Depth-first search (DFS) method, different traversal orders can be selected
@ -209,16 +234,16 @@ export class Heap<E = any> {
if (index < this.size) {
if (order === 'in') {
dfsHelper(2 * index + 1);
result.push(this.nodes[index]);
result.push(this.elements[index]);
dfsHelper(2 * index + 2);
} else if (order === 'pre') {
result.push(this.nodes[index]);
result.push(this.elements[index]);
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
} else if (order === 'post') {
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
result.push(this.nodes[index]);
result.push(this.elements[index]);
}
}
};
@ -241,15 +266,7 @@ export class Heap<E = any> {
* @returns An array containing the elements of the heap.
*/
toArray(): E[] {
return [...this.nodes];
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
*/
getNodes(): E[] {
return this.nodes;
return [...this.elements];
}
/**
@ -266,7 +283,7 @@ export class Heap<E = any> {
*/
clone(): Heap<E> {
const clonedHeap = new Heap<E>({ comparator: this.comparator });
clonedHeap._nodes = [...this.nodes];
clonedHeap._elements = [...this.elements];
return clonedHeap;
}
@ -292,6 +309,21 @@ export class Heap<E = any> {
return visitedNode;
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* Fix the entire heap to maintain heap properties.
*/
fix() {
for (let i = Math.floor(this.size / 2); i >= 0; i--) this._sinkDown(i, this.elements.length >> 1);
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
@ -304,33 +336,20 @@ export class Heap<E = any> {
* Float operation to maintain heap properties after adding an element.
* @param index - The index of the newly added element.
*/
protected bubbleUp(index: number): void {
// const element = this.nodes[index];
// while (index > 0) {
// const parentIndex = (index - 1) >> 1;
// const parent = this.nodes[parentIndex];
// if (this.comparator(element, parent) < 0) {
// this.nodes[index] = parent;
// this.nodes[parentIndex] = element;
// index = parentIndex;
// } else {
// break;
// }
// }
const item = this.nodes[index];
protected _bubbleUp(index: number) {
const element = this.elements[index];
while (index > 0) {
const parent = (index - 1) >> 1;
const parentItem = this.nodes[parent];
if (this.comparator(parentItem, item) <= 0) break;
this.nodes[index] = parentItem;
const parentItem = this.elements[parent];
if (this._comparator(parentItem, element) <= 0) break;
this.elements[index] = parentItem;
index = parent;
}
this.nodes[index] = item;
this.elements[index] = element;
}
/**
* Time Complexity: O(log n)
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
@ -340,41 +359,26 @@ export class Heap<E = any> {
*
* Sinking operation to maintain heap properties after removing the top element.
* @param index - The index from which to start sinking.
* @param halfLength
*/
protected sinkDown(index: number): void {
const leftChildIndex = index << 1 | 1;
const rightChildIndex = leftChildIndex + 1;
const length = this.nodes.length;
let targetIndex = index;
if (leftChildIndex < length && this.comparator(this.nodes[leftChildIndex], this.nodes[targetIndex]) < 0) {
targetIndex = leftChildIndex;
protected _sinkDown(index: number, halfLength: number) {
const element = this.elements[index];
while (index < halfLength) {
let left = index << 1 | 1;
const right = left + 1;
let minItem = this.elements[left];
if (
right < this.elements.length &&
this._comparator(minItem, this.elements[right]) > 0
) {
left = right;
minItem = this.elements[right];
}
if (this._comparator(minItem, element) >= 0) break;
this.elements[index] = minItem;
index = left;
}
if (rightChildIndex < length && this.comparator(this.nodes[rightChildIndex], this.nodes[targetIndex]) < 0) {
targetIndex = rightChildIndex;
}
if (targetIndex !== index) {
const temp = this.nodes[index];
this.nodes[index] = this.nodes[targetIndex];
this.nodes[targetIndex] = temp;
this.sinkDown(targetIndex);
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* Fix the entire heap to maintain heap properties.
*/
protected fix() {
for (let i = Math.floor(this.size / 2); i >= 0; i--) this.sinkDown(i);
this.elements[index] = element;
}
}
@ -500,22 +504,22 @@ export class FibonacciHeap<E> {
}
/**
* Time Complexity: O(n), where n is the number of nodes in the linked list.
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n), where n is the number of nodes in the linked list.
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*
* Get the size (number of elements) of the heap.
* @param {FibonacciHeapNode<E>} head - The head of the linked list.
* @protected
* @returns FibonacciHeapNode<E>[] - An array containing the nodes of the linked list.
* @returns FibonacciHeapNode<E>[] - An array containing the elements of the linked list.
*/
consumeLinkedList(head?: FibonacciHeapNode<E>): FibonacciHeapNode<E>[] {
const nodes: FibonacciHeapNode<E>[] = [];
if (!head) return nodes;
const elements: FibonacciHeapNode<E>[] = [];
if (!head) return elements;
let node: FibonacciHeapNode<E> | undefined = head;
let flag = false;
@ -525,12 +529,12 @@ export class FibonacciHeap<E> {
else if (node === head) flag = true;
if (node) {
nodes.push(node);
elements.push(node);
node = node.right;
}
}
return nodes;
return elements;
}
/**
@ -552,12 +556,12 @@ export class FibonacciHeap<E> {
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Remove and return the top element (smallest or largest element) from the heap.
@ -568,12 +572,12 @@ export class FibonacciHeap<E> {
}
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n), where n is the number of nodes in the heap.
* Time Complexity: O(log n), where n is the number of elements in the heap.
* Space Complexity: O(1)
*
* Remove and return the top element (smallest or largest element) from the heap.
@ -584,8 +588,8 @@ export class FibonacciHeap<E> {
const z = this.min!;
if (z.child) {
const nodes = this.consumeLinkedList(z.child);
for (const node of nodes) {
const elements = this.consumeLinkedList(z.child);
for (const node of elements) {
this.mergeWithRoot(node);
node.parent = undefined;
}
@ -737,12 +741,12 @@ export class FibonacciHeap<E> {
}
/**
* Time Complexity: O(n log n), where n is the number of nodes in the heap.
* Time Complexity: O(n log n), where n is the number of elements in the heap.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n log n), where n is the number of nodes in the heap.
* Time Complexity: O(n log n), where n is the number of elements in the heap.
* Space Complexity: O(n)
*
* Remove and return the top element (smallest or largest element) from the heap.
@ -750,13 +754,13 @@ export class FibonacciHeap<E> {
*/
protected consolidate(): void {
const A: (FibonacciHeapNode<E> | undefined)[] = new Array(this.size);
const nodes = this.consumeLinkedList(this.root);
const elements = this.consumeLinkedList(this.root);
let x: FibonacciHeapNode<E> | undefined,
y: FibonacciHeapNode<E> | undefined,
d: number,
t: FibonacciHeapNode<E> | undefined;
for (const node of nodes) {
for (const node of elements) {
x = node;
d = x.degree;

View file

@ -5,28 +5,28 @@ import { magnitude } from '../../../utils';
import { isCompetitor } from '../../../config';
const suite = new Benchmark.Suite();
const { TEN_THOUSAND } = magnitude;
const { HUNDRED_THOUSAND } = magnitude;
suite.add(`${TEN_THOUSAND.toLocaleString()} add & pop`, () => {
suite.add(`${HUNDRED_THOUSAND.toLocaleString()} add & pop`, () => {
const pq = new PriorityQueue<number>({ comparator: (a, b) => b - a });
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
pq.add(i);
}
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
pq.pop();
}
});
if (isCompetitor) {
suite.add(`${TEN_THOUSAND.toLocaleString()} CPT add & pop`, () => {
suite.add(`${HUNDRED_THOUSAND.toLocaleString()} CPT add & pop`, () => {
const pq = new CPriorityQueue<number>();
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
pq.push(i);
}
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
pq.pop();
}
});

View file

@ -1,6 +1,7 @@
import { FibonacciHeap, MaxHeap, MinHeap } from '../../../../src';
import { logBigOMetricsWrap } from '../../../utils';
describe('Heap Operation Test', () => {
it('should numeric heap work well', function () {
const minNumHeap = new MinHeap<number>();
@ -262,7 +263,7 @@ describe('FibonacciHeap Stress Test', () => {
// const minHeap = new MinHeap<number>();
// const cHeap = new CHeap<number>();
// const cPQ = new PriorityQueue<number>(undefined, (a, b) => a - b);
// const n = 10000;
// const n = 100000;
//
// it('should add performance well', () => {
// const heapCost = calcRunTime(() => {

View file

@ -52,7 +52,7 @@ describe('MaxPriorityQueue Operation Test', () => {
it('should correctly heapify an array', () => {
const array = [5, 3, 7, 1];
const heap = MaxPriorityQueue.heapify<number>({ nodes: array, comparator: (a, b) => b - a });
const heap = MaxPriorityQueue.heapify<number>({ elements: array, comparator: (a, b) => b - a });
heap.refill(array);
expect(heap.poll()).toBe(7);
@ -62,8 +62,8 @@ describe('MaxPriorityQueue Operation Test', () => {
});
it('should correctly heapify an object array', () => {
const nodes = [{ keyA: 5 }, { keyA: 3 }, { keyA: 7 }, { keyA: 1 }];
const maxPQ = MaxPriorityQueue.heapify<{ keyA: number }>({ nodes: nodes, comparator: (a, b) => b.keyA - a.keyA });
const elements = [{ keyA: 5 }, { keyA: 3 }, { keyA: 7 }, { keyA: 1 }];
const maxPQ = MaxPriorityQueue.heapify<{ keyA: number }>({ elements, comparator: (a, b) => b.keyA - a.keyA });
expect(maxPQ.poll()?.keyA).toBe(7);
expect(maxPQ.poll()?.keyA).toBe(5);

View file

@ -15,7 +15,7 @@ describe('PriorityQueue Operation Test', () => {
expect(minPQ.peek()).toBe(4);
expect(
PriorityQueue.heapify({
nodes: [3, 2, 1, 5, 6, 7, 8, 9, 10],
elements: [3, 2, 1, 5, 6, 7, 8, 9, 10],
comparator: (a, b) => a - b
}).toArray()
).toEqual([1, 2, 3, 5, 6, 7, 8, 9, 10]);
@ -32,7 +32,7 @@ describe('PriorityQueue Operation Test', () => {
expect(maxPriorityQueue.peek()).toBe(3);
expect(
PriorityQueue.heapify({
nodes: [3, 2, 1, 5, 6, 7, 8, 9, 10],
elements: [3, 2, 1, 5, 6, 7, 8, 9, 10],
comparator: (a, b) => a - b
}).toArray()
).toEqual([1, 2, 3, 5, 6, 7, 8, 9, 10]);
@ -42,7 +42,7 @@ describe('PriorityQueue Operation Test', () => {
const minPQ1 = new PriorityQueue<number>({ comparator: (a, b) => a - b });
minPQ1.refill([2, 5, 8, 3, 1, 6, 7, 4]);
const clonedPriorityQueue = minPQ1.clone();
expect(clonedPriorityQueue.getNodes()).toEqual(minPQ1.getNodes());
expect(clonedPriorityQueue.elements).toEqual(minPQ1.elements);
expect(clonedPriorityQueue.sort()).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);
expect(minPQ1.dfs('in')).toEqual([4, 3, 2, 5, 1, 8, 6, 7]);
expect(minPQ1.dfs('post')).toEqual([4, 3, 5, 2, 8, 7, 6, 1]);