fix: Fix bug #71.

feat: Define abstract methods through a base class to add clone and isEmpty methods to all data structures. Add delete and deleteAt methods to Deque, Queue, and Stack data structures.
refactor: Optimize the type definition N to NODE for alignment with the design of TREE.
This commit is contained in:
Revone 2024-01-03 15:52:10 +08:00
parent 58ea2cb3c3
commit ecf4d6ad11
32 changed files with 1069 additions and 566 deletions

View file

@ -939,43 +939,43 @@ We strictly adhere to computer science theory and software development standards
[//]: # (No deletion!!! Start of Replace Section)
<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>132.61</td><td>7.54</td><td>0.03</td></tr><tr><td>10,000 add & delete randomly</td><td>179.82</td><td>5.56</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>128.84</td><td>7.76</td><td>7.04e-4</td></tr><tr><td>10,000 get</td><td>48.40</td><td>20.66</td><td>3.34e-4</td></tr></table></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>120.95</td><td>8.27</td><td>0.00</td></tr><tr><td>10,000 add & delete randomly</td><td>180.36</td><td>5.54</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>137.91</td><td>7.25</td><td>0.02</td></tr><tr><td>10,000 get</td><td>49.52</td><td>20.19</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-overall</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 RBTree add</td><td>5.84</td><td>171.12</td><td>8.80e-5</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>16.30</td><td>61.34</td><td>0.01</td></tr><tr><td>10,000 RBTree get</td><td>19.80</td><td>50.50</td><td>0.00</td></tr><tr><td>10,000 AVLTree add</td><td>122.94</td><td>8.13</td><td>0.00</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>185.43</td><td>5.39</td><td>0.00</td></tr><tr><td>10,000 AVLTree get</td><td>0.96</td><td>1044.69</td><td>6.87e-6</td></tr></table></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 RBTree add</td><td>6.17</td><td>162.12</td><td>8.39e-5</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>16.24</td><td>61.59</td><td>7.03e-4</td></tr><tr><td>10,000 RBTree get</td><td>21.72</td><td>46.05</td><td>0.01</td></tr><tr><td>10,000 AVLTree add</td><td>126.30</td><td>7.92</td><td>0.01</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>187.04</td><td>5.35</td><td>0.00</td></tr><tr><td>10,000 AVLTree get</td><td>0.91</td><td>1095.03</td><td>1.07e-5</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>79.39</td><td>12.60</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>211.76</td><td>4.72</td><td>0.00</td></tr><tr><td>100,000 getNode</td><td>169.34</td><td>5.91</td><td>6.62e-4</td></tr><tr><td>100,000 add & iterator</td><td>112.02</td><td>8.93</td><td>0.01</td></tr></table></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>89.08</td><td>11.23</td><td>0.01</td></tr><tr><td>100,000 add & delete randomly</td><td>232.78</td><td>4.30</td><td>0.01</td></tr><tr><td>100,000 getNode</td><td>190.64</td><td>5.25</td><td>0.00</td></tr><tr><td>100,000 add & iterator</td><td>135.29</td><td>7.39</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'>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.10</td><td>9590.36</td><td>1.32e-6</td></tr><tr><td>1,000 addEdge</td><td>6.19</td><td>161.68</td><td>4.32e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.16e+4</td><td>3.75e-7</td></tr><tr><td>1,000 getEdge</td><td>24.72</td><td>40.45</td><td>0.01</td></tr><tr><td>tarjan</td><td>226.08</td><td>4.42</td><td>0.01</td></tr><tr><td>tarjan all</td><td>6667.55</td><td>0.15</td><td>0.27</td></tr><tr><td>topologicalSort</td><td>186.59</td><td>5.36</td><td>0.00</td></tr></table></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>9163.90</td><td>9.52e-6</td></tr><tr><td>1,000 addEdge</td><td>6.11</td><td>163.62</td><td>1.43e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.14e+4</td><td>5.12e-7</td></tr><tr><td>1,000 getEdge</td><td>24.84</td><td>40.26</td><td>0.00</td></tr><tr><td>tarjan</td><td>236.10</td><td>4.24</td><td>0.02</td></tr><tr><td>tarjan all</td><td>6707.73</td><td>0.15</td><td>0.11</td></tr><tr><td>topologicalSort</td><td>192.62</td><td>5.19</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'>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>1,000,000 set</td><td>137.00</td><td>7.30</td><td>0.08</td></tr><tr><td>Native Map 1,000,000 set</td><td>236.58</td><td>4.23</td><td>0.05</td></tr><tr><td>Native Set 1,000,000 add</td><td>187.78</td><td>5.33</td><td>0.05</td></tr><tr><td>1,000,000 set & get</td><td>123.91</td><td>8.07</td><td>0.04</td></tr><tr><td>Native Map 1,000,000 set & get</td><td>286.03</td><td>3.50</td><td>0.03</td></tr><tr><td>Native Set 1,000,000 add & has</td><td>188.67</td><td>5.30</td><td>0.03</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>327.70</td><td>3.05</td><td>0.05</td></tr><tr><td>Native Map 1,000,000 ObjKey set & get</td><td>285.22</td><td>3.51</td><td>0.05</td></tr><tr><td>Native Set 1,000,000 ObjKey add & has</td><td>278.08</td><td>3.60</td><td>0.07</td></tr></table></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 set</td><td>123.32</td><td>8.11</td><td>0.05</td></tr><tr><td>Native Map 1,000,000 set</td><td>232.46</td><td>4.30</td><td>0.03</td></tr><tr><td>Native Set 1,000,000 add</td><td>190.54</td><td>5.25</td><td>0.05</td></tr><tr><td>1,000,000 set & get</td><td>118.71</td><td>8.42</td><td>0.03</td></tr><tr><td>Native Map 1,000,000 set & get</td><td>294.15</td><td>3.40</td><td>0.04</td></tr><tr><td>Native Set 1,000,000 add & has</td><td>182.79</td><td>5.47</td><td>0.02</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>331.70</td><td>3.01</td><td>0.06</td></tr><tr><td>Native Map 1,000,000 ObjKey set & get</td><td>282.44</td><td>3.54</td><td>0.06</td></tr><tr><td>Native Set 1,000,000 ObjKey add & has</td><td>290.19</td><td>3.45</td><td>0.07</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>100,000 add & poll</td><td>23.99</td><td>41.68</td><td>0.00</td></tr><tr><td>100,000 add & dfs</td><td>33.23</td><td>30.09</td><td>0.00</td></tr><tr><td>10,000 fib add & pop</td><td>358.16</td><td>2.79</td><td>0.00</td></tr></table></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 & poll</td><td>24.36</td><td>41.06</td><td>0.00</td></tr><tr><td>100,000 add & dfs</td><td>32.10</td><td>31.15</td><td>3.40e-4</td></tr><tr><td>10,000 fib add & pop</td><td>354.32</td><td>2.82</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'>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 push</td><td>229.07</td><td>4.37</td><td>0.06</td></tr><tr><td>1,000,000 unshift</td><td>217.64</td><td>4.59</td><td>0.08</td></tr><tr><td>1,000,000 unshift & shift</td><td>175.13</td><td>5.71</td><td>0.04</td></tr><tr><td>1,000,000 addBefore</td><td>342.22</td><td>2.92</td><td>0.08</td></tr></table></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>215.49</td><td>4.64</td><td>0.02</td></tr><tr><td>1,000,000 unshift</td><td>229.17</td><td>4.36</td><td>0.05</td></tr><tr><td>1,000,000 unshift & shift</td><td>182.41</td><td>5.48</td><td>0.04</td></tr><tr><td>1,000,000 addBefore</td><td>313.24</td><td>3.19</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'>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>1,000,000 push & shift</td><td>210.65</td><td>4.75</td><td>0.06</td></tr><tr><td>10,000 push & pop</td><td>214.54</td><td>4.66</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>248.45</td><td>4.02</td><td>0.01</td></tr></table></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 & shift</td><td>209.03</td><td>4.78</td><td>0.05</td></tr><tr><td>10,000 push & pop</td><td>217.38</td><td>4.60</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>245.42</td><td>4.07</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'>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 & poll</td><td>75.67</td><td>13.22</td><td>0.00</td></tr></table></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 & poll</td><td>75.48</td><td>13.25</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'>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>13.14</td><td>76.13</td><td>1.36e-4</td></tr><tr><td>10,000 push & delete</td><td>4716.79</td><td>0.21</td><td>0.13</td></tr><tr><td>1,000,000 push & pop</td><td>22.38</td><td>44.68</td><td>0.00</td></tr><tr><td>100,000 push & shift</td><td>2.15</td><td>464.20</td><td>1.98e-5</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2241.30</td><td>0.45</td><td>0.14</td></tr><tr><td>100,000 unshift & shift</td><td>2.34</td><td>426.69</td><td>0.00</td></tr><tr><td>Native Array 100,000 unshift & shift</td><td>3971.32</td><td>0.25</td><td>0.18</td></tr></table></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>13.69</td><td>73.07</td><td>0.00</td></tr><tr><td>10,000 push & delete</td><td>4928.03</td><td>0.20</td><td>0.41</td></tr><tr><td>1,000,000 push & pop</td><td>25.16</td><td>39.74</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>3.12</td><td>320.28</td><td>0.00</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2216.18</td><td>0.45</td><td>0.08</td></tr><tr><td>100,000 unshift & shift</td><td>2.17</td><td>461.58</td><td>4.01e-4</td></tr><tr><td>Native Array 100,000 unshift & shift</td><td>4474.58</td><td>0.22</td><td>0.29</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>44.80</td><td>22.32</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>4.91</td><td>203.64</td><td>1.15e-4</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2116.78</td><td>0.47</td><td>0.12</td></tr><tr><td>Native Array 100,000 push & pop</td><td>4.30</td><td>232.29</td><td>9.32e-5</td></tr></table></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>49.42</td><td>20.23</td><td>0.02</td></tr><tr><td>100,000 push & shift</td><td>5.21</td><td>191.82</td><td>9.75e-4</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2328.85</td><td>0.43</td><td>0.25</td></tr><tr><td>Native Array 100,000 push & pop</td><td>4.44</td><td>225.24</td><td>1.83e-4</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>42.15</td><td>23.72</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>52.90</td><td>18.90</td><td>0.02</td></tr></table></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.54</td><td>21.96</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>50.59</td><td>19.77</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>44.55</td><td>22.45</td><td>8.46e-4</td></tr><tr><td>100,000 getWords</td><td>87.48</td><td>11.43</td><td>0.00</td></tr></table></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>47.74</td><td>20.95</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>94.40</td><td>10.59</td><td>0.01</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -287,10 +287,14 @@ export abstract class IterableEntryBase<K = any, V = any> {
console.log([...this]);
}
abstract isEmpty(): boolean;
abstract clone(): any;
protected abstract _getIterator(...args: any[]): IterableIterator<[K, V]>;
}
export abstract class IterableElementBase<E> {
export abstract class IterableElementBase<E = any, C = any> {
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
@ -491,5 +495,9 @@ export abstract class IterableElementBase<E> {
console.log([...this]);
}
abstract isEmpty(): boolean;
abstract clone(): C;
protected abstract _getIterator(...args: any[]): IterableIterator<E>;
}

View file

@ -17,11 +17,11 @@ import type {
} from '../../types';
import { IBinaryTree } from '../../interfaces';
export class AVLTreeNode<K = any, V = any, N extends AVLTreeNode<K, V, N> = AVLTreeNodeNested<K, V>> extends BSTNode<
K,
V,
N
> {
export class AVLTreeNode<
K = any,
V = any,
NODE extends AVLTreeNode<K, V, NODE> = AVLTreeNodeNested<K, V>
> extends BSTNode<K, V, NODE> {
height: number;
constructor(key: K, value?: V) {
@ -42,21 +42,21 @@ export class AVLTreeNode<K = any, V = any, N extends AVLTreeNode<K, V, N> = AVLT
export class AVLTree<
K = any,
V = any,
N extends AVLTreeNode<K, V, N> = AVLTreeNode<K, V, AVLTreeNodeNested<K, V>>,
TREE extends AVLTree<K, V, N, TREE> = AVLTree<K, V, N, AVLTreeNested<K, V, N>>
NODE extends AVLTreeNode<K, V, NODE> = AVLTreeNode<K, V, AVLTreeNodeNested<K, V>>,
TREE extends AVLTree<K, V, NODE, TREE> = AVLTree<K, V, NODE, AVLTreeNested<K, V, NODE>>
>
extends BST<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
extends BST<K, V, NODE, TREE>
implements IBinaryTree<K, V, NODE, TREE> {
/**
* The constructor function initializes an AVLTree object with optional keysOrNodesOrEntries and options.
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, NODE>`
* objects. It represents a collection of nodes that will be added to the AVL tree during
* initialization.
* @param [options] - The `options` parameter is an optional object that allows you to customize the
* behavior of the AVL tree. It is of type `Partial<AVLTreeOptions>`, which means that you can
* provide only a subset of the properties defined in the `AVLTreeOptions` interface.
*/
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: AVLTreeOptions<K>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>> = [], options?: AVLTreeOptions<K>) {
super([], options);
if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries);
}
@ -67,11 +67,11 @@ export class AVLTree<
* the new node. It is used to determine the position of the node in the binary search tree.
* @param [value] - The parameter `value` is an optional value that can be assigned to the node. It is of
* type `V`, which means it can be any value that is assignable to the `value` property of the
* node type `N`.
* node type `NODE`.
* @returns a new AVLTreeNode object with the specified key and value.
*/
override createNode(key: K, value?: V): N {
return new AVLTreeNode<K, V, N>(key, value) as N;
override createNode(key: K, value?: V): NODE {
return new AVLTreeNode<K, V, NODE>(key, value) as NODE;
}
/**
@ -82,7 +82,7 @@ export class AVLTree<
* @returns a new AVLTree object.
*/
override createTree(options?: AVLTreeOptions<K>): TREE {
return new AVLTree<K, V, N, TREE>([], {
return new AVLTree<K, V, NODE, TREE>([], {
iterationType: this.iterationType,
variant: this.variant,
...options
@ -91,10 +91,10 @@ export class AVLTree<
/**
* The function checks if an keyOrNodeOrEntry is an instance of AVLTreeNode.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`.
* @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the AVLTreeNode class.
*/
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>): keyOrNodeOrEntry is N {
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>): keyOrNodeOrEntry is NODE {
return keyOrNodeOrEntry instanceof AVLTreeNode;
}
@ -116,7 +116,7 @@ export class AVLTree<
* being added to the binary tree.
* @returns The method is returning either the inserted node or undefined.
*/
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V): boolean {
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V): boolean {
if (keyOrNodeOrEntry === null) return false;
const inserted = super.add(keyOrNodeOrEntry, value);
if (inserted) this._balancePath(keyOrNodeOrEntry);
@ -140,13 +140,13 @@ export class AVLTree<
* @param {C} callback - The `callback` parameter is a function that will be called for each node
* that is deleted from the binary tree. It is an optional parameter and if not provided, it will
* default to the `_defaultOneParamCallback` function. The `callback` function should have a single
* parameter of type `N
* @returns The method is returning an array of `BinaryTreeDeleteResult<N>`.
* parameter of type `NODE
* @returns The method is returning an array of `BinaryTreeDeleteResult<NODE>`.
*/
override delete<C extends BTNCallback<N>>(
override delete<C extends BTNCallback<NODE>>(
identifier: ReturnType<C>,
callback: C = this._defaultOneParamCallback as C
): BinaryTreeDeleteResult<N>[] {
): BinaryTreeDeleteResult<NODE>[] {
if ((identifier as any) instanceof AVLTreeNode) callback = (node => node) as C;
const deletedResults = super.delete(identifier, callback);
for (const { needBalanced } of deletedResults) {
@ -160,14 +160,17 @@ export class AVLTree<
/**
* The `_swapProperties` function swaps the key, value, and height properties between two nodes in a binary
* tree.
* @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node that
* needs to be swapped with the destination node. It can be of type `K`, `N`, or `undefined`.
* @param {K | N | undefined} destNode - The `destNode` parameter represents the destination
* @param {K | NODE | undefined} srcNode - The `srcNode` parameter represents the source node that
* needs to be swapped with the destination node. It can be of type `K`, `NODE`, or `undefined`.
* @param {K | NODE | undefined} destNode - The `destNode` parameter represents the destination
* node where the values from the source node will be swapped to.
* @returns either the `destNode` object if both `srcNode` and `destNode` are defined, or `undefined`
* if either `srcNode` or `destNode` is undefined.
*/
protected override _swapProperties(srcNode: BSTNKeyOrNode<K, N>, destNode: BSTNKeyOrNode<K, N>): N | undefined {
protected override _swapProperties(
srcNode: BSTNKeyOrNode<K, NODE>,
destNode: BSTNKeyOrNode<K, NODE>
): NODE | undefined {
srcNode = this.ensureNode(srcNode);
destNode = this.ensureNode(destNode);
@ -203,11 +206,11 @@ export class AVLTree<
* Space Complexity: O(1)
*
* The function calculates the balance factor of a node in a binary tree.
* @param {N} node - The parameter "node" represents a node in a binary tree data structure.
* @param {NODE} node - The parameter "node" represents a node in a binary tree data structure.
* @returns the balance factor of a given node. The balance factor is calculated by subtracting the
* height of the left subtree from the height of the right subtree.
*/
protected _balanceFactor(node: N): number {
protected _balanceFactor(node: NODE): number {
if (!node.right)
// node has no right subtree
return -node.height;
@ -229,9 +232,9 @@ export class AVLTree<
*
* The function updates the height of a node in a binary tree based on the heights of its left and
* right children.
* @param {N} node - The parameter "node" represents a node in a binary tree data structure.
* @param {NODE} node - The parameter "node" represents a node in a binary tree data structure.
*/
protected _updateHeight(node: N): void {
protected _updateHeight(node: NODE): void {
if (!node.left && !node.right) node.height = 0;
else if (!node.left) {
const rightHeight = node.right ? node.right.height : 0;
@ -252,10 +255,10 @@ export class AVLTree<
*
* The `_balancePath` function is used to update the heights of nodes and perform rotation operations
* to restore balance in an AVL tree after inserting a node.
* @param {N} node - The `node` parameter in the `_balancePath` function represents the node in the
* @param {NODE} node - The `node` parameter in the `_balancePath` function represents the node in the
* AVL tree that needs to be balanced.
*/
protected _balancePath(node: KeyOrNodeOrEntry<K, V, N>): void {
protected _balancePath(node: KeyOrNodeOrEntry<K, V, NODE>): void {
node = this.ensureNode(node);
const path = this.getPathToRoot(node, false); // first O(log n) + O(log n)
for (let i = 0; i < path.length; i++) {
@ -306,9 +309,9 @@ export class AVLTree<
* Space Complexity: O(1)
*
* The function `_balanceLL` performs a left-left rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
* @param {NODE} A - A is a node in a binary tree.
*/
protected _balanceLL(A: N): void {
protected _balanceLL(A: NODE): void {
const parentOfA = A.parent;
const B = A.left;
A.parent = B;
@ -344,9 +347,9 @@ export class AVLTree<
* Space Complexity: O(1)
*
* The `_balanceLR` function performs a left-right rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
* @param {NODE} A - A is a node in a binary tree.
*/
protected _balanceLR(A: N): void {
protected _balanceLR(A: NODE): void {
const parentOfA = A.parent;
const B = A.left;
let C = undefined;
@ -400,9 +403,9 @@ export class AVLTree<
* Space Complexity: O(1)
*
* The function `_balanceRR` performs a right-right rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
* @param {NODE} A - A is a node in a binary tree.
*/
protected _balanceRR(A: N): void {
protected _balanceRR(A: NODE): void {
const parentOfA = A.parent;
const B = A.right;
A.parent = B;
@ -443,9 +446,9 @@ export class AVLTree<
* Space Complexity: O(1)
*
* The function `_balanceRL` performs a right-left rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
* @param {NODE} A - A is a node in a binary tree.
*/
protected _balanceRL(A: N): void {
protected _balanceRL(A: NODE): void {
const parentOfA = A.parent;
const B = A.right;
let C = undefined;
@ -488,7 +491,7 @@ export class AVLTree<
C && this._updateHeight(C);
}
protected _replaceNode(oldNode: N, newNode: N): N {
protected _replaceNode(oldNode: NODE, newNode: NODE): NODE {
newNode.height = oldNode.height;
return super._replaceNode(oldNode, newNode);

File diff suppressed because it is too large Load diff

View file

@ -18,12 +18,12 @@ import { BinaryTree, BinaryTreeNode } from './binary-tree';
import { IBinaryTree } from '../../interfaces';
import { Queue } from '../queue';
export class BSTNode<K = any, V = any, N extends BSTNode<K, V, N> = BSTNodeNested<K, V>> extends BinaryTreeNode<
export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNodeNested<K, V>> extends BinaryTreeNode<
K,
V,
N
NODE
> {
override parent?: N;
override parent?: NODE;
constructor(key: K, value?: V) {
super(key, value);
@ -32,42 +32,42 @@ export class BSTNode<K = any, V = any, N extends BSTNode<K, V, N> = BSTNodeNeste
this._right = undefined;
}
protected override _left?: N;
protected override _left?: NODE;
/**
* Get the left child node.
*/
override get left(): N | undefined {
override get left(): NODE | undefined {
return this._left;
}
/**
* Set the left child node.
* @param {N | undefined} v - The left child node.
* @param {NODE | undefined} v - The left child node.
*/
override set left(v: N | undefined) {
override set left(v: NODE | undefined) {
if (v) {
v.parent = this as unknown as N;
v.parent = this as unknown as NODE;
}
this._left = v;
}
protected override _right?: N;
protected override _right?: NODE;
/**
* Get the right child node.
*/
override get right(): N | undefined {
override get right(): NODE | undefined {
return this._right;
}
/**
* Set the right child node.
* @param {N | undefined} v - The right child node.
* @param {NODE | undefined} v - The right child node.
*/
override set right(v: N | undefined) {
override set right(v: NODE | undefined) {
if (v) {
v.parent = this as unknown as N;
v.parent = this as unknown as NODE;
}
this._right = v;
}
@ -85,11 +85,11 @@ export class BSTNode<K = any, V = any, N extends BSTNode<K, V, N> = BSTNodeNeste
export class BST<
K = any,
V = any,
N extends BSTNode<K, V, N> = BSTNode<K, V, BSTNodeNested<K, V>>,
TREE extends BST<K, V, N, TREE> = BST<K, V, N, BSTNested<K, V, N>>
NODE extends BSTNode<K, V, NODE> = BSTNode<K, V, BSTNodeNested<K, V>>,
TREE extends BST<K, V, NODE, TREE> = BST<K, V, NODE, BSTNested<K, V, NODE>>
>
extends BinaryTree<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
extends BinaryTree<K, V, NODE, TREE>
implements IBinaryTree<K, V, NODE, TREE> {
/**
* This is the constructor function for a binary search tree class in TypeScript, which initializes
* the tree with optional keysOrNodesOrEntries and options.
@ -98,7 +98,7 @@ export class BST<
* @param [options] - The `options` parameter is an optional object that can contain additional
* configuration options for the binary search tree. It can have the following properties:
*/
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: BSTOptions<K>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>> = [], options?: BSTOptions<K>) {
super([], options);
if (options) {
@ -111,9 +111,9 @@ export class BST<
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);
}
protected override _root?: N;
protected override _root?: NODE;
override get root(): N | undefined {
override get root(): NODE | undefined {
return this._root;
}
@ -131,8 +131,8 @@ export class BST<
* represents the value associated with the node in a binary search tree.
* @returns a new instance of the BSTNode class with the specified key and value.
*/
override createNode(key: K, value?: V): N {
return new BSTNode<K, V, N>(key, value) as N;
override createNode(key: K, value?: V): NODE {
return new BSTNode<K, V, NODE>(key, value) as NODE;
}
/**
@ -143,7 +143,7 @@ export class BST<
* @returns a new instance of the BST class with the specified options.
*/
override createTree(options?: Partial<BSTOptions<K>>): TREE {
return new BST<K, V, N, TREE>([], {
return new BST<K, V, NODE, TREE>([], {
iterationType: this.iterationType,
variant: this.variant,
...options
@ -153,13 +153,13 @@ export class BST<
/**
* The function `keyValueOrEntryToNode` takes an keyOrNodeOrEntry and returns a node if the keyOrNodeOrEntry is valid,
* otherwise it returns undefined.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`, where:
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`, where:
* @param {V} [value] - The `value` parameter is an optional value that can be passed to the
* `keyValueOrEntryToNode` function. It represents the value associated with the keyOrNodeOrEntry node.
* @returns a node of type N or undefined.
* @returns a node of type NODE or undefined.
*/
override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V): N | undefined {
let node: N | undefined;
override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V): NODE | undefined {
let node: NODE | undefined;
if (keyOrNodeOrEntry === null || keyOrNodeOrEntry === undefined) {
return;
} else if (this.isNode(keyOrNodeOrEntry)) {
@ -191,17 +191,17 @@ export class BST<
*
* The function `ensureNode` returns the node corresponding to the given key if it is a node key,
* otherwise it returns the key itself.
* @param {K | N | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `N`, or
* @param {K | NODE | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `NODE`, or
* `undefined`.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to be performed. It has a default value of `IterationType.ITERATIVE`.
* @returns either a node object (N) or undefined.
* @returns either a node object (NODE) or undefined.
*/
override ensureNode(
keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>,
keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>,
iterationType = IterationType.ITERATIVE
): N | undefined {
let res: N | undefined;
): NODE | undefined {
let res: NODE | undefined;
if (this.isRealNode(keyOrNodeOrEntry)) {
res = keyOrNodeOrEntry;
} else if (this.isEntry(keyOrNodeOrEntry)) {
@ -214,10 +214,10 @@ export class BST<
/**
* The function checks if an keyOrNodeOrEntry is an instance of BSTNode.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry<K, V, N>`.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry<K, V, NODE>`.
* @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the BSTNode class.
*/
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>): keyOrNodeOrEntry is N {
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>): keyOrNodeOrEntry is NODE {
return keyOrNodeOrEntry instanceof BSTNode;
}
@ -239,7 +239,7 @@ export class BST<
* @returns The method `add` returns either the newly added node (`newNode`) or `undefined` if the
* node was not added.
*/
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V): boolean {
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V): boolean {
const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value);
if (newNode === undefined) return false;
@ -309,10 +309,10 @@ export class BST<
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to use when adding multiple keys or nodes. It has a default value of
* `this.iterationType`, which suggests that it is a property of the current object.
* @returns The function `addMany` returns an array of nodes (`N`) or `undefined` values.
* @returns The function `addMany` returns an array of nodes (`NODE`) or `undefined` values.
*/
override addMany(
keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>>,
keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>>,
values?: Iterable<V | undefined>,
isBalanceAdd = true,
iterationType = this.iterationType
@ -334,9 +334,9 @@ export class BST<
return inserted;
}
const realBTNExemplars: BTNodePureExemplar<K, V, N>[] = [];
const realBTNExemplars: BTNodePureExemplar<K, V, NODE>[] = [];
const isRealBTNExemplar = (kve: KeyOrNodeOrEntry<K, V, N>): kve is BTNodePureExemplar<K, V, N> => {
const isRealBTNExemplar = (kve: KeyOrNodeOrEntry<K, V, NODE>): kve is BTNodePureExemplar<K, V, NODE> => {
if (kve === undefined || kve === null) return false;
return !(this.isEntry(kve) && (kve[0] === undefined || kve[0] === null));
};
@ -345,7 +345,7 @@ export class BST<
isRealBTNExemplar(kve) && realBTNExemplars.push(kve);
}
let sorted: BTNodePureExemplar<K, V, N>[] = [];
let sorted: BTNodePureExemplar<K, V, NODE>[] = [];
sorted = realBTNExemplars.sort((a, b) => {
let aR: number, bR: number;
@ -360,7 +360,7 @@ export class BST<
return aR - bR;
});
const _dfs = (arr: BTNodePureExemplar<K, V, N>[]) => {
const _dfs = (arr: BTNodePureExemplar<K, V, NODE>[]) => {
if (arr.length === 0) return;
const mid = Math.floor((arr.length - 1) / 2);
@ -413,13 +413,13 @@ export class BST<
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to use when searching for a node in the binary tree. It can have two possible
* values:
* @returns The function `getNodeByKey` returns a node (`N`) if a node with the specified key is
* @returns The function `getNodeByKey` returns a node (`NODE`) if a node with the specified key is
* found in the binary tree. If no node is found, it returns `undefined`.
*/
override getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): N | undefined {
override getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): NODE | undefined {
if (!this.root) return undefined;
if (iterationType === IterationType.RECURSIVE) {
const _dfs = (cur: N): N | undefined => {
const _dfs = (cur: NODE): NODE | undefined => {
if (cur.key === key) return cur;
if (!cur.left && !cur.right) return;
@ -429,7 +429,7 @@ export class BST<
return _dfs(this.root);
} else {
const queue = new Queue<N>([this.root]);
const queue = new Queue<NODE>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
@ -456,33 +456,33 @@ export class BST<
* @param {ReturnType<C> | undefined} identifier - The `identifier` parameter is the value that you
* want to search for in the nodes of the binary tree. It can be of any type that is returned by the
* callback function `C`.
* @param {C} callback - The `callback` parameter is a function that takes a node of type `N` as its
* @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` as its
* argument and returns a value of type `ReturnType<C>`. The `C` type parameter represents a callback
* function type that extends the `BTNCallback<N>` type. The `BTNCallback<N>` type is
* function type that extends the `BTNCallback<NODE>` type. The `BTNCallback<NODE>` type is
* @param [onlyOne=false] - A boolean flag indicating whether to stop searching after finding the
* first node that matches the identifier. If set to true, the function will return an array
* containing only the first matching node. If set to false (default), the function will continue
* searching for all nodes that match the identifier and return an array containing
* @param {K | N | undefined} beginRoot - The `beginRoot` parameter represents the starting node
* @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter represents the starting node
* for the traversal. It can be either a key value or a node object. If it is undefined, the
* traversal will start from the root of the tree.
* @param iterationType - The `iterationType` parameter determines the type of iteration to be
* performed on the binary tree. It can have two possible values:
* @returns The method returns an array of nodes (`N[]`).
* @returns The method returns an array of nodes (`NODE[]`).
*/
override getNodes<C extends BTNCallback<N>>(
override getNodes<C extends BTNCallback<NODE>>(
identifier: ReturnType<C> | undefined,
callback: C = this._defaultOneParamCallback as C,
onlyOne = false,
beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root,
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): N[] {
): NODE[] {
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return [];
const ans: N[] = [];
const ans: NODE[] = [];
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
const _traverse = (cur: NODE) => {
const callbackResult = callback(cur);
if (callbackResult === identifier) {
ans.push(cur);
@ -502,7 +502,7 @@ export class BST<
_traverse(beginRoot);
} else {
const queue = new Queue<N>([beginRoot]);
const queue = new Queue<NODE>([beginRoot]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
@ -530,7 +530,7 @@ export class BST<
// * The function overrides the subTreeTraverse method and returns the result of calling the super
// * method with the provided arguments.
// * @param {C} callback - The `callback` parameter is a function that will be called for each node in
// * the subtree traversal. It should accept a single parameter of type `N`, which represents a node in
// * the subtree traversal. It should accept a single parameter of type `NODE`, which represents a node in
// * the tree. The return type of the callback function can be any type.
// * @param beginRoot - The `beginRoot` parameter is the starting point for traversing the subtree. It
// * can be either a key, a node, or an entry.
@ -538,9 +538,9 @@ export class BST<
// * be performed during the traversal of the subtree. It can have one of the following values:
// * @returns The method is returning an array of the return type of the callback function.
// */
// override subTreeTraverse<C extends BTNCallback<N>>(
// override subTreeTraverse<C extends BTNCallback<NODE>>(
// callback: C = this._defaultOneParamCallback as C,
// beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root,
// beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
// iterationType = this.iterationType
// ): ReturnType<C>[] {
// return super.subTreeTraverse(callback, beginRoot, iterationType, false);
@ -570,10 +570,10 @@ export class BST<
* following values:
* @returns The method is returning an array of the return type of the callback function.
*/
override dfs<C extends BTNCallback<N>>(
override dfs<C extends BTNCallback<NODE>>(
callback: C = this._defaultOneParamCallback as C,
pattern: DFSOrderPattern = 'in',
beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root,
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType: IterationType = IterationType.ITERATIVE
): ReturnType<C>[] {
return super.dfs(callback, pattern, beginRoot, iterationType, false);
@ -601,9 +601,9 @@ export class BST<
* nodes are visited.
* @returns The method is returning an array of the return type of the callback function.
*/
override bfs<C extends BTNCallback<N>>(
override bfs<C extends BTNCallback<NODE>>(
callback: C = this._defaultOneParamCallback as C,
beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root,
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): ReturnType<C>[] {
return super.bfs(callback, beginRoot, iterationType, false);
@ -621,7 +621,7 @@ export class BST<
* The function overrides the listLevels method and returns an array of arrays containing the return
* type of the callback function for each level of the tree.
* @param {C} callback - The `callback` parameter is a generic type `C` that extends
* `BTNCallback<N>`. It represents a callback function that will be called for each node in the tree
* `BTNCallback<NODE>`. It represents a callback function that will be called for each node in the tree
* during the level listing process.
* @param beginRoot - The `beginRoot` parameter is used to specify the starting point for listing the
* levels of a binary tree. It can be either a key, a node, or an entry in the binary tree. If not
@ -632,9 +632,9 @@ export class BST<
* @returns The method is returning a two-dimensional array of the return type of the callback
* function.
*/
override listLevels<C extends BTNCallback<N>>(
override listLevels<C extends BTNCallback<NODE>>(
callback: C = this._defaultOneParamCallback as C,
beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root,
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): ReturnType<C>[][] {
return super.listLevels(callback, beginRoot, iterationType, false);
@ -652,14 +652,14 @@ export class BST<
*
* The `lastKey` function returns the key of the rightmost node in a binary tree, or the key of the
* leftmost node if the comparison result is greater than.
* @param {K | N | undefined} beginRoot - The `beginRoot` parameter is optional and can be of
* type `K`, `N`, or `undefined`. It represents the starting point for finding the last key in
* @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter is optional and can be of
* type `K`, `NODE`, or `undefined`. It represents the starting point for finding the last key in
* the binary tree. If not provided, it defaults to the root of the binary tree (`this.root`).
* @returns the key of the rightmost node in the binary tree if the comparison result is less than,
* the key of the leftmost node if the comparison result is greater than, and the key of the
* rightmost node otherwise. If no node is found, it returns 0.
*/
lastKey(beginRoot: KeyOrNodeOrEntry<K, V, N> = this.root): K | undefined {
lastKey(beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root): K | undefined {
let current = this.ensureNode(beginRoot);
if (!current) return undefined;
@ -691,12 +691,12 @@ export class BST<
* are either lesser or greater than a target node, depending on the specified comparison type.
* @param {C} callback - The `callback` parameter is a function that will be called for each node
* that satisfies the condition specified by the `lesserOrGreater` parameter. It takes a single
* parameter of type `N` (the node type) and returns a value of any type.
* parameter of type `NODE` (the node type) and returns a value of any type.
* @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to
* traverse nodes that are lesser than, greater than, or equal to the `targetNode`. It is of type
* `CP`, which is a custom type representing the comparison operator. The possible values for
* `lesserOrGreater` are
* @param {K | N | undefined} targetNode - The `targetNode` parameter represents the node in the
* @param {K | NODE | undefined} targetNode - The `targetNode` parameter represents the node in the
* binary tree that you want to traverse from. It can be specified either by its key, by the node
* object itself, or it can be left undefined to start the traversal from the root of the tree.
* @param iterationType - The `iterationType` parameter determines the type of traversal to be
@ -704,21 +704,21 @@ export class BST<
* @returns The function `lesserOrGreaterTraverse` returns an array of values of type
* `ReturnType<C>`, which is the return type of the callback function passed as an argument.
*/
lesserOrGreaterTraverse<C extends BTNCallback<N>>(
lesserOrGreaterTraverse<C extends BTNCallback<NODE>>(
callback: C = this._defaultOneParamCallback as C,
lesserOrGreater: CP = CP.lt,
targetNode: KeyOrNodeOrEntry<K, V, N> = this.root,
targetNode: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): ReturnType<C>[] {
targetNode = this.ensureNode(targetNode);
const ans: ReturnType<BTNCallback<N>>[] = [];
const ans: ReturnType<BTNCallback<NODE>>[] = [];
if (!targetNode) return ans;
if (!this.root) return ans;
const targetKey = targetNode.key;
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
const _traverse = (cur: NODE) => {
const compared = this._compare(cur.key, targetKey);
if (compared === lesserOrGreater) ans.push(callback(cur));
@ -730,7 +730,7 @@ export class BST<
_traverse(this.root);
return ans;
} else {
const queue = new Queue<N>([this.root]);
const queue = new Queue<NODE>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
@ -828,7 +828,7 @@ export class BST<
let balanced = true;
if (iterationType === IterationType.RECURSIVE) {
const _height = (cur: N | undefined): number => {
const _height = (cur: NODE | undefined): number => {
if (!cur) return 0;
const leftHeight = _height(cur.left),
rightHeight = _height(cur.right);
@ -837,10 +837,10 @@ export class BST<
};
_height(this.root);
} else {
const stack: N[] = [];
let node: N | undefined = this.root,
last: N | undefined = undefined;
const depths: Map<N, number> = new Map();
const stack: NODE[] = [];
let node: NODE | undefined = this.root,
last: NODE | undefined = undefined;
const depths: Map<NODE, number> = new Map();
while (stack.length > 0 || node) {
if (node) {
@ -866,7 +866,7 @@ export class BST<
return balanced;
}
protected _setRoot(v: N | undefined) {
protected _setRoot(v: NODE | undefined) {
if (v) {
v.parent = undefined;
}

View file

@ -23,8 +23,8 @@ import { IBinaryTree } from '../../interfaces';
export class RedBlackTreeNode<
K = any,
V = any,
N extends RedBlackTreeNode<K, V, N> = RedBlackTreeNodeNested<K, V>
> extends BSTNode<K, V, N> {
NODE extends RedBlackTreeNode<K, V, NODE> = RedBlackTreeNodeNested<K, V>
> extends BSTNode<K, V, NODE> {
color: RBTNColor;
constructor(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK) {
@ -43,17 +43,17 @@ export class RedBlackTreeNode<
export class RedBlackTree<
K = any,
V = any,
N extends RedBlackTreeNode<K, V, N> = RedBlackTreeNode<K, V, RedBlackTreeNodeNested<K, V>>,
TREE extends RedBlackTree<K, V, N, TREE> = RedBlackTree<K, V, N, RedBlackTreeNested<K, V, N>>
NODE extends RedBlackTreeNode<K, V, NODE> = RedBlackTreeNode<K, V, RedBlackTreeNodeNested<K, V>>,
TREE extends RedBlackTree<K, V, NODE, TREE> = RedBlackTree<K, V, NODE, RedBlackTreeNested<K, V, NODE>>
>
extends BST<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
Sentinel: N = new RedBlackTreeNode<K, V>(NaN as K) as unknown as N;
extends BST<K, V, NODE, TREE>
implements IBinaryTree<K, V, NODE, TREE> {
Sentinel: NODE = new RedBlackTreeNode<K, V>(NaN as K) as unknown as NODE;
/**
* This is the constructor function for a Red-Black Tree data structure in TypeScript, which
* initializes the tree with optional nodes and options.
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, NODE>`
* objects. It represents the initial nodes that will be added to the RBTree during its
* construction. If this parameter is provided, the `addMany` method is called to add all the
* nodes to the
@ -61,16 +61,16 @@ export class RedBlackTree<
* behavior of the RBTree. It is of type `Partial<RBTreeOptions>`, which means that you can provide
* only a subset of the properties defined in the `RBTreeOptions` interface.
*/
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: RBTreeOptions<K>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>> = [], options?: RBTreeOptions<K>) {
super([], options);
this._root = this.Sentinel;
if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries);
}
protected _root: N;
protected _root: NODE;
get root(): N {
get root(): NODE {
return this._root;
}
@ -92,8 +92,8 @@ export class RedBlackTree<
* @returns The method is returning a new instance of a RedBlackTreeNode with the specified key,
* value, and color.
*/
override createNode(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK): N {
return new RedBlackTreeNode<K, V, N>(key, value, color) as N;
override createNode(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK): NODE {
return new RedBlackTreeNode<K, V, NODE>(key, value, color) as NODE;
}
/**
@ -104,7 +104,7 @@ export class RedBlackTree<
* @returns a new instance of a RedBlackTree object.
*/
override createTree(options?: RBTreeOptions<K>): TREE {
return new RedBlackTree<K, V, N, TREE>([], {
return new RedBlackTree<K, V, NODE, TREE>([], {
iterationType: this.iterationType,
...options
}) as TREE;
@ -112,14 +112,14 @@ export class RedBlackTree<
/**
* The function `keyValueOrEntryToNode` takes an keyOrNodeOrEntry and converts it into a node object if possible.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`, where:
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`, where:
* @param {V} [value] - The `value` parameter is an optional value that can be passed to the
* `keyValueOrEntryToNode` function. It represents the value associated with the keyOrNodeOrEntry node. If a value
* is provided, it will be used when creating the new node. If no value is provided, the new node
* @returns a node of type N or undefined.
* @returns a node of type NODE or undefined.
*/
override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V): N | undefined {
let node: N | undefined;
override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V): NODE | undefined {
let node: NODE | undefined;
if (keyOrNodeOrEntry === null || keyOrNodeOrEntry === undefined) {
return;
@ -142,11 +142,11 @@ export class RedBlackTree<
/**
* The function checks if an keyOrNodeOrEntry is an instance of the RedBlackTreeNode class.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`.
* @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the RedBlackTreeNode
* class.
*/
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>): keyOrNodeOrEntry is N {
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>): keyOrNodeOrEntry is NODE {
return keyOrNodeOrEntry instanceof RedBlackTreeNode;
}
@ -155,7 +155,7 @@ export class RedBlackTree<
* Space Complexity: O(1)
*/
override isRealNode(node: N | undefined): node is N {
override isRealNode(node: NODE | undefined): node is NODE {
if (node === this.Sentinel || node === undefined) return false;
return node instanceof RedBlackTreeNode;
}
@ -176,17 +176,17 @@ export class RedBlackTree<
* entry.
* @param {V} [value] - The `value` parameter represents the value associated with the key that is
* being added to the binary search tree.
* @returns The method `add` returns either the newly added node (`N`) or `undefined`.
* @returns The method `add` returns either the newly added node (`NODE`) or `undefined`.
*/
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V): boolean {
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V): boolean {
const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value);
if (newNode === undefined) return false;
newNode.left = this.Sentinel;
newNode.right = this.Sentinel;
let y: N | undefined = undefined;
let x: N | undefined = this.root;
let y: NODE | undefined = undefined;
let x: NODE | undefined = this.root;
while (x !== this.Sentinel) {
y = x;
@ -245,20 +245,20 @@ export class RedBlackTree<
* that you want to use to identify the node that you want to delete from the binary tree. It can be
* of any type that is returned by the callback function `C`. It can also be `null` or `undefined` if
* you don't want to
* @param {C} callback - The `callback` parameter is a function that takes a node of type `N` and
* @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` and
* returns a value of type `ReturnType<C>`. It is used to determine if a node should be deleted based
* on its identifier. The `callback` function is optional and defaults to `this._defaultOneParam
* @returns an array of `BinaryTreeDeleteResult<N>`.
* @returns an array of `BinaryTreeDeleteResult<NODE>`.
*/
delete<C extends BTNCallback<N>>(
delete<C extends BTNCallback<NODE>>(
identifier: ReturnType<C> | null | undefined,
callback: C = this._defaultOneParamCallback as C
): BinaryTreeDeleteResult<N>[] {
const ans: BinaryTreeDeleteResult<N>[] = [];
): BinaryTreeDeleteResult<NODE>[] {
const ans: BinaryTreeDeleteResult<NODE>[] = [];
if (identifier === null) return ans;
const helper = (node: N | undefined): void => {
let z: N = this.Sentinel;
let x: N | undefined, y: N;
const helper = (node: NODE | undefined): void => {
let z: NODE = this.Sentinel;
let x: NODE | undefined, y: NODE;
while (node !== this.Sentinel) {
if (node && callback(node) === identifier) {
z = node;
@ -311,26 +311,26 @@ export class RedBlackTree<
return ans;
}
getNode<C extends BTNCallback<N, K>>(
getNode<C extends BTNCallback<NODE, K>>(
identifier: K,
callback?: C,
beginRoot?: N | undefined,
beginRoot?: NODE | undefined,
iterationType?: IterationType
): N | undefined;
): NODE | undefined;
getNode<C extends BTNCallback<N, N>>(
identifier: N | undefined,
getNode<C extends BTNCallback<NODE, NODE>>(
identifier: NODE | undefined,
callback?: C,
beginRoot?: N | undefined,
beginRoot?: NODE | undefined,
iterationType?: IterationType
): N | undefined;
): NODE | undefined;
getNode<C extends BTNCallback<N>>(
getNode<C extends BTNCallback<NODE>>(
identifier: ReturnType<C>,
callback: C,
beginRoot?: N | undefined,
beginRoot?: NODE | undefined,
iterationType?: IterationType
): N | undefined;
): NODE | undefined;
/**
* Time Complexity: O(log n)
@ -349,21 +349,21 @@ export class RedBlackTree<
* node that matches the other criteria
* @param {C} callback - The `callback` parameter is a function that will be called for each node in
* the binary tree. It is used to determine if a node matches the given identifier. The `callback`
* function should take a single parameter of type `N` (the type of the nodes in the binary tree) and
* @param {K | N | undefined} beginRoot - The `beginRoot` parameter is the starting point for
* function should take a single parameter of type `NODE` (the type of the nodes in the binary tree) and
* @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter is the starting point for
* searching for a node in a binary tree. It can be either a key value or a node object. If it is not
* provided, the search will start from the root of the binary tree.
* @param iterationType - The `iterationType` parameter is a variable that determines the type of
* iteration to be performed when searching for nodes in the binary tree. It is used in the
* `getNodes` method, which is called within the `getNode` method.
* @returns a value of type `N`, `null`, or `undefined`.
* @returns a value of type `NODE`, `null`, or `undefined`.
*/
getNode<C extends BTNCallback<N>>(
getNode<C extends BTNCallback<NODE>>(
identifier: ReturnType<C> | undefined,
callback: C = this._defaultOneParamCallback as C,
beginRoot: BSTNKeyOrNode<K, N> = this.root,
beginRoot: BSTNKeyOrNode<K, NODE> = this.root,
iterationType = this.iterationType
): N | null | undefined {
): NODE | null | undefined {
if ((identifier as any) instanceof RedBlackTreeNode) callback = (node => node) as C;
beginRoot = this.ensureNode(beginRoot);
return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined;
@ -383,12 +383,12 @@ export class RedBlackTree<
* Red-Black Tree.
* @returns the predecessor of the given RedBlackTreeNode 'x'.
*/
override getPredecessor(x: N): N {
override getPredecessor(x: NODE): NODE {
if (this.isRealNode(x.left)) {
return this.getRightMost(x.left)!;
}
let y: N | undefined = x.parent;
let y: NODE | undefined = x.parent;
while (this.isRealNode(y) && x === y.left) {
x = y!;
y = y!.parent;
@ -407,7 +407,7 @@ export class RedBlackTree<
this._size = 0;
}
protected override _setRoot(v: N) {
protected override _setRoot(v: NODE) {
if (v) {
v.parent = undefined;
}
@ -424,11 +424,11 @@ export class RedBlackTree<
* Space Complexity: O(1)
*
* The function performs a left rotation on a binary tree node.
* @param {RedBlackTreeNode} x - The parameter `x` is of type `N`, which likely represents a node in a binary tree.
* @param {RedBlackTreeNode} x - The parameter `x` is of type `NODE`, which likely represents a node in a binary tree.
*/
protected _leftRotate(x: N): void {
protected _leftRotate(x: NODE): void {
if (x.right) {
const y: N = x.right;
const y: NODE = x.right;
x.right = y.left;
if (y.left !== this.Sentinel) {
if (y.left) y.left.parent = x;
@ -459,9 +459,9 @@ export class RedBlackTree<
* @param {RedBlackTreeNode} x - x is a RedBlackTreeNode, which represents the node that needs to be right
* rotated.
*/
protected _rightRotate(x: N): void {
protected _rightRotate(x: NODE): void {
if (x.left) {
const y: N = x.left;
const y: NODE = x.left;
x.left = y.right;
if (y.right !== this.Sentinel) {
if (y.right) y.right.parent = x;
@ -491,8 +491,8 @@ export class RedBlackTree<
* The function `_fixDelete` is used to fix the red-black tree after a node deletion.
* @param {RedBlackTreeNode} x - The parameter `x` represents a node in a Red-Black Tree (RBT).
*/
protected _fixDelete(x: N): void {
let s: N | undefined;
protected _fixDelete(x: NODE): void {
let s: NODE | undefined;
while (x !== this.root && x.color === RBTNColor.BLACK) {
if (x.parent && x === x.parent.left) {
s = x.parent.right!;
@ -564,7 +564,7 @@ export class RedBlackTree<
* @param {RedBlackTreeNode} u - The parameter "u" represents a RedBlackTreeNode object.
* @param {RedBlackTreeNode} v - The parameter "v" is a RedBlackTreeNode object.
*/
protected _rbTransplant(u: N, v: N): void {
protected _rbTransplant(u: NODE, v: NODE): void {
if (u.parent === undefined) {
this._setRoot(v);
} else if (u === u.parent.left) {
@ -588,8 +588,8 @@ export class RedBlackTree<
* @param {RedBlackTreeNode} k - The parameter `k` is a RedBlackTreeNode object, which represents a node in a
* red-black tree.
*/
protected _fixInsert(k: N): void {
let u: N | undefined;
protected _fixInsert(k: NODE): void {
let u: NODE | undefined;
while (k.parent && k.parent.color === 1) {
if (k.parent.parent && k.parent === k.parent.parent.right) {
u = k.parent.parent.left;
@ -636,14 +636,14 @@ export class RedBlackTree<
/**
* The function replaces an old node with a new node while preserving the color of the old node.
* @param {N} oldNode - The `oldNode` parameter represents the node that needs to be replaced in a
* data structure. It is of type `N`, which is the type of the nodes in the data structure.
* @param {N} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the
* @param {NODE} oldNode - The `oldNode` parameter represents the node that needs to be replaced in a
* data structure. It is of type `NODE`, which is the type of the nodes in the data structure.
* @param {NODE} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the
* data structure.
* @returns The method is returning the result of calling the `_replaceNode` method from the
* superclass, passing in the `oldNode` and `newNode` as arguments.
*/
protected _replaceNode(oldNode: N, newNode: N): N {
protected _replaceNode(oldNode: NODE, newNode: NODE): NODE {
newNode.color = oldNode.color;
return super._replaceNode(oldNode, newNode);

View file

@ -21,8 +21,8 @@ import { AVLTree, AVLTreeNode } from './avl-tree';
export class TreeMultimapNode<
K = any,
V = any,
N extends TreeMultimapNode<K, V, N> = TreeMultimapNodeNested<K, V>
> extends AVLTreeNode<K, V, N> {
NODE extends TreeMultimapNode<K, V, NODE> = TreeMultimapNodeNested<K, V>
> extends AVLTreeNode<K, V, NODE> {
count: number;
/**
@ -47,12 +47,12 @@ export class TreeMultimapNode<
export class TreeMultimap<
K = any,
V = any,
N extends TreeMultimapNode<K, V, N> = TreeMultimapNode<K, V, TreeMultimapNodeNested<K, V>>,
TREE extends TreeMultimap<K, V, N, TREE> = TreeMultimap<K, V, N, TreeMultimapNested<K, V, N>>
NODE extends TreeMultimapNode<K, V, NODE> = TreeMultimapNode<K, V, TreeMultimapNodeNested<K, V>>,
TREE extends TreeMultimap<K, V, NODE, TREE> = TreeMultimap<K, V, NODE, TreeMultimapNested<K, V, NODE>>
>
extends AVLTree<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: TreeMultimapOptions<K>) {
extends AVLTree<K, V, NODE, TREE>
implements IBinaryTree<K, V, NODE, TREE> {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>> = [], options?: TreeMultimapOptions<K>) {
super([], options);
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);
}
@ -70,17 +70,17 @@ export class TreeMultimap<
* The function creates a new BSTNode with the given key, value, and count.
* @param {K} key - The key parameter is the unique identifier for the binary tree node. It is used to
* distinguish one node from another in the tree.
* @param {N} value - The `value` parameter represents the value that will be stored in the binary search tree node.
* @param {NODE} value - The `value` parameter represents the value that will be stored in the binary search tree node.
* @param {number} [count] - The "count" parameter is an optional parameter of type number. It represents the number of
* occurrences of the value in the binary search tree node. If not provided, the count will default to 1.
* @returns A new instance of the BSTNode class with the specified key, value, and count (if provided).
*/
override createNode(key: K, value?: V, count?: number): N {
return new TreeMultimapNode(key, value, count) as N;
override createNode(key: K, value?: V, count?: number): NODE {
return new TreeMultimapNode(key, value, count) as NODE;
}
override createTree(options?: TreeMultimapOptions<K>): TREE {
return new TreeMultimap<K, V, N, TREE>([], {
return new TreeMultimap<K, V, NODE, TREE>([], {
iterationType: this.iterationType,
variant: this.variant,
...options
@ -89,17 +89,21 @@ export class TreeMultimap<
/**
* The function `keyValueOrEntryToNode` converts an keyOrNodeOrEntry object into a node object.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`, which means it
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`, which means it
* can be one of the following:
* @param {V} [value] - The `value` parameter is an optional argument that represents the value
* associated with the node. It is of type `V`, which can be any data type. If no value is provided,
* it defaults to `undefined`.
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
* times the value should be added to the node. If not provided, it defaults to 1.
* @returns a node of type `N` or `undefined`.
* @returns a node of type `NODE` or `undefined`.
*/
override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V, count = 1): N | undefined {
let node: N | undefined;
override keyValueOrEntryToNode(
keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>,
value?: V,
count = 1
): NODE | undefined {
let node: NODE | undefined;
if (keyOrNodeOrEntry === undefined || keyOrNodeOrEntry === null) {
return;
} else if (this.isNode(keyOrNodeOrEntry)) {
@ -121,11 +125,11 @@ export class TreeMultimap<
/**
* The function checks if an keyOrNodeOrEntry is an instance of the TreeMultimapNode class.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, N>`.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry<K, V, NODE>`.
* @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the TreeMultimapNode
* class.
*/
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>): keyOrNodeOrEntry is N {
override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>): keyOrNodeOrEntry is NODE {
return keyOrNodeOrEntry instanceof TreeMultimapNode;
}
@ -151,7 +155,7 @@ export class TreeMultimap<
* @returns The method is returning either the newly inserted node or `undefined` if the insertion
* was not successful.
*/
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, N>, value?: V, count = 1): boolean {
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V, count = 1): boolean {
const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value, count);
if (newNode === undefined) return false;
@ -177,9 +181,9 @@ export class TreeMultimap<
* structure.
* @param keysOrNodesOrEntries - The parameter `keysOrNodesOrEntries` is an iterable that can contain
* either keys, nodes, or entries.
* @returns The method is returning an array of type `N | undefined`.
* @returns The method is returning an array of type `NODE | undefined`.
*/
override addMany(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>>): boolean[] {
override addMany(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, NODE>>): boolean[] {
return super.addMany(keysOrNodesOrEntries);
}
@ -261,22 +265,22 @@ export class TreeMultimap<
* being deleted. If set to true, the count of the node will not be considered and the node will be
* deleted regardless of its count. If set to false (default), the count of the node will be
* decremented by 1 and
* @returns an array of `BinaryTreeDeleteResult<N>`.
* @returns an array of `BinaryTreeDeleteResult<NODE>`.
*/
override delete<C extends BTNCallback<N>>(
override delete<C extends BTNCallback<NODE>>(
identifier: ReturnType<C>,
callback: C = this._defaultOneParamCallback as C,
ignoreCount = false
): BinaryTreeDeleteResult<N>[] {
const deletedResult: BinaryTreeDeleteResult<N>[] = [];
): BinaryTreeDeleteResult<NODE>[] {
const deletedResult: BinaryTreeDeleteResult<NODE>[] = [];
if (!this.root) return deletedResult;
const curr: N | undefined = this.getNode(identifier, callback) ?? undefined;
const curr: NODE | undefined = this.getNode(identifier, callback) ?? undefined;
if (!curr) return deletedResult;
const parent: N | undefined = curr?.parent ? curr.parent : undefined;
let needBalanced: N | undefined = undefined,
orgCurrent: N | undefined = curr;
const parent: NODE | undefined = curr?.parent ? curr.parent : undefined;
let needBalanced: NODE | undefined = undefined,
orgCurrent: NODE | undefined = curr;
if (curr.count > 1 && !ignoreCount) {
curr.count--;
@ -359,14 +363,17 @@ export class TreeMultimap<
/**
* The `_swapProperties` function swaps the key, value, count, and height properties between two nodes.
* @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node from
* which the values will be swapped. It can be of type `K`, `N`, or `undefined`.
* @param {K | N | undefined} destNode - The `destNode` parameter represents the destination
* @param {K | NODE | undefined} srcNode - The `srcNode` parameter represents the source node from
* which the values will be swapped. It can be of type `K`, `NODE`, or `undefined`.
* @param {K | NODE | undefined} destNode - The `destNode` parameter represents the destination
* node where the values from the source node will be swapped to.
* @returns either the `destNode` object if both `srcNode` and `destNode` are defined, or `undefined`
* if either `srcNode` or `destNode` is undefined.
*/
protected override _swapProperties(srcNode: BSTNKeyOrNode<K, N>, destNode: BSTNKeyOrNode<K, N>): N | undefined {
protected override _swapProperties(
srcNode: BSTNKeyOrNode<K, NODE>,
destNode: BSTNKeyOrNode<K, NODE>
): NODE | undefined {
srcNode = this.ensureNode(srcNode);
destNode = this.ensureNode(destNode);
if (srcNode && destNode) {
@ -391,7 +398,7 @@ export class TreeMultimap<
return undefined;
}
protected _replaceNode(oldNode: N, newNode: N): N {
protected _replaceNode(oldNode: NODE, newNode: NODE): NODE {
newNode.count = oldNode.count + newNode.count;
return super._replaceNode(oldNode, newNode);
}

View file

@ -78,6 +78,10 @@ export abstract class AbstractGraph<
return this._vertexMap;
}
set vertexMap(v: Map<VertexKey, VO>) {
this._vertexMap = v;
}
/**
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.

View file

@ -66,12 +66,20 @@ export class DirectedGraph<
return this._outEdgeMap;
}
set outEdgeMap(v: Map<VO, EO[]>) {
this._outEdgeMap = v;
}
protected _inEdgeMap: Map<VO, EO[]> = new Map<VO, EO[]>();
get inEdgeMap(): Map<VO, EO[]> {
return this._inEdgeMap;
}
set inEdgeMap(v: Map<VO, EO[]>) {
this._inEdgeMap = v;
}
/**
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
@ -599,6 +607,28 @@ export class DirectedGraph<
}
}
/**
* The isEmpty function checks if the graph is empty.
*
* @return A boolean value
*/
isEmpty(): boolean {
return this.vertexMap.size === 0 && this.inEdgeMap.size === 0 && this.outEdgeMap.size === 0;
}
/**
* The clone function creates a new DirectedGraph object with the same vertices and edges as the original.
*
* @return A new instance of the directedgraph class
*/
clone(): DirectedGraph<V, E, VO, EO> {
const cloned = new DirectedGraph<V, E, VO, EO>();
cloned.vertexMap = new Map<VertexKey, VO>(this.vertexMap);
cloned.inEdgeMap = new Map<VO, EO[]>(this.inEdgeMap);
cloned.outEdgeMap = new Map<VO, EO[]>(this.outEdgeMap);
return cloned;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)

View file

@ -108,4 +108,19 @@ export class MapGraph<
override createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO {
return new MapEdge(src, dest, weight, value) as EO;
}
/**
* The override function is used to override the default behavior of a function.
* In this case, we are overriding the clone() function from Graph&lt;V, E&gt;.
* The clone() function returns a new graph that is an exact copy of the original graph.
*
* @return A mapgraph&lt;v, e, vo, eo&gt;
*/
override clone(): MapGraph<V, E, VO, EO> {
const cloned = new MapGraph<V, E, VO, EO>(this.originCoord, this.bottomRight);
cloned.vertexMap = new Map<VertexKey, VO>(this.vertexMap);
cloned.inEdgeMap = new Map<VO, EO[]>(this.inEdgeMap);
cloned.outEdgeMap = new Map<VO, EO[]>(this.outEdgeMap);
return cloned;
}
}

View file

@ -64,6 +64,10 @@ export class UndirectedGraph<
return this._edgeMap;
}
set edgeMap(v: Map<VO, EO[]>) {
this._edgeMap = v;
}
/**
* The function creates a new vertex with an optional value and returns it.
* @param {VertexKey} key - The `key` parameter is the unique identifier for the vertex. It is used to distinguish one
@ -365,6 +369,30 @@ export class UndirectedGraph<
}
}
/**
* The isEmpty function checks if the graph is empty.
* @return True if the graph is empty and false otherwise
*/
isEmpty(): boolean {
return this.vertexMap.size === 0 && this.edgeMap.size === 0;
}
/**
* The clone function creates a new UndirectedGraph object and copies the
* vertexMap and edgeMap from this graph to the new one. This is done by
* assigning each of these properties to their respective counterparts in the
* cloned graph. The clone function returns a reference to this newly created,
* cloned UndirectedGraph object.
*
* @return A new instance of the undirectedgraph class
*/
clone(): UndirectedGraph<V, E, VO, EO> {
const cloned = new UndirectedGraph<V, E, VO, EO>();
cloned.vertexMap = new Map<VertexKey, VO>(this.vertexMap);
cloned.edgeMap = new Map<VO, EO[]>(this.edgeMap);
return cloned;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)

View file

@ -32,7 +32,7 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
* `T`. It is an optional parameter and its default value is an empty array `[]`.
* @param [options] - The `options` parameter is an optional object that can contain two properties:
*/
constructor(rawCollection: Iterable<R> = [], options?: HashMapOptions<K, V, R>) {
constructor(rawCollection: Iterable<R | [K, V]> = [], options?: HashMapOptions<K, V, R>) {
super();
if (options) {
const { hashFn, toEntryFn } = options;
@ -67,6 +67,16 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
return this._toEntryFn;
}
/**
* The hasFn function is a function that takes in an item and returns a boolean
* indicating whether the item is contained within the hash table.
*
* @return The hash function
*/
get hasFn() {
return this._hashFn;
}
protected _size = 0;
/**
@ -137,10 +147,18 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
* `T`.
* @returns The `setMany` function is returning an array of booleans.
*/
setMany(rawCollection: Iterable<R>): boolean[] {
setMany(rawCollection: Iterable<R | [K, V]>): boolean[] {
const results: boolean[] = [];
for (const rawEle of rawCollection) {
const [key, value] = this.toEntryFn(rawEle);
let key, value;
if (this.isEntry(rawEle)) {
key = rawEle[0];
value = rawEle[1];
} else {
const item = this.toEntryFn(rawEle);
key = item[0];
value = item[1];
}
results.push(this.set(key, value));
}
return results;
@ -203,6 +221,17 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
}
}
/**
* The clone function creates a new HashMap with the same key-value pairs as
* this one. The clone function is useful for creating a copy of an existing
* HashMap, and then modifying that copy without affecting the original.
*
* @return A new hashmap with the same values as this one
*/
clone(): HashMap<K, V, R> {
return new HashMap<K, V, R>(this, { hashFn: this._hashFn, toEntryFn: this.toEntryFn });
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)

View file

@ -150,6 +150,26 @@ export class Queue<E = any> extends IterableElementBase<E> {
return first;
}
/**
* The delete function removes an element from the list.
* @param element: E Specify the element to be deleted
* @return A boolean value indicating whether the element was successfully deleted or not
*/
delete(element: E): boolean {
const index = this.elements.indexOf(element);
return this.deleteAt(index);
}
/**
* The deleteAt function deletes the element at a given index.
* @param index: number Determine the index of the element to be deleted
* @return A boolean value
*/
deleteAt(index: number): boolean {
const spliced = this.elements.splice(index, 1);
return spliced.length === 1;
}
/**
* Time Complexity: O(1) - constant time as it retrieves the value at the current offset.
* Space Complexity: O(1) - no additional space is used.

View file

@ -129,6 +129,26 @@ export class Stack<E = any> extends IterableElementBase<E> {
return this.elements.pop();
}
/**
* The delete function removes an element from the stack.
* @param element: E Specify the element to be deleted
* @return A boolean value indicating whether the element was successfully deleted or not
*/
delete(element: E): boolean {
const index = this.elements.indexOf(element);
return this.deleteAt(index);
}
/**
* The deleteAt function deletes the element at a given index.
* @param index: number Determine the index of the element to be deleted
* @return A boolean value
*/
deleteAt(index: number): boolean {
const spliced = this.elements.splice(index, 1);
return spliced.length === 1;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)

View file

@ -37,7 +37,7 @@ export class TrieNode {
* 10. IP Routing: Used in certain types of IP routing algorithms.
* 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data."
*/
export class Trie extends IterableElementBase<string> {
export class Trie extends IterableElementBase<string, Trie> {
/**
* The constructor function for the Trie class.
* @param words: Iterable string Initialize the trie with a set of words
@ -143,6 +143,14 @@ export class Trie extends IterableElementBase<string> {
return cur.isEnd;
}
/**
* The isEmpty function checks if the size of the queue is 0.
* @return True if the size of the queue is 0
*/
isEmpty(): boolean {
return this.size === 0;
}
/**
* Time Complexity: O(M), where M is the length of the word being deleted.
* Space Complexity: O(M) - Due to the recursive DFS approach.

View file

@ -3,7 +3,7 @@ import { IterationType } from "../../common";
export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeNested<K, V, N extends BinaryTreeNode<K, V, N>> = BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, BinaryTree<K, V, N, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeNested<K, V, NODE extends BinaryTreeNode<K, V, NODE>> = BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, BinaryTree<K, V, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeOptions<K> = {
iterationType?: IterationType,

View file

@ -4,3 +4,5 @@ export type TrlFn = (...args: any[]) => any;
export type TrlAsyncFn = (...args: any[]) => any;
export type SpecifyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type Any = string | number | boolean | object | null | undefined | symbol;

View file

@ -235,6 +235,43 @@ describe('AVLTree APIs test', () => {
const bfsRes = avl.bfs(node => node.key);
expect(bfsRes[0]).toBe(2);
});
it('should the clone method', () => {
function checkTreeStructure(avl: AVLTree<string, number>) {
expect(avl.size).toBe(4);
expect(avl.root?.key).toBe('2');
expect(avl.root?.left?.key).toBe('1');
expect(avl.root?.left?.left?.key).toBe(undefined);
expect(avl.root?.left?.right?.key).toBe(undefined);
expect(avl.root?.right?.key).toBe('4');
expect(avl.root?.right?.left?.key).toBe(undefined);
expect(avl.root?.right?.right?.key).toBe('5');
}
const avl = new AVLTree<string, number>();
avl.addMany([
['2', 2],
['4', 4],
['5', 5],
['3', 3],
['1', 1]
]);
expect(avl.size).toBe(5);
expect(avl.root?.key).toBe('2');
expect(avl.root?.left?.key).toBe('1');
expect(avl.root?.left?.left?.key).toBe(undefined);
expect(avl.root?.left?.right?.key).toBe(undefined);
expect(avl.root?.right?.key).toBe('4');
expect(avl.root?.right?.left?.key).toBe('3');
expect(avl.root?.right?.right?.key).toBe('5');
avl.delete('3');
checkTreeStructure(avl);
const cloned = avl.clone();
checkTreeStructure(cloned);
cloned.delete('1');
expect(avl.size).toBe(4);
expect(cloned.size).toBe(3);
});
});
describe('AVLTree', () => {

View file

@ -152,6 +152,51 @@ describe('BinaryTree', () => {
expect(tree.has('3', node => node.value?.toString())).toBe(true);
});
it('should the clone method work fine', () => {
expect(tree.isEmpty()).toBe(true);
tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
expect(tree.root?.key).toBe(4);
expect(tree.root?.left?.key).toBe(2);
expect(tree.root?.left?.left).toBe(null);
expect(tree.root?.left?.right?.key).toBe(1);
expect(tree.root?.right?.key).toBe(6);
expect(tree.root?.right?.left?.key).toBe(3);
expect(tree.root?.right?.right).toBe(null);
const cloned = tree.clone();
expect(cloned.root?.key).toBe(4);
expect(cloned.root?.left?.key).toBe(2);
expect(cloned.root?.left?.left).toBe(null);
expect(cloned.root?.left?.right?.key).toBe(1);
expect(cloned.root?.right?.key).toBe(6);
expect(cloned.root?.right?.left?.key).toBe(3);
expect(cloned.root?.right?.right).toBe(null);
expect(cloned.dfs(node => node.key, 'pre', cloned.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]);
expect(
cloned.dfs(node => (node ? node.key : null), 'pre', cloned.getNode(6), IterationType.ITERATIVE, true)
).toEqual([6, 3, 7, null]);
expect(
cloned.dfs(node => (node ? node.key : node), 'pre', cloned.getNode(6), IterationType.ITERATIVE, true)
).toEqual([6, 3, 7, null]);
expect(
cloned.dfs(node => (node ? node.key : null), 'pre', cloned.getNode(6), IterationType.RECURSIVE, true)
).toEqual([6, 3, 7, null]);
cloned.delete(6);
cloned.delete(3);
cloned.delete(7);
cloned.delete(1);
cloned.delete(5);
cloned.delete(4);
cloned.delete(2);
// cloned.delete(null);
// cloned.delete(null);
// cloned.delete(null);
expect(tree.size).toBe(10);
expect(cloned.size).toBe(3);
// expect(cloned.size).toBe(0);
// expect(cloned.isEmpty()).toBe(true);
});
it('should be a balance tree after malicious manipulation', () => {
tree.add(3);
tree.add(12);
@ -238,22 +283,6 @@ describe('BinaryTree', () => {
expect(tree.getNodes(tree.getNodeByKey(2), undefined, false, tree.root)).toEqual([tree.getNodeByKey(2)]);
});
// it('should subTreeTraverse', () => {
// tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
// expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]);
// expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.ITERATIVE, false)).toEqual([6, 3, 7]);
// expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.RECURSIVE)).toEqual([6, 3, 7]);
// expect(
// tree.subTreeTraverse(node => (node ? node.key : null), tree.getNode(6), IterationType.ITERATIVE, true)
// ).toEqual([6, 3, 7, null]);
// expect(
// tree.subTreeTraverse(node => (node ? node.key : node), tree.getNode(6), IterationType.ITERATIVE, true)
// ).toEqual([6, 3, 7, null]);
// expect(
// tree.subTreeTraverse(node => (node ? node.key : null), tree.getNode(6), IterationType.RECURSIVE, true)
// ).toEqual([6, 3, 7, null]);
// });
it('should sub tree traverse', () => {
tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
expect(tree.dfs(node => node.key, 'pre', tree.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]);
@ -613,7 +642,7 @@ describe('BinaryTree', () => {
tree.delete(5);
tree.delete(7);
tree.delete(3);
expect(tree.root).toBe(null);
expect(tree.root).toBe(undefined);
expect(tree.getHeight()).toBe(-1);
});
});

View file

@ -795,6 +795,70 @@ describe('BST operations test recursively', () => {
expect(bfsNodes[1].key).toBe(12);
expect(bfsNodes[2].key).toBe(16);
});
it('should delete', () => {
const numBST = new BST<number>();
numBST.addMany([2, 4, 5, 3, 1]);
expect(numBST.size).toBe(5);
numBST.delete(1);
expect(numBST.size).toBe(4);
numBST.delete(2);
numBST.delete(3);
numBST.delete(4);
numBST.delete(5);
expect(numBST.size).toBe(0);
numBST.delete(5);
expect(numBST.size).toBe(0);
});
it('should the clone method', () => {
function checkTreeStructure(bst: BST<string, number>) {
expect(bst.size).toBe(4);
expect(bst.root?.key).toBe('2');
expect(bst.root?.left?.key).toBe('1');
expect(bst.root?.left?.left?.key).toBe(undefined);
expect(bst.root?.left?.right?.key).toBe(undefined);
expect(bst.root?.right?.key).toBe('4');
expect(bst.root?.right?.left?.key).toBe(undefined);
expect(bst.root?.right?.right?.key).toBe('5');
}
const bst = new BST<string, number>();
bst.addMany([
['2', 2],
['4', 4],
['5', 5],
['3', 3],
['1', 1]
]);
expect(bst.size).toBe(5);
expect(bst.root?.key).toBe('3');
expect(bst.root?.left?.key).toBe('1');
expect(bst.root?.left?.left?.key).toBe(undefined);
expect(bst.root?.left?.right?.key).toBe('2');
expect(bst.root?.right?.key).toBe('4');
expect(bst.root?.right?.left?.key).toBe(undefined);
expect(bst.root?.right?.right?.key).toBe('5');
bst.delete('3');
checkTreeStructure(bst);
const cloned = bst.clone();
checkTreeStructure(cloned);
bst.delete('2');
bst.delete('1');
bst.delete('4');
bst.delete('5');
expect(bst.size).toBe(0);
cloned.delete('1');
expect(bst.size).toBe(0);
expect(cloned.size).toBe(3);
cloned.delete('2');
cloned.delete('3');
cloned.delete('4');
cloned.delete('5');
expect(cloned.size).toBe(0);
});
});
describe('BST isBST', function () {
@ -865,7 +929,7 @@ describe('BST Performance test', function () {
// expect(bst.lastKey()).toBe(9);
});
it('should subTreeTraverse, null should be ignored', () => {
it('should dfs as sub tree traversal, null should be ignored', () => {
const bst = new BST();
bst.addMany([4, 2, 6, 1, 3, 5, 7]);
expect(bst.dfs(node => node.key, 'pre', bst.getNode(6), IterationType.ITERATIVE)).toEqual([6, 5, 7]);

View file

@ -138,9 +138,46 @@ describe('RedBlackTree', () => {
expect(predecessorNode).toBe(tree.getNode(10));
});
});
it('should the clone method', () => {
function checkTreeStructure(rbTree: RedBlackTree<string, number>) {
expect(rbTree.size).toBe(4);
expect(rbTree.root?.key).toBe('2');
expect(rbTree.root?.left?.key).toBe('1');
expect(rbTree.root?.left?.left?.key).toBe(NaN);
expect(rbTree.root?.left?.right?.key).toBe(NaN);
expect(rbTree.root?.right?.key).toBe('4');
expect(rbTree.root?.right?.left?.key).toBe(NaN);
expect(rbTree.root?.right?.right?.key).toBe('5');
}
const rbTree = new RedBlackTree<string, number>();
rbTree.addMany([
['2', 2],
['4', 4],
['5', 5],
['3', 3],
['1', 1]
]);
expect(rbTree.size).toBe(5);
expect(rbTree.root?.key).toBe('2');
expect(rbTree.root?.left?.key).toBe('1');
expect(rbTree.root?.left?.left?.key).toBe(NaN);
expect(rbTree.root?.left?.right?.key).toBe(NaN);
expect(rbTree.root?.right?.key).toBe('4');
expect(rbTree.root?.right?.left?.key).toBe('3');
expect(rbTree.root?.right?.right?.key).toBe('5');
rbTree.delete('3');
checkTreeStructure(rbTree);
const cloned = rbTree.clone();
checkTreeStructure(cloned);
cloned.delete('1');
expect(rbTree.size).toBe(4);
expect(cloned.size).toBe(3);
});
});
describe('RedBlackTree', () => {
describe('RedBlackTree 2', () => {
let tree: RedBlackTree<number>;
beforeEach(() => {

View file

@ -555,38 +555,6 @@ describe('TreeMultimap Performance test', function () {
beforeEach(() => {
treeMS.clear();
});
it(`Observe the time consumption of TreeMultimap.add fitting O(n log n)`, function () {
// // Create a benchmark suite
// const suite = new Benchmark.Suite();
// // Define a function to generate a random array of a given size
// function generateRandomArray(size: number): number[] {
// const arr: number[] = [];
// for (let i = 0; i < size; i++) {
// arr.push(Math.floor(Math.random() * size));
// }
// return arr;
// }
// const inputArray = generateRandomArray(inputSize[0]);
//
// suite.add(`TreeMultimap addMany (n=${inputSize[0]})`, () => {
// treeMS.addMany([...inputArray]);
// });
//
// // Run the benchmarks
// suite
// .on('cycle', (event: any) => {
// const benchmark = event.target;
// const n = parseInt(benchmark.name.split('=')[1]);
// const observedTime = benchmark.times.elapsed;
// const expected = expectedTime(n);
// console.log(`Input size (n): ${n}, Observed time: ${observedTime.toFixed(2)}ms, Expected time: ${expected.toFixed(2)}ms`);
// })
// .on('complete', () => {
// console.log(`Benchmark (n=${inputSize[0]}) completed.`);
// done(); // Call done to indicate the test is complete
// })
// .run({async: true});
});
it(`Observe the time consumption of TreeMultimap.dfs be good`, function () {
const startDFS = performance.now();
@ -604,6 +572,43 @@ describe('TreeMultimap Performance test', function () {
treeMS.lesserOrGreaterTraverse(node => (node.count += 1), CP.lt, inputSize / 2);
isDebug && console.log('---lesserOrGreaterTraverse', performance.now() - startL);
});
it('should the clone method', () => {
function checkTreeStructure(treeMultimap: TreeMultimap<string, number>) {
expect(treeMultimap.size).toBe(4);
expect(treeMultimap.root?.key).toBe('2');
expect(treeMultimap.root?.left?.key).toBe('1');
expect(treeMultimap.root?.left?.left?.key).toBe(undefined);
expect(treeMultimap.root?.left?.right?.key).toBe(undefined);
expect(treeMultimap.root?.right?.key).toBe('4');
expect(treeMultimap.root?.right?.left?.key).toBe(undefined);
expect(treeMultimap.root?.right?.right?.key).toBe('5');
}
const treeMultimap = new TreeMultimap<string, number>();
treeMultimap.addMany([
['2', 2],
['4', 4],
['5', 5],
['3', 3],
['1', 1]
]);
expect(treeMultimap.size).toBe(5);
expect(treeMultimap.root?.key).toBe('2');
expect(treeMultimap.root?.left?.key).toBe('1');
expect(treeMultimap.root?.left?.left?.key).toBe(undefined);
expect(treeMultimap.root?.left?.right?.key).toBe(undefined);
expect(treeMultimap.root?.right?.key).toBe('4');
expect(treeMultimap.root?.right?.left?.key).toBe('3');
expect(treeMultimap.root?.right?.right?.key).toBe('5');
treeMultimap.delete('3');
checkTreeStructure(treeMultimap);
const cloned = treeMultimap.clone();
checkTreeStructure(cloned);
cloned.delete('1');
expect(treeMultimap.size).toBe(4);
expect(cloned.size).toBe(3);
});
});
describe('TreeMultimap iterative methods test', () => {

View file

@ -77,6 +77,14 @@ class MyGraph<
return true;
}
isEmpty(): boolean {
return true;
}
clone(): any {
return {};
}
protected _addEdge(edge: EO): boolean {
return edge ? true : true;
}

View file

@ -85,6 +85,29 @@ describe('HashMap', () => {
// expect(hashMap.table[0].length).toBe(2);
});
it('should clone', () => {
hashMap = new HashMap<string, number>();
hashMap.set('one', 1);
hashMap.set('two', 2);
for (let i = 3; i <= 100; i++) {
hashMap.set(i.toString(), i);
}
expect(hashMap.get('one')).toBe(1);
expect(hashMap.get('two')).toBe(2);
expect(hashMap.get('86')).toBe(86);
expect(hashMap.size).toBe(100);
hashMap.delete('two');
expect(hashMap.size).toBe(99);
const cloned = hashMap.clone();
expect(cloned.get('one')).toBe(1);
expect(cloned.get('two')).toBe(undefined);
expect(cloned.get('86')).toBe(86);
expect(cloned.size).toBe(99);
});
describe('HashMap Test2', () => {
let hashMap: HashMap;

View file

@ -1,4 +1,4 @@
import { FibonacciHeap, MaxHeap, MinHeap } from '../../../../src';
import { FibonacciHeap, Heap, MaxHeap, MinHeap } from '../../../../src';
import { logBigOMetricsWrap } from '../../../utils';
describe('Heap Operation Test', () => {
@ -26,6 +26,23 @@ describe('Heap Operation Test', () => {
expect(minNumHeap.sort()).toEqual([2, 5, 6, 9]);
});
it('should clone', function () {
const minNumHeap = new Heap<string>([], { comparator: (a, b) => Number(a) - Number(b) });
minNumHeap.add('1');
minNumHeap.add('6');
minNumHeap.add('2');
minNumHeap.add('0');
minNumHeap.add('5');
minNumHeap.add('9');
minNumHeap.delete('2');
expect([...minNumHeap]).toEqual(['0', '1', '9', '6', '5']);
const cloned = minNumHeap.clone();
expect([...cloned]).toEqual(['0', '1', '9', '6', '5']);
minNumHeap.delete('5');
expect([...minNumHeap]).toEqual(['0', '1', '9', '6']);
expect([...cloned]).toEqual(['0', '1', '9', '6', '5']);
});
it('should object heap work well', function () {
const minHeap = new MinHeap<{ a: string; key: number }>([], { comparator: (a, b) => a.key - b.key });
minHeap.add({ key: 1, a: 'a1' });

View file

@ -37,6 +37,23 @@ describe('DoublyLinkedList Operation Test', () => {
expect(list.tail?.value).toBe(4);
});
it('should clone', function () {
const dList = new DoublyLinkedList<string>();
dList.addLast('1');
dList.addLast('6');
dList.addLast('2');
dList.addLast('0');
dList.addLast('5');
dList.addLast('9');
dList.delete('2');
expect([...dList]).toEqual(['1', '6', '0', '5', '9']);
const cloned = dList.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
dList.delete('5');
expect([...dList]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
it('should find undefined', () => {
expect(list.find(value => value === 6)).toBe(undefined);
expect(list.find(value => value === 4)).toBe(4);

View file

@ -348,6 +348,23 @@ describe('SinglyLinkedList Operation Test', () => {
});
});
it('should clone', function () {
const sList = new SinglyLinkedList<string>();
sList.addLast('1');
sList.addLast('6');
sList.addLast('2');
sList.addLast('0');
sList.addLast('5');
sList.addLast('9');
sList.delete('2');
expect([...sList]).toEqual(['1', '6', '0', '5', '9']);
const cloned = sList.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
sList.delete('5');
expect([...sList]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
describe('countOccurrences', () => {
it('should count occurrences of a value', () => {
list.push(1);

View file

@ -54,7 +54,7 @@ describe('SkipList', () => {
});
});
describe('SkipList', () => {
describe('SkipList Test2', () => {
let skipList: SkipList<number, string>;
beforeEach(() => {

View file

@ -49,6 +49,23 @@ describe('Deque - Basic Operations', () => {
deque.setAt(0, 3);
expect(deque.at(0)).toBe(3);
});
it('should clone', function () {
const deque = new Deque<string>();
deque.addLast('1');
deque.addLast('6');
deque.addLast('2');
deque.addLast('0');
deque.addLast('5');
deque.addLast('9');
deque.delete('2');
expect([...deque]).toEqual(['1', '6', '0', '5', '9']);
const cloned = deque.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
deque.delete('5');
expect([...deque]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
});
describe('Deque - Complex Operations', () => {
let deque: Deque<number>;

View file

@ -89,6 +89,23 @@ describe('Queue', () => {
}
expect(queue.isEmpty()).toBeTruthy();
});
it('should clone', function () {
const queue = new Queue<string>();
queue.push('1');
queue.push('6');
queue.push('2');
queue.push('0');
queue.push('5');
queue.push('9');
queue.delete('2');
expect([...queue]).toEqual(['1', '6', '0', '5', '9']);
const cloned = queue.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
queue.delete('5');
expect([...queue]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
});
describe('Queue - Advanced Methods', () => {

View file

@ -35,6 +35,23 @@ describe('Stack', () => {
expect(stack.size).toBe(2);
});
it('should clone', function () {
const stack = new Stack<string>();
stack.push('1');
stack.push('6');
stack.push('2');
stack.push('0');
stack.push('5');
stack.push('9');
stack.delete('2');
expect([...stack]).toEqual(['1', '6', '0', '5', '9']);
const cloned = stack.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
stack.delete('5');
expect([...stack]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
it('should return undefined when popping from an empty stack', () => {
const poppedElement = stack.pop();
expect(poppedElement).toBe(undefined);

View file

@ -56,6 +56,23 @@ describe('Trie', () => {
expect(trie.hasPrefix('banana')).toBe(false);
});
it('should clone', function () {
const trie = new Trie();
trie.add('1');
trie.add('6');
trie.add('2');
trie.add('0');
trie.add('5');
trie.add('9');
trie.delete('2');
expect([...trie]).toEqual(['1', '6', '0', '5', '9']);
const cloned = trie.clone();
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
trie.delete('5');
expect([...trie]).toEqual(['1', '6', '0', '9']);
expect([...cloned]).toEqual(['1', '6', '0', '5', '9']);
});
it('should check if a string is a common prefix', () => {
const trie = new Trie();
trie.add('apple');