refactor: Rearrange constructor parameters for all data structures, with the first parameter being the output and the second parameter being a configuration object. Make all configuration object properties optional.

fix: Provide optional parameters for inheritable data structures to facilitate inheritance. #64
This commit is contained in:
Revone 2023-12-24 17:44:55 +08:00
parent fdf5d06245
commit c949b5b741
43 changed files with 207 additions and 784 deletions

View file

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

View file

@ -984,43 +984,43 @@ avl2.print();
[//]: # (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>122.81</td><td>8.14</td><td>0.00</td></tr><tr><td>10,000 add & delete randomly</td><td>184.27</td><td>5.43</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>132.27</td><td>7.56</td><td>0.00</td></tr><tr><td>10,000 get</td><td>51.04</td><td>19.59</td><td>7.82e-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>126.82</td><td>7.88</td><td>0.01</td></tr><tr><td>10,000 add & delete randomly</td><td>186.36</td><td>5.37</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>133.15</td><td>7.51</td><td>0.00</td></tr><tr><td>10,000 get</td><td>50.65</td><td>19.74</td><td>5.59e-4</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.81</td><td>172.09</td><td>8.66e-5</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>15.03</td><td>66.54</td><td>2.26e-4</td></tr><tr><td>10,000 RBTree get</td><td>18.76</td><td>53.30</td><td>4.08e-4</td></tr><tr><td>10,000 AVLTree add</td><td>125.39</td><td>7.98</td><td>0.00</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>189.09</td><td>5.29</td><td>0.00</td></tr><tr><td>10,000 AVLTree get</td><td>0.92</td><td>1087.75</td><td>2.99e-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>10,000 RBTree add</td><td>6.12</td><td>163.48</td><td>1.94e-4</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>15.46</td><td>64.70</td><td>3.64e-4</td></tr><tr><td>10,000 RBTree get</td><td>19.98</td><td>50.06</td><td>0.00</td></tr><tr><td>10,000 AVLTree add</td><td>130.40</td><td>7.67</td><td>0.02</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>193.64</td><td>5.16</td><td>0.01</td></tr><tr><td>10,000 AVLTree get</td><td>0.99</td><td>1005.44</td><td>3.95e-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>81.23</td><td>12.31</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>205.00</td><td>4.88</td><td>0.00</td></tr><tr><td>100,000 getNode</td><td>178.14</td><td>5.61</td><td>8.78e-4</td></tr><tr><td>100,000 add & iterator</td><td>110.77</td><td>9.03</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</td><td>90.72</td><td>11.02</td><td>0.03</td></tr><tr><td>100,000 add & delete randomly</td><td>228.77</td><td>4.37</td><td>0.02</td></tr><tr><td>100,000 getNode</td><td>192.25</td><td>5.20</td><td>5.16e-4</td></tr><tr><td>100,000 add & iterator</td><td>112.49</td><td>8.89</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'>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>9863.41</td><td>1.37e-6</td></tr><tr><td>1,000 addEdge</td><td>6.29</td><td>159.07</td><td>1.77e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.15e+4</td><td>4.86e-7</td></tr><tr><td>1,000 getEdge</td><td>23.46</td><td>42.63</td><td>0.00</td></tr><tr><td>tarjan</td><td>216.87</td><td>4.61</td><td>0.01</td></tr><tr><td>tarjan all</td><td>6549.75</td><td>0.15</td><td>0.03</td></tr><tr><td>topologicalSort</td><td>182.67</td><td>5.47</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>9250.94</td><td>1.22e-5</td></tr><tr><td>1,000 addEdge</td><td>6.35</td><td>157.51</td><td>2.88e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.06e+4</td><td>8.69e-6</td></tr><tr><td>1,000 getEdge</td><td>23.02</td><td>43.43</td><td>0.00</td></tr><tr><td>tarjan</td><td>213.85</td><td>4.68</td><td>0.01</td></tr><tr><td>tarjan all</td><td>6674.11</td><td>0.15</td><td>0.28</td></tr><tr><td>topologicalSort</td><td>179.09</td><td>5.58</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>hash-map</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 set</td><td>119.49</td><td>8.37</td><td>0.04</td></tr><tr><td>Native Map 1,000,000 set</td><td>222.50</td><td>4.49</td><td>0.02</td></tr><tr><td>Native Set 1,000,000 add</td><td>173.11</td><td>5.78</td><td>0.01</td></tr><tr><td>1,000,000 set & get</td><td>118.37</td><td>8.45</td><td>0.02</td></tr><tr><td>Native Map 1,000,000 set & get</td><td>273.63</td><td>3.65</td><td>0.01</td></tr><tr><td>Native Set 1,000,000 add & has</td><td>175.42</td><td>5.70</td><td>0.02</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>345.12</td><td>2.90</td><td>0.05</td></tr><tr><td>Native Map 1,000,000 ObjKey set & get</td><td>495.13</td><td>2.02</td><td>0.05</td></tr><tr><td>Native Set 1,000,000 ObjKey add & has</td><td>276.79</td><td>3.61</td><td>0.04</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>131.27</td><td>7.62</td><td>0.05</td></tr><tr><td>Native Map 1,000,000 set</td><td>267.34</td><td>3.74</td><td>0.04</td></tr><tr><td>Native Set 1,000,000 add</td><td>207.03</td><td>4.83</td><td>0.06</td></tr><tr><td>1,000,000 set & get</td><td>132.19</td><td>7.56</td><td>0.03</td></tr><tr><td>Native Map 1,000,000 set & get</td><td>276.30</td><td>3.62</td><td>0.01</td></tr><tr><td>Native Set 1,000,000 add & has</td><td>187.74</td><td>5.33</td><td>0.02</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>336.39</td><td>2.97</td><td>0.03</td></tr><tr><td>Native Map 1,000,000 ObjKey set & get</td><td>394.47</td><td>2.54</td><td>0.09</td></tr><tr><td>Native Set 1,000,000 ObjKey add & has</td><td>295.48</td><td>3.38</td><td>0.04</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>27.57</td><td>36.27</td><td>0.00</td></tr><tr><td>100,000 add & dfs</td><td>34.44</td><td>29.04</td><td>2.58e-4</td></tr><tr><td>10,000 fib add & pop</td><td>361.99</td><td>2.76</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.18</td><td>41.35</td><td>6.43e-4</td></tr><tr><td>100,000 add & dfs</td><td>33.64</td><td>29.72</td><td>0.00</td></tr><tr><td>10,000 fib add & pop</td><td>363.38</td><td>2.75</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>214.62</td><td>4.66</td><td>0.03</td></tr><tr><td>1,000,000 unshift</td><td>223.53</td><td>4.47</td><td>0.03</td></tr><tr><td>1,000,000 unshift & shift</td><td>173.70</td><td>5.76</td><td>0.03</td></tr><tr><td>1,000,000 addBefore</td><td>341.89</td><td>2.92</td><td>0.09</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>220.16</td><td>4.54</td><td>0.03</td></tr><tr><td>1,000,000 unshift</td><td>210.84</td><td>4.74</td><td>0.05</td></tr><tr><td>1,000,000 unshift & shift</td><td>189.59</td><td>5.27</td><td>0.07</td></tr><tr><td>1,000,000 addBefore</td><td>412.74</td><td>2.42</td><td>0.17</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>195.74</td><td>5.11</td><td>0.04</td></tr><tr><td>10,000 push & pop</td><td>239.88</td><td>4.17</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>257.45</td><td>3.88</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>252.06</td><td>3.97</td><td>0.09</td></tr><tr><td>10,000 push & pop</td><td>230.29</td><td>4.34</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>261.57</td><td>3.82</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>79.62</td><td>12.56</td><td>5.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>100,000 add & poll</td><td>75.71</td><td>13.21</td><td>8.95e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>deque</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>14.20</td><td>70.43</td><td>1.32e-4</td></tr><tr><td>1,000,000 push & pop</td><td>23.13</td><td>43.22</td><td>1.71e-4</td></tr><tr><td>100,000 push & shift</td><td>2.40</td><td>416.67</td><td>2.68e-5</td></tr><tr><td>Native Array 100,000 push & shift</td><td>3050.20</td><td>0.33</td><td>0.25</td></tr><tr><td>100,000 unshift & shift</td><td>2.24</td><td>446.26</td><td>5.35e-5</td></tr><tr><td>Native Array 100,000 unshift & shift</td><td>5037.86</td><td>0.20</td><td>0.17</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>25.18</td><td>39.71</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>33.52</td><td>29.83</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>3.61</td><td>276.96</td><td>5.50e-4</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2703.16</td><td>0.37</td><td>0.11</td></tr><tr><td>100,000 unshift & shift</td><td>3.73</td><td>268.14</td><td>8.29e-4</td></tr><tr><td>Native Array 100,000 unshift & shift</td><td>4767.61</td><td>0.21</td><td>0.40</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>47.09</td><td>21.23</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>5.06</td><td>197.72</td><td>1.25e-4</td></tr><tr><td>Native Array 100,000 push & shift</td><td>3038.51</td><td>0.33</td><td>0.12</td></tr><tr><td>Native Array 100,000 push & pop</td><td>4.44</td><td>225.23</td><td>1.51e-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>1,000,000 push</td><td>50.56</td><td>19.78</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>5.99</td><td>166.85</td><td>0.00</td></tr><tr><td>Native Array 100,000 push & shift</td><td>2962.43</td><td>0.34</td><td>0.29</td></tr><tr><td>Native Array 100,000 push & pop</td><td>4.49</td><td>222.69</td><td>3.01e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>stack</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>45.16</td><td>22.14</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>51.35</td><td>19.47</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</td><td>59.68</td><td>16.76</td><td>0.03</td></tr><tr><td>1,000,000 push & pop</td><td>52.04</td><td>19.22</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.20</td><td>22.63</td><td>5.61e-4</td></tr><tr><td>100,000 getWords</td><td>91.81</td><td>10.89</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.70</td><td>20.96</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>66.53</td><td>15.03</td><td>0.00</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -48,17 +48,17 @@ export class AVLTree<
extends BST<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
/**
* The constructor function initializes an AVLTree object with optional nodes and options.
* @param [nodes] - The `nodes` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
* 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>`
* 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(nodes?: Iterable<KeyOrNodeOrEntry<K, V, N>>, options?: Partial<AVLTreeOptions<K>>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: AVLTreeOptions<K>) {
super([], options);
if (nodes) super.addMany(nodes);
if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries);
}
/**

View file

@ -111,29 +111,25 @@ export class BinaryTree<
iterationType = IterationType.ITERATIVE;
/**
* The constructor function initializes a binary tree object with optional nodes and options.
* @param [nodes] - An optional iterable of KeyOrNodeOrEntry objects. These objects represent the
* The constructor function initializes a binary tree object with optional keysOrNodesOrEntries and options.
* @param [keysOrNodesOrEntries] - An optional iterable of KeyOrNodeOrEntry objects. These objects represent the
* nodes to be added to the binary tree.
* @param [options] - The `options` parameter is an optional object that can contain additional
* configuration options for the binary tree. In this case, it is of type
* `Partial<BinaryTreeOptions>`, which means that not all properties of `BinaryTreeOptions` are
* required.
*/
constructor(nodes?: Iterable<KeyOrNodeOrEntry<K, V, N>>, options?: Partial<BinaryTreeOptions<K>>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: BinaryTreeOptions<K>) {
super();
if (options) {
const { iterationType, extractor } = options;
if (iterationType) {
this.iterationType = iterationType;
}
if (extractor) {
this._extractor = extractor;
}
if (iterationType) this.iterationType = iterationType;
if (extractor) this._extractor = extractor;
}
this._size = 0;
if (nodes) this.addMany(nodes);
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);
}
protected _extractor = (key: K) => Number(key);

View file

@ -92,25 +92,23 @@ export class BST<
implements IBinaryTree<K, V, N, TREE> {
/**
* This is the constructor function for a binary search tree class in TypeScript, which initializes
* the tree with optional nodes and options.
* @param [nodes] - An optional iterable of KeyOrNodeOrEntry objects that will be added to the
* the tree with optional keysOrNodesOrEntries and options.
* @param [keysOrNodesOrEntries] - An optional iterable of KeyOrNodeOrEntry objects that will be added to the
* binary search tree.
* @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(nodes?: Iterable<KeyOrNodeOrEntry<K, V, N>>, options?: Partial<BSTOptions<K>>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: BSTOptions<K>) {
super([], options);
if (options) {
const { variant } = options;
if (variant) {
this._variant = variant;
}
if (variant) this._variant = variant;
}
this._root = undefined;
if (nodes) this.addMany(nodes);
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);
}
protected override _root?: N;

View file

@ -53,7 +53,7 @@ export class RedBlackTree<
/**
* This is the constructor function for a Red-Black Tree data structure in TypeScript, which
* initializes the tree with optional nodes and options.
* @param [nodes] - The `nodes` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
* 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,11 +61,11 @@ 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(nodes?: Iterable<KeyOrNodeOrEntry<K, V, N>>, options?: Partial<RBTreeOptions<K>>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: RBTreeOptions<K>) {
super([], options);
this._root = this.Sentinel;
if (nodes) super.addMany(nodes);
if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries);
}
protected _root: N;

View file

@ -52,9 +52,9 @@ export class TreeMultimap<
>
extends AVLTree<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
constructor(nodes?: Iterable<KeyOrNodeOrEntry<K, V, N>>, options?: Partial<TreeMultimapOptions<K>>) {
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: TreeMultimapOptions<K>) {
super([], options);
if (nodes) this.addMany(nodes);
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);
}
private _count = 0;

View file

@ -5,34 +5,35 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { EntryCallback, HashMapLinkedNode, HashMapOptions, HashMapStoreItem } from '../../types';
import type {
EntryCallback,
HashMapLinkedNode,
HashMapOptions,
HashMapStoreItem,
LinkedHashMapOptions
} from '../../types';
import { IterableEntryBase } from '../base';
import { isWeakKey, rangeCheck } from '../../utils';
/**
* 1. Key-Value Pair Storage: HashMap stores key-value pairs. Each key maps to a value.
* 2. Fast Lookup: It's used when you need to quickly find, insert, or delete elements based on a key.
* 2. Fast Lookup: It's used when you need to quickly find, insert, or delete entries based on a key.
* 3. Unique Keys: Keys are unique. If you try to insert another entry with the same key, the old entry will be replaced by the new one.
* 4. Unordered Collection: HashMap does not guarantee the order of elements, and the order may change over time.
* 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time.
*/
export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
protected _store: { [key: string]: HashMapStoreItem<K, V> } = {};
protected _objMap: Map<object, V> = new Map();
/**
* The constructor function initializes a new instance of a class with optional elements and options.
* @param elements - The `elements` parameter is an iterable containing key-value pairs `[K, V]`. It
* The constructor function initializes a new instance of a class with optional entries and options.
* @param entries - The `entries` parameter is an iterable containing key-value pairs `[K, V]`. It
* is optional and defaults to an empty array `[]`. This parameter is used to initialize the map with
* key-value pairs.
* @param [options] - The `options` parameter is an optional object that can contain additional
* configuration options for the constructor. In this case, it has one property:
*/
constructor(
elements: Iterable<[K, V]> = [],
options?: {
hashFn: (key: K) => string;
}
) {
constructor(entries: Iterable<[K, V]> = [], options?: HashMapOptions<K>) {
super();
if (options) {
const { hashFn } = options;
@ -40,8 +41,8 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
this._hashFn = hashFn;
}
}
if (elements) {
this.setMany(elements);
if (entries) {
this.setMany(entries);
}
}
@ -88,12 +89,12 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
/**
* The function "setMany" sets multiple key-value pairs in a map.
* @param elements - The `elements` parameter is an iterable containing key-value pairs. Each
* key-value pair is represented as an array with two elements: the key and the value.
* @param entries - The `entries` parameter is an iterable containing key-value pairs. Each
* key-value pair is represented as an array with two entries: the key and the value.
*/
setMany(elements: Iterable<[K, V]>): boolean[] {
setMany(entries: Iterable<[K, V]>): boolean[] {
const results: boolean[] = [];
for (const [key, value] of elements) results.push(this.set(key, value));
for (const [key, value] of entries) results.push(this.set(key, value));
return results;
}
@ -214,10 +215,6 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
return filteredMap;
}
print(): void {
console.log([...this.entries()]);
}
put(key: K, value: V): boolean {
return this.set(key, value);
}
@ -261,8 +258,8 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
}
/**
* 1. Maintaining the Order of Element Insertion: Unlike HashMap, LinkedHashMap maintains the order in which elements are inserted. Therefore, when you traverse it, elements will be returned in the order they were inserted into the map.
* 2. Based on Hash Table and Linked List: It combines the structures of a hash table and a linked list, using the hash table to ensure fast access, while maintaining the order of elements through the linked list.
* 1. Maintaining the Order of Element Insertion: Unlike HashMap, LinkedHashMap maintains the order in which entries are inserted. Therefore, when you traverse it, entries will be returned in the order they were inserted into the map.
* 2. Based on Hash Table and Linked List: It combines the structures of a hash table and a linked list, using the hash table to ensure fast access, while maintaining the order of entries through the linked list.
* 3. Time Complexity: Similar to HashMap, LinkedHashMap offers constant-time performance for get and put operations in most cases.
*/
export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
@ -271,25 +268,20 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
protected _head: HashMapLinkedNode<K, V | undefined>;
protected _tail: HashMapLinkedNode<K, V | undefined>;
protected readonly _sentinel: HashMapLinkedNode<K, V | undefined>;
protected _hashFn: (key: K) => string;
protected _objHashFn: (key: K) => object;
constructor(
elements?: Iterable<[K, V]>,
options: HashMapOptions<K> = {
hashFn: (key: K) => String(key),
objHashFn: (key: K) => <object>key
}
) {
constructor(entries?: Iterable<[K, V]>, options?: LinkedHashMapOptions<K>) {
super();
this._sentinel = <HashMapLinkedNode<K, V>>{};
this._sentinel.prev = this._sentinel.next = this._head = this._tail = this._sentinel;
const { hashFn, objHashFn } = options;
this._hashFn = hashFn;
this._objHashFn = objHashFn;
if (elements) {
for (const el of elements) {
if (options) {
const { hashFn, objHashFn } = options;
if (hashFn) this._hashFn = hashFn;
if (objHashFn) this._objHashFn = objHashFn;
}
if (entries) {
for (const el of entries) {
this.set(el[0], el[1]);
}
}
@ -547,7 +539,7 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
* Time Complexity: O(1)
* Space Complexity: O(1)
*
* The `clear` function clears all the elements in a data structure and resets its properties.
* The `clear` function clears all the entries in a data structure and resets its properties.
*/
clear(): void {
this._noObjMap = {};
@ -564,11 +556,6 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
return cloned;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
@ -596,11 +583,6 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
return filteredMap;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
@ -629,12 +611,26 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
return mappedMap;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
put(key: K, value: V): boolean {
return this.set(key, value);
}
/**
* Time Complexity: O(n), where n is the number of elements in the LinkedHashMap.
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
protected _hashFn: (key: K) => string = (key: K) => String(key);
protected _objHashFn: (key: K) => object = (key: K) => <object>key;
/**
* Time Complexity: O(n), where n is the number of entries in the LinkedHashMap.
* Space Complexity: O(1)
*
* The above function is an iterator that yields key-value pairs from a linked list.

View file

@ -1,318 +0,0 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { HashFunction } from '../../types';
export class HashTableNode<K = any, V = any> {
key: K;
value: V;
next: HashTableNode<K, V> | undefined;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
this.next = undefined;
}
}
export class HashTable<K = any, V = any> {
protected static readonly DEFAULT_CAPACITY = 16;
protected static readonly LOAD_FACTOR = 0.75;
constructor(capacity: number = HashTable.DEFAULT_CAPACITY, hashFn?: HashFunction<K>) {
this._hashFn = hashFn || this._defaultHashFn;
this._capacity = Math.max(capacity, HashTable.DEFAULT_CAPACITY);
this._size = 0;
this._buckets = new Array<HashTableNode<K, V> | undefined>(this._capacity).fill(undefined);
}
protected _capacity: number;
get capacity(): number {
return this._capacity;
}
protected _size: number;
get size(): number {
return this._size;
}
protected _buckets: Array<HashTableNode<K, V> | undefined>;
get buckets(): Array<HashTableNode<K, V> | undefined> {
return this._buckets;
}
protected _hashFn: HashFunction<K>;
get hashFn(): HashFunction<K> {
return this._hashFn;
}
/**
* The set function adds a key-value pair to the hash table, handling collisions and resizing if necessary.
* @param {K} key - The key parameter represents the key of the key-value pair that you want to insert into the hash
* table. It is of type K, which is a generic type representing the key's data type.
* @param {V} value - The parameter `value` represents the value that you want to associate with the given key in the hash
* table.
* @returns Nothing is being returned. The return type of the `put` method is `void`, which means it does not return any
* value.
*/
set(key: K, value: V): void {
const index = this._hash(key);
const newNode = new HashTableNode<K, V>(key, value);
if (!this._buckets[index]) {
this._buckets[index] = newNode;
} else {
// Handle collisions, consider using open addressing, etc.
let currentNode = this._buckets[index]!;
while (currentNode) {
if (currentNode.key === key) {
// If the key already exists, update the value
currentNode.value = value;
return;
}
if (!currentNode.next) {
break;
}
currentNode = currentNode.next;
}
// Add to the end of the linked list
currentNode.next = newNode;
}
this._size++;
// If the load factor is too high, resize the hash table
if (this._size / this._capacity >= HashTable.LOAD_FACTOR) {
this._expand();
}
}
/**
* The `get` function retrieves the value associated with a given key from a hash table.
* @param {K} key - The `key` parameter represents the key of the element that we want to retrieve from the data
* structure.
* @returns The method is returning the value associated with the given key if it exists in the hash table. If the key is
* not found, it returns `undefined`.
*/
get(key: K): V | undefined {
const index = this._hash(key);
let currentNode = this._buckets[index];
while (currentNode) {
if (currentNode.key === key) {
return currentNode.value;
}
currentNode = currentNode.next;
}
return undefined; // Key not found
}
/**
* The delete function removes a key-value pair from a hash table.
* @param {K} key - The `key` parameter represents the key of the key-value pair that needs to be removed from the hash
* table.
* @returns Nothing is being returned. The `delete` method has a return type of `void`, which means it does not return
* any value.
*/
delete(key: K): void {
const index = this._hash(key);
let currentNode = this._buckets[index];
let prevNode: HashTableNode<K, V> | undefined = undefined;
while (currentNode) {
if (currentNode.key === key) {
if (prevNode) {
prevNode.next = currentNode.next;
} else {
this._buckets[index] = currentNode.next;
}
this._size--;
currentNode.next = undefined; // Release memory
return;
}
prevNode = currentNode;
currentNode = currentNode.next;
}
}
* [Symbol.iterator](): Generator<[K, V], void, undefined> {
for (const bucket of this._buckets) {
let currentNode = bucket;
while (currentNode) {
yield [currentNode.key, currentNode.value];
currentNode = currentNode.next;
}
}
}
forEach(callback: (entry: [K, V], index: number, table: HashTable<K, V>) => void): void {
let index = 0;
for (const entry of this) {
callback(entry, index, this);
index++;
}
}
filter(predicate: (entry: [K, V], index: number, table: HashTable<K, V>) => boolean): HashTable<K, V> {
const newTable = new HashTable<K, V>();
let index = 0;
for (const [key, value] of this) {
if (predicate([key, value], index, this)) {
newTable.set(key, value);
}
index++;
}
return newTable;
}
map<T>(callback: (entry: [K, V], index: number, table: HashTable<K, V>) => T): HashTable<K, T> {
const newTable = new HashTable<K, T>();
let index = 0;
for (const [key, value] of this) {
newTable.set(key, callback([key, value], index, this));
index++;
}
return newTable;
}
reduce<T>(callback: (accumulator: T, entry: [K, V], index: number, table: HashTable<K, V>) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const entry of this) {
accumulator = callback(accumulator, entry, index, this);
index++;
}
return accumulator;
}
/**
* The function `_defaultHashFn` calculates the hash value of a given key and returns the remainder when divided by the
* capacity of the data structure.
* @param {K} key - The `key` parameter is the input value that needs to be hashed. It can be of any type, but in this
* code snippet, it is checked whether the key is a string or an object. If it is a string, the `_murmurStringHashFn`
* function is used to
* @returns the hash value of the key modulo the capacity of the data structure.
*/
protected _defaultHashFn(key: K): number {
// Can be replaced with other hash functions as needed
const hashValue = typeof key === 'string' ? this._murmurStringHashFn(key) : this._objectHash(key);
return hashValue % this._capacity;
}
/**
* The `_multiplicativeStringHashFn` function calculates a hash value for a given string key using the multiplicative
* string hash function.
* @param {K} key - The `key` parameter is the input value for which we want to calculate the hash. It can be of any
* type, as it is generic (`K`). The function converts the `key` to a string using the `String()` function.
* @returns a number, which is the result of the multiplicative string hash function applied to the input key.
*/
protected _multiplicativeStringHashFn<K>(key: K): number {
const keyString = String(key);
let hash = 0;
for (let i = 0; i < keyString.length; i++) {
const charCode = keyString.charCodeAt(i);
// Some constants for adjusting the hash function
const A = 0.618033988749895;
const M = 1 << 30; // 2^30
hash = (hash * A + charCode) % M;
}
return Math.abs(hash); // Take absolute value to ensure non-negative numbers
}
/**
* The function `_murmurStringHashFn` calculates a hash value for a given string key using the MurmurHash algorithm.
* @param {K} key - The `key` parameter is the input value for which you want to calculate the hash. It can be of any
* type, but it will be converted to a string using the `String()` function before calculating the hash.
* @returns a number, which is the hash value calculated for the given key.
*/
protected _murmurStringHashFn<K>(key: K): number {
const keyString = String(key);
const seed = 0;
let hash = seed;
for (let i = 0; i < keyString.length; i++) {
const char = keyString.charCodeAt(i);
hash = (hash ^ char) * 0x5bd1e995;
hash = (hash ^ (hash >>> 15)) * 0x27d4eb2d;
hash = hash ^ (hash >>> 15);
}
return Math.abs(hash);
}
/**
* The _hash function takes a key and returns a number.
* @param {K} key - The parameter "key" is of type K, which represents the type of the key that will be hashed.
* @returns The hash function is returning a number.
*/
protected _hash(key: K): number {
return this.hashFn(key);
}
/**
* The function calculates a hash value for a given string using the djb2 algorithm.
* @param {string} key - The `key` parameter in the `stringHash` function is a string value that represents the input for
* which we want to calculate the hash value.
* @returns a number, which is the hash value of the input string.
*/
protected _stringHash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash * 31 + key.charCodeAt(i)) & 0xffffffff;
}
return hash;
}
/**
* The function `_objectHash` takes a key and returns a hash value, using a custom hash function for objects.
* @param {K} key - The parameter "key" is of type "K", which means it can be any type. It could be a string, number,
* boolean, object, or any other type of value. The purpose of the objectHash function is to generate a hash value for
* the key, which can be used for
* @returns a number, which is the hash value of the key.
*/
protected _objectHash(key: K): number {
// If the key is an object, you can write a custom hash function
// For example, convert the object's properties to a string and use string hashing
// This is just an example; you should write a specific object hash function as needed
return this._stringHash(JSON.stringify(key));
}
/**
* The `expand` function increases the capacity of a hash table by creating a new array of buckets with double the
* capacity and rehashing all the existing key-value pairs into the new buckets.
*/
protected _expand(): void {
const newCapacity = this._capacity * 2;
const newBuckets = new Array<HashTableNode<K, V> | undefined>(newCapacity).fill(undefined);
for (const bucket of this._buckets) {
let currentNode = bucket;
while (currentNode) {
const newIndex = this._hash(currentNode.key);
const newNode = new HashTableNode<K, V>(currentNode.key, currentNode.value);
if (!newBuckets[newIndex]) {
newBuckets[newIndex] = newNode;
} else {
let currentNewNode = newBuckets[newIndex]!;
while (currentNewNode.next) {
currentNewNode = currentNewNode.next;
}
currentNewNode.next = newNode;
}
currentNode = currentNode.next;
}
}
this._buckets = newBuckets;
this._capacity = newCapacity;
}
}

View file

@ -1,2 +1 @@
export * from './hash-table';
export * from './hash-map';

View file

@ -21,23 +21,12 @@ import { IterableElementBase } from '../base';
* 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prim's minimum spanning tree algorithm, which use heaps to improve performance.
*/
export class Heap<E = any> extends IterableElementBase<E> {
options: HeapOptions<E>;
constructor(elements?: Iterable<E>, options?: HeapOptions<E>) {
constructor(elements: Iterable<E> = [], options?: HeapOptions<E>) {
super();
const defaultComparator = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
};
if (options) {
this.options = options;
} else {
this.options = {
comparator: defaultComparator
};
const { comparator } = options;
if (comparator) this._comparator = comparator;
}
if (elements) {
@ -48,6 +37,18 @@ export class Heap<E = any> extends IterableElementBase<E> {
}
}
protected _comparator = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
};
get comparator() {
return this._comparator;
}
protected _elements: E[] = [];
get elements(): E[] {
@ -278,7 +279,7 @@ export class Heap<E = any> extends IterableElementBase<E> {
* @returns A new Heap instance containing the same elements.
*/
clone(): Heap<E> {
const clonedHeap = new Heap<E>([], this.options);
const clonedHeap = new Heap<E>([], { comparator: this.comparator });
clonedHeap._elements = [...this.elements];
return clonedHeap;
}
@ -413,7 +414,7 @@ export class Heap<E = any> extends IterableElementBase<E> {
while (index > 0) {
const parent = (index - 1) >> 1;
const parentItem = this.elements[parent];
if (this.options.comparator(parentItem, element) <= 0) break;
if (this.comparator(parentItem, element) <= 0) break;
this.elements[index] = parentItem;
index = parent;
}
@ -435,11 +436,11 @@ export class Heap<E = any> extends IterableElementBase<E> {
let left = (index << 1) | 1;
const right = left + 1;
let minItem = this.elements[left];
if (right < this.elements.length && this.options.comparator(minItem, this.elements[right]) > 0) {
if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) {
left = right;
minItem = this.elements[right];
}
if (this.options.comparator(minItem, element) >= 0) break;
if (this.comparator(minItem, element) >= 0) break;
this.elements[index] = minItem;
index = left;
}

View file

@ -20,7 +20,7 @@ import { Heap } from './heap';
*/
export class MaxHeap<E = any> extends Heap<E> {
constructor(
elements?: Iterable<E>,
elements: Iterable<E> = [],
options: HeapOptions<E> = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {

View file

@ -20,7 +20,7 @@ import { Heap } from './heap';
*/
export class MinHeap<E = any> extends Heap<E> {
constructor(
elements?: Iterable<E>,
elements: Iterable<E> = [],
options: HeapOptions<E> = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {

View file

@ -35,7 +35,7 @@ export class DoublyLinkedList<E = any> extends IterableElementBase<E> {
/**
* The constructor initializes the linked list with an empty head, tail, and size.
*/
constructor(elements?: Iterable<E>) {
constructor(elements: Iterable<E> = []) {
super();
this._head = undefined;
this._tail = undefined;

View file

@ -27,11 +27,8 @@ export class SinglyLinkedList<E = any> extends IterableElementBase<E> {
/**
* The constructor initializes the linked list with an empty head, tail, and length.
*/
constructor(elements?: Iterable<E>) {
constructor(elements: Iterable<E> = []) {
super();
this._head = undefined;
this._tail = undefined;
this._size = 0;
if (elements) {
for (const el of elements) this.push(el);
}
@ -49,7 +46,7 @@ export class SinglyLinkedList<E = any> extends IterableElementBase<E> {
return this._tail;
}
protected _size: number;
protected _size: number = 0;
get size(): number {
return this._size;

View file

@ -5,6 +5,7 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { SkipLinkedListOptions } from '../../types';
export class SkipListNode<K, V> {
key: K;
@ -19,39 +20,37 @@ export class SkipListNode<K, V> {
}
export class SkipList<K, V> {
/**
* The constructor initializes a SkipList with a specified maximum level and probability.
* @param [maxLevel=16] - The `maxLevel` parameter represents the maximum level that a skip list can have. It determines
* the maximum number of levels that can be created in the skip list.
* @param [probability=0.5] - The probability parameter represents the probability of a node being promoted to a higher
* level in the skip list. It is used to determine the height of each node in the skip list.
*/
constructor(maxLevel = 16, probability = 0.5) {
this._head = new SkipListNode<K, V>(undefined as any, undefined as any, maxLevel);
this._level = 0;
this._maxLevel = maxLevel;
this._probability = probability;
constructor(elements: Iterable<[K, V]> = [], options?: SkipLinkedListOptions) {
if (options) {
const { maxLevel, probability } = options;
if (typeof maxLevel === 'number') this._maxLevel = maxLevel;
if (typeof probability === 'number') this._probability = probability;
}
if (elements) {
for (const [key, value] of elements) this.add(key, value);
}
}
protected _head: SkipListNode<K, V>;
protected _head: SkipListNode<K, V> = new SkipListNode<K, V>(undefined as any, undefined as any, this.maxLevel);
get head(): SkipListNode<K, V> {
return this._head;
}
protected _level: number;
protected _level: number = 0;
get level(): number {
return this._level;
}
protected _maxLevel: number;
protected _maxLevel: number = 16;
get maxLevel(): number {
return this._maxLevel;
}
protected _probability: number;
protected _probability: number = 0.5;
get probability(): number {
return this._probability;

View file

@ -5,6 +5,7 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { MatrixOptions } from '../../types';
export class Matrix {
/**
@ -14,16 +15,7 @@ export class Matrix {
* @param [options] - The `options` parameter is an optional object that can contain the following
* properties:
*/
constructor(
data: number[][],
options?: {
rows?: number;
cols?: number;
addFn?: (a: number, b: number) => any;
subtractFn?: (a: number, b: number) => any;
multiplyFn?: (a: number, b: number) => any;
}
) {
constructor(data: number[][], options?: MatrixOptions) {
if (options) {
const { rows, cols, addFn, subtractFn, multiplyFn } = options;
if (typeof rows === 'number' && rows > 0) this._rows = rows;

View file

@ -10,7 +10,7 @@ import { PriorityQueue } from './priority-queue';
export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
elements?: Iterable<E>,
elements: Iterable<E> = [],
options: PriorityQueueOptions<E> = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {

View file

@ -10,7 +10,7 @@ import { PriorityQueue } from './priority-queue';
export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
elements?: Iterable<E>,
elements: Iterable<E> = [],
options: PriorityQueueOptions<E> = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {

View file

@ -17,7 +17,7 @@ import { Heap } from '../heap';
* 6. Kth Largest Element in a Data Stream: Used to maintain a min-heap of size K for quickly finding the Kth largest element in stream data
*/
export class PriorityQueue<E = any> extends Heap<E> {
constructor(elements?: Iterable<E>, options?: PriorityQueueOptions<E>) {
constructor(elements: Iterable<E> = [], options?: PriorityQueueOptions<E>) {
super(elements, options);
}
}

View file

@ -5,7 +5,7 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { ElementCallback, IterableWithSizeOrLength } from '../../types';
import type { DequeOptions, ElementCallback, IterableWithSizeOrLength } from '../../types';
import { IterableElementBase } from '../base';
import { calcMinUnitsRequired, rangeCheck } from '../../utils';
@ -22,19 +22,16 @@ export class Deque<E> extends IterableElementBase<E> {
protected _bucketLast = 0;
protected _lastInBucket = 0;
protected _bucketCount = 0;
protected readonly _bucketSize: number;
protected readonly _bucketSize: number = 1 << 12;
/**
* The constructor initializes a data structure with a specified bucket size and populates it with
* elements from an iterable.
* @param elements - The `elements` parameter is an iterable object (such as an array or a Set) that
* contains the initial elements to be stored in the data structure. It can also be an object with a
* `length` property or a `size` property, which represents the number of elements in the iterable.
* @param bucketSize - The `bucketSize` parameter is the maximum number of elements that can be
* stored in each bucket. It determines the size of each bucket in the data structure.
*/
constructor(elements: IterableWithSizeOrLength<E> = [], bucketSize = 1 << 12) {
constructor(elements: IterableWithSizeOrLength<E> = [], options?: DequeOptions) {
super();
if (options) {
const { bucketSize } = options;
if (typeof bucketSize === 'number') this._bucketSize = bucketSize;
}
let _size: number;
if ('length' in elements) {
if (elements.length instanceof Function) _size = elements.length();
@ -44,7 +41,6 @@ export class Deque<E> extends IterableElementBase<E> {
else _size = elements.size;
}
this._bucketSize = bucketSize;
this._bucketCount = calcMinUnitsRequired(_size, this._bucketSize) || 1;
for (let i = 0; i < this._bucketCount; ++i) {
this._buckets.push(new Array(this._bucketSize));
@ -637,7 +633,7 @@ export class Deque<E> extends IterableElementBase<E> {
* satisfy the given predicate function.
*/
filter(predicate: ElementCallback<E, boolean>, thisArg?: any): Deque<E> {
const newDeque = new Deque<E>([], this._bucketSize);
const newDeque = new Deque<E>([], { bucketSize: this._bucketSize });
let index = 0;
for (const el of this) {
if (predicate.call(thisArg, el, index, this)) {
@ -666,7 +662,7 @@ export class Deque<E> extends IterableElementBase<E> {
* @returns a new Deque object with the mapped values.
*/
map<T>(callback: ElementCallback<E, T>, thisArg?: any): Deque<T> {
const newDeque = new Deque<T>([], this._bucketSize);
const newDeque = new Deque<T>([], { bucketSize: this._bucketSize });
let index = 0;
for (const el of this) {
newDeque.push(callback.call(thisArg, el, index, this));

View file

@ -13,29 +13,30 @@ import { SinglyLinkedList } from '../linked-list';
* 3. Uses: Queues are commonly used to manage a series of tasks or elements that need to be processed in order. For example, managing task queues in a multi-threaded environment, or in algorithms for data structures like trees and graphs for breadth-first search.
* 4. Task Scheduling: Managing the order of task execution in operating systems or applications.
* 5. Data Buffering: Acting as a buffer for data packets in network communication.
* 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store nodes that are to be visited.
* 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store elements that are to be visited.
* 7. Real-time Queuing: Like queuing systems in banks or supermarkets.
*/
export class Queue<E = any> extends IterableElementBase<E> {
/**
* The constructor initializes an instance of a class with an optional array of elements and sets the offset to 0.
* @param {E[]} [elements] - The `elements` parameter is an optional array of elements of type `E`. If provided, it
* will be used to initialize the `_nodes` property of the class. If not provided, the `_nodes` property will be
* will be used to initialize the `_elements` property of the class. If not provided, the `_elements` property will be
* initialized as an empty array.
*/
constructor(elements?: E[]) {
constructor(elements: Iterable<E> = []) {
super();
this._nodes = elements || [];
this._offset = 0;
if (elements) {
for (const el of elements) this.push(el);
}
}
protected _nodes: E[];
protected _elements: E[] = [];
get nodes(): E[] {
return this._nodes;
get elements(): E[] {
return this._elements;
}
protected _offset: number;
protected _offset: number = 0;
get offset(): number {
return this._offset;
@ -46,19 +47,19 @@ export class Queue<E = any> extends IterableElementBase<E> {
* @returns {number} The size of the array, which is the difference between the length of the array and the offset.
*/
get size(): number {
return this.nodes.length - this.offset;
return this.elements.length - this.offset;
}
/**
* Time Complexity: O(1) - constant time as it retrieves the value at the current offset.
* Space Complexity: O(1) - no additional space is used.
*
* The `first` function returns the first element of the array `_nodes` if it exists, otherwise it returns `undefined`.
* @returns The `get first()` method returns the first element of the data structure, represented by the `_nodes` array at
* The `first` function returns the first element of the array `_elements` if it exists, otherwise it returns `undefined`.
* @returns The `get first()` method returns the first element of the data structure, represented by the `_elements` array at
* the `_offset` index. If the data structure is empty (size is 0), it returns `undefined`.
*/
get first(): E | undefined {
return this.size > 0 ? this.nodes[this.offset] : undefined;
return this.size > 0 ? this.elements[this.offset] : undefined;
}
/**
@ -71,11 +72,11 @@ export class Queue<E = any> extends IterableElementBase<E> {
* Space Complexity: O(1) - no additional space is used.
*
* The `last` function returns the last element in an array-like data structure, or undefined if the structure is empty.
* @returns The method `get last()` returns the last element of the `_nodes` array if the array is not empty. If the
* @returns The method `get last()` returns the last element of the `_elements` array if the array is not empty. If the
* array is empty, it returns `undefined`.
*/
get last(): E | undefined {
return this.size > 0 ? this.nodes[this.nodes.length - 1] : undefined;
return this.size > 0 ? this.elements[this.elements.length - 1] : undefined;
}
/**
@ -109,7 +110,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* @returns The `add` method is returning a `Queue<E>` object.
*/
push(element: E): boolean {
this.nodes.push(element);
this.elements.push(element);
return true;
}
@ -132,11 +133,11 @@ export class Queue<E = any> extends IterableElementBase<E> {
const first = this.first;
this._offset += 1;
if (this.offset * 2 < this.nodes.length) return first;
if (this.offset * 2 < this.elements.length) return first;
// only delete dequeued elements when reaching half size
// to decrease latency of shifting elements.
this._nodes = this.nodes.slice(this.offset);
this._elements = this.elements.slice(this.offset);
this._offset = 0;
return first;
}
@ -150,8 +151,8 @@ export class Queue<E = any> extends IterableElementBase<E> {
* Time Complexity: O(1) - constant time as it retrieves the value at the current offset.
* Space Complexity: O(1) - no additional space is used.
*
* The `peek` function returns the first element of the array `_nodes` if it exists, otherwise it returns `undefined`.
* @returns The `peek()` method returns the first element of the data structure, represented by the `_nodes` array at
* The `peek` function returns the first element of the array `_elements` if it exists, otherwise it returns `undefined`.
* @returns The `peek()` method returns the first element of the data structure, represented by the `_elements` array at
* the `_offset` index. If the data structure is empty (size is 0), it returns `undefined`.
*/
peek(): E | undefined {
@ -168,7 +169,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* Space Complexity: O(1) - no additional space is used.
*
* The `peekLast` function returns the last element in an array-like data structure, or undefined if the structure is empty.
* @returns The method `peekLast()` returns the last element of the `_nodes` array if the array is not empty. If the
* @returns The method `peekLast()` returns the last element of the `_elements` array if the array is not empty. If the
* array is empty, it returns `undefined`.
*/
peekLast(): E | undefined {
@ -219,7 +220,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* @param index
*/
getAt(index: number): E | undefined {
return this.nodes[index];
return this.elements[index];
}
/**
@ -247,18 +248,18 @@ export class Queue<E = any> extends IterableElementBase<E> {
* Time Complexity: O(1) - constant time as it returns a shallow copy of the internal array.
* Space Complexity: O(n) - where n is the number of elements in the queue.
*
* The toArray() function returns an array of elements from the current offset to the end of the _nodes array.
* The toArray() function returns an array of elements from the current offset to the end of the _elements array.
* @returns An array of type E is being returned.
*/
toArray(): E[] {
return this.nodes.slice(this.offset);
return this.elements.slice(this.offset);
}
/**
* The clear function resets the nodes array and offset to their initial values.
* The clear function resets the elements array and offset to their initial values.
*/
clear(): void {
this._nodes = [];
this._elements = [];
this._offset = 0;
}
@ -275,7 +276,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* @returns The `clone()` method is returning a new instance of the `Queue` class.
*/
clone(): Queue<E> {
return new Queue(this.nodes.slice(this.offset));
return new Queue(this.elements.slice(this.offset));
}
/**
@ -345,7 +346,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
*/
protected* _getIterator(): IterableIterator<E> {
for (const item of this.nodes) {
for (const item of this.elements) {
yield item;
}
}

View file

@ -23,17 +23,14 @@ export class Stack<E = any> extends IterableElementBase<E> {
* of elements of type `E`. It is used to initialize the `_elements` property of the class. If the `elements` parameter
* is provided and is an array, it is assigned to the `_elements
*/
constructor(elements?: Iterable<E>) {
constructor(elements: Iterable<E> = []) {
super();
this._elements = [];
if (elements) {
for (const el of elements) {
this.push(el);
}
for (const el of elements) this.push(el);
}
}
protected _elements: E[];
protected _elements: E[] = [];
get elements(): E[] {
return this._elements;

View file

@ -5,7 +5,7 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type { ElementCallback } from '../../types';
import type { ElementCallback, TrieOptions } from '../../types';
import { IterableElementBase } from '../base';
/**
@ -38,31 +38,30 @@ export class TrieNode {
* 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> {
constructor(words?: string[], caseSensitive = true) {
constructor(words: Iterable<string> = [], options?: TrieOptions) {
super();
this._root = new TrieNode('');
this._caseSensitive = caseSensitive;
this._size = 0;
if (options) {
const { caseSensitive } = options;
if (caseSensitive !== undefined) this._caseSensitive = caseSensitive;
}
if (words) {
for (const word of words) {
this.add(word);
}
for (const word of words) this.add(word);
}
}
protected _size: number;
protected _size: number = 0;
get size(): number {
return this._size;
}
protected _caseSensitive: boolean;
protected _caseSensitive: boolean = true;
get caseSensitive(): boolean {
return this._caseSensitive;
}
protected _root: TrieNode;
protected _root: TrieNode = new TrieNode('');
get root() {
return this._root;

View file

@ -6,6 +6,6 @@ export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K,
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 BinaryTreeOptions<K> = {
iterationType: IterationType,
extractor: (key: K) => number
iterationType?: IterationType,
extractor?: (key: K) => number
}

View file

@ -7,5 +7,5 @@ export type BSTNodeNested<K, V> = BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTN
export type BSTNested<K, V, N extends BSTNode<K, V, N>> = BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, BST<K, V, N, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BSTOptions<K> = BinaryTreeOptions<K> & {
variant: BSTVariant
variant?: BSTVariant
}

View file

@ -5,4 +5,4 @@ export type TreeMultimapNodeNested<K, V> = TreeMultimapNode<K, V, TreeMultimapNo
export type TreeMultimapNested<K, V, N extends TreeMultimapNode<K, V, N>> = TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, TreeMultimap<K, V, N, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type TreeMultimapOptions<K> = Omit<AVLTreeOptions<K>, 'isMergeDuplicatedNodeByKey'> & {}
export type TreeMultimapOptions<K> = AVLTreeOptions<K> & {}

View file

@ -5,9 +5,13 @@ export type HashMapLinkedNode<K, V> = {
prev: HashMapLinkedNode<K, V>;
};
export type LinkedHashMapOptions<K> = {
hashFn?: (key: K) => string;
objHashFn?: (key: K) => object;
};
export type HashMapOptions<K> = {
hashFn: (key: K) => string;
objHashFn: (key: K) => object;
hashFn?: (key: K) => string;
};
export type HashMapStoreItem<K, V> = { key: K; value: V };

View file

@ -1 +0,0 @@
export {};

View file

@ -1,4 +1,3 @@
export * from './hash-map';
export * from './hash-table';
export type HashFunction<K> = (key: K) => number;

View file

@ -1,3 +1,3 @@
import { Comparator } from '../../common';
export type HeapOptions<T> = { comparator: Comparator<T> };
export type HeapOptions<T> = { comparator?: Comparator<T> };

View file

@ -1,2 +1,3 @@
export * from './singly-linked-list';
export * from './doubly-linked-list';
export * from './skip-linked-list';

View file

@ -1 +1 @@
export {};
export type SkipLinkedListOptions = { maxLevel?: number; probability?: number };

View file

@ -1 +1,2 @@
export * from './navigator';
export * from './matrix';

View file

@ -1 +1,7 @@
export {};
export type MatrixOptions = {
rows?: number;
cols?: number;
addFn?: (a: number, b: number) => any;
subtractFn?: (a: number, b: number) => any;
multiplyFn?: (a: number, b: number) => any;
};

View file

@ -1 +0,0 @@
export {};

View file

@ -1 +0,0 @@
export {};

View file

@ -1 +1 @@
export {};
export type DequeOptions = { bucketSize?: number };

View file

@ -1 +1 @@
export {};
export type TrieOptions = { caseSensitive?: boolean };

View file

@ -1,238 +0,0 @@
import { HashTable, HashTableNode } from '../../../../src';
describe('HashNode', () => {
it('should create a HashNode with key and value', () => {
const key = 'testKey';
const value = 'testValue';
const hashNode = new HashTableNode(key, value);
expect(hashNode.key).toBe(key);
expect(hashNode.value).toBe(value);
expect(hashNode.next).toBe(undefined);
});
});
describe('HashTable', () => {
it('should initialize with default capacity', () => {
const hashTable = new HashTable<string, string>();
expect(hashTable.capacity).toBe(16);
expect(hashTable.buckets).toEqual(new Array(16).fill(undefined));
expect(hashTable.hashFn('a')).toBe(6);
expect(hashTable.capacity).toBe(16);
expect(hashTable.size).toBe(0);
expect(hashTable.buckets.length).toBe(16);
});
it('should initialize with custom capacity', () => {
const customCapacity = 500;
const hashTable = new HashTable<string, string>(customCapacity);
expect(hashTable.capacity).toBe(customCapacity);
expect(hashTable.size).toBe(0);
expect(hashTable.buckets.length).toBe(customCapacity);
});
it('should put and get values correctly', () => {
const hashTable = new HashTable<string, string>();
const key = 'testKey';
const value = 'testValue';
hashTable.set(key, value);
const retrievedValue = hashTable.get(key);
expect(retrievedValue).toBe(value);
});
it('should handle collisions by chaining', () => {
const hashTable = new HashTable<string, string>();
const key1 = 'testKey1';
const value1 = 'testValue1';
const key2 = 'testKey2';
const value2 = 'testValue2';
hashTable.set(key1, value1);
hashTable.set(key2, value2);
const retrievedValue1 = hashTable.get(key1);
const retrievedValue2 = hashTable.get(key2);
expect(retrievedValue1).toBe(value1);
expect(retrievedValue2).toBe(value2);
});
it('should update value for an existing key', () => {
const hashTable = new HashTable<string, string>();
const key = 'testKey';
const initialValue = 'testValue1';
const updatedValue = 'testValue2';
hashTable.set(key, initialValue);
hashTable.set(key, updatedValue);
const retrievedValue = hashTable.get(key);
expect(retrievedValue).toBe(updatedValue);
});
it('should return undefined for non-existent key', () => {
const hashTable = new HashTable<string, string>();
const key = 'nonExistentKey';
const retrievedValue = hashTable.get(key);
expect(retrievedValue).toBeUndefined();
});
it('should delete key-value pair correctly', () => {
const hashTable = new HashTable<string, string>();
const key = 'testKey';
const value = 'testValue';
hashTable.set(key, value);
hashTable.delete(key);
const retrievedValue = hashTable.get(key);
expect(retrievedValue).toBeUndefined();
expect(hashTable.size).toBe(0);
});
});
describe('HashTable', () => {
let hashTable: HashTable<string, number>;
beforeEach(() => {
hashTable = new HashTable<string, number>();
});
it('should insert and retrieve values correctly', () => {
hashTable.set('one', 1);
hashTable.set('two', 2);
expect(hashTable.get('one')).toBe(1);
expect(hashTable.get('two')).toBe(2);
});
it('should update values correctly', () => {
hashTable.set('one', 1);
expect(hashTable.get('one')).toBe(1);
hashTable.set('one', 100); // Update the value
expect(hashTable.get('one')).toBe(100);
});
it('should handle collisions correctly', () => {
hashTable = new HashTable<string, number>(1); // Set a small capacity to force collisions
hashTable.set('one', 1);
hashTable.set('two', 2);
expect(hashTable.get('one')).toBe(1);
expect(hashTable.get('two')).toBe(2);
});
it('should delete values correctly', () => {
hashTable.set('one', 1);
hashTable.set('two', 2);
hashTable.delete('one');
expect(hashTable.get('one')).toBeUndefined();
expect(hashTable.get('two')).toBe(2);
});
it('should handle non-existent keys correctly', () => {
expect(hashTable.get('non-existent')).toBeUndefined();
hashTable.delete('non-existent'); // Removing a non-existent key should not cause errors
});
it('should handle custom hash function correctly', () => {
// const customHashFn = () => {
// // Custom hash function that returns a fixed value for all keys
// return 42;
// };
hashTable = new HashTable<string, number>(16);
hashTable.set('one', 1);
expect(hashTable.get('one')).toBe(1);
expect(hashTable.get('two')).toBeUndefined();
});
it('should expand when load factor exceeds threshold', () => {
hashTable = new HashTable<string, number>(2); // Set a small capacity to trigger expansion
hashTable.set('one', 1);
hashTable.set('two', 2);
hashTable.set('three', 3); // This should trigger an expansion
expect(hashTable.capacity).toBe(16);
expect(hashTable.get('one')).toBe(1);
expect(hashTable.get('two')).toBe(2);
expect(hashTable.get('three')).toBe(3);
});
});
describe('HashTable performance', function () {
it('Items set performance', function () {
const mag = 100000;
const ht = new HashTable();
// const s = performance.now();
for (let i = 0; i < mag; i++) {
ht.set(i, i);
}
// const s1 = performance.now();
const map = new Map();
for (let i = 0; i < mag; i++) {
map.set(i, i);
}
});
});
describe('HashTable methods', () => {
let hashTable: HashTable<string, string>;
beforeEach(() => {
hashTable = new HashTable();
for (let i = 0; i < 10; i++) {
hashTable.set(`key${i}`, `value${i}`);
}
});
test('should retrieve correct values with get method', () => {
for (let i = 0; i < 10; i++) {
expect(hashTable.get(`key${i}`)).toBe(`value${i}`);
}
});
// test('forEach should apply a function to each key-value pair', () => {
// const mockCallback = jest.fn();
// hashTable.forEach(mockCallback);
//
// expect(mockCallback.mock.calls.length).toBe(10);
// for (let i = 0; i < 10; i++) {
// // Check whether each key-value pair has been called before, regardless of the order
// const call = mockCallback.mock.calls.find(call => call[1] === `value${i}`);
// expect(call).toBeTruthy();
// expect(call[0]).toBe(`key${i}`);
// }
// });
test('filter should return a new HashTable with elements that satisfy the condition', () => {
const filtered = hashTable.filter(([key]) => key.endsWith('1') || key.endsWith('3'));
expect(filtered.size).toBe(2);
expect(filtered.get('key1')).toBe('value1');
expect(filtered.get('key3')).toBe('value3');
});
test('map should return a new HashTable with mapped values', () => {
const mapped = hashTable.map(([, value]) => value.toUpperCase());
for (let i = 0; i < 10; i++) {
expect(mapped.get(`key${i}`)).toBe(`value${i}`.toUpperCase());
}
});
test('reduce should accumulate values based on the reducer function', () => {
const result = hashTable.reduce((acc, [, value]) => `${acc}-${value}`, '');
expect(result).toBe('-value5-value7-value3-value4-value6-value0-value2-value8-value1-value9');
});
});

View file

@ -32,7 +32,7 @@ describe('SkipList', () => {
skipList.delete(2);
expect(skipList.get(2)).toBeUndefined(); // 修改这里的断言
expect(skipList.get(2)).toBeUndefined();
});
it('should handle random data correctly', () => {

View file

@ -231,10 +231,10 @@ describe('Deque - Additional Operations', () => {
});
describe('Deque - push Method', () => {
let deque: Deque<number>;
const bucketSize = 10; // 假设的 bucket 大小
const bucketSize = 10;
beforeEach(() => {
deque = new Deque<number>([], bucketSize);
deque = new Deque<number>([], { bucketSize });
});
test('push should add an element when deque is empty', () => {
@ -276,7 +276,7 @@ describe('Deque - pop Method', () => {
const bucketSize = 10;
beforeEach(() => {
deque = new Deque<number>([], bucketSize);
deque = new Deque<number>([], { bucketSize });
});
test('pop should remove and return the last element', () => {
@ -307,7 +307,7 @@ describe('Deque - unshift Method', () => {
const bucketSize = 10;
beforeEach(() => {
deque = new Deque<number>([], bucketSize);
deque = new Deque<number>([], { bucketSize });
});
test('unshift should add an element to the beginning when deque is empty', () => {
@ -339,7 +339,7 @@ describe('Deque - shift Method', () => {
const bucketSize = 10;
beforeEach(() => {
deque = new Deque<number>([], bucketSize);
deque = new Deque<number>([], { bucketSize });
});
test('shift should remove and return the first element', () => {

View file

@ -778,7 +778,7 @@ describe('Trie operations', () => {
});
it('Case Sensitivity', () => {
const caseInsensitiveTrie = new Trie(['apple', 'Banana'], false);
const caseInsensitiveTrie = new Trie(['apple', 'Banana'], { caseSensitive: false });
expect(caseInsensitiveTrie.has('APPLE')).toBe(true);
expect(caseInsensitiveTrie.has('banana')).toBe(true);
expect(caseInsensitiveTrie.has('Cherry')).toBe(false);