mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
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:
parent
fdf5d06245
commit
c949b5b741
|
@ -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
|
||||
|
||||
|
|
26
README.md
26
README.md
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,2 +1 @@
|
|||
export * from './hash-table';
|
||||
export * from './hash-map';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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> & {}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export {};
|
|
@ -1,4 +1,3 @@
|
|||
export * from './hash-map';
|
||||
export * from './hash-table';
|
||||
|
||||
export type HashFunction<K> = (key: K) => number;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { Comparator } from '../../common';
|
||||
|
||||
export type HeapOptions<T> = { comparator: Comparator<T> };
|
||||
export type HeapOptions<T> = { comparator?: Comparator<T> };
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './singly-linked-list';
|
||||
export * from './doubly-linked-list';
|
||||
export * from './skip-linked-list';
|
||||
|
|
|
@ -1 +1 @@
|
|||
export {};
|
||||
export type SkipLinkedListOptions = { maxLevel?: number; probability?: number };
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export * from './navigator';
|
||||
export * from './matrix';
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export {};
|
|
@ -1 +0,0 @@
|
|||
export {};
|
|
@ -1 +1 @@
|
|||
export {};
|
||||
export type DequeOptions = { bucketSize?: number };
|
||||
|
|
|
@ -1 +1 @@
|
|||
export {};
|
||||
export type TrieOptions = { caseSensitive?: boolean };
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue