mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 04:44:04 +00:00
Compare commits
5 commits
fde5af1f1c
...
c3122e7fc7
Author | SHA1 | Date | |
---|---|---|---|
c3122e7fc7 | |||
080a671de1 | |||
713b145a1c | |||
a0623db58c | |||
a1c468b19b |
|
@ -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.53.5](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
|
||||
## [v1.53.6](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
|
||||
|
||||
### Changes
|
||||
|
||||
|
|
47
README.md
47
README.md
|
@ -832,43 +832,34 @@ Version 11.7.9
|
|||
[//]: # (No deletion!!! Start of Replace Section)
|
||||
<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</td><td>6.63</td><td>150.92</td><td>3.07e-4</td></tr><tr><td>100,000 add & poll</td><td>35.09</td><td>28.50</td><td>8.68e-4</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>89.65</td><td>11.15</td><td>0.00</td></tr><tr><td>100,000 add randomly</td><td>92.74</td><td>10.78</td><td>0.01</td></tr><tr><td>100,000 get</td><td>1.09</td><td>921.21</td><td>7.98e-5</td></tr><tr><td>100,000 iterator</td><td>25.76</td><td>38.82</td><td>0.00</td></tr><tr><td>100,000 add & delete orderly</td><td>159.43</td><td>6.27</td><td>0.02</td></tr><tr><td>100,000 add & delete randomly</td><td>239.83</td><td>4.17</td><td>0.00</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>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>45.01</td><td>22.22</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>6.24</td><td>160.17</td><td>0.01</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2227.55</td><td>0.45</td><td>0.22</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>20.21</td><td>49.47</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>26.85</td><td>37.24</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>27.57</td><td>36.27</td><td>0.00</td></tr><tr><td>100,000 push & shift</td><td>2.61</td><td>382.64</td><td>4.60e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2152.90</td><td>0.46</td><td>0.22</td></tr><tr><td>100,000 unshift & shift</td><td>2.51</td><td>398.74</td><td>3.60e-4</td></tr><tr><td>Native JS Array 100,000 unshift & shift</td><td>4376.45</td><td>0.23</td><td>0.30</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>86.79</td><td>11.52</td><td>0.03</td></tr><tr><td>Native JS Map 1,000,000 set</td><td>207.96</td><td>4.81</td><td>0.01</td></tr><tr><td>Native JS Set 1,000,000 add</td><td>169.32</td><td>5.91</td><td>0.02</td></tr><tr><td>1,000,000 set & get</td><td>81.30</td><td>12.30</td><td>0.02</td></tr><tr><td>Native JS Map 1,000,000 set & get</td><td>272.31</td><td>3.67</td><td>0.01</td></tr><tr><td>Native JS Set 1,000,000 add & has</td><td>237.78</td><td>4.21</td><td>0.02</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>374.05</td><td>2.67</td><td>0.06</td></tr><tr><td>Native JS Map 1,000,000 ObjKey set & get</td><td>345.51</td><td>2.89</td><td>0.06</td></tr><tr><td>Native JS Set 1,000,000 ObjKey add & has</td><td>286.45</td><td>3.49</td><td>0.05</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>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>41.60</td><td>24.04</td><td>5.38e-4</td></tr><tr><td>100,000 getWords</td><td>81.04</td><td>12.34</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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>6.85</td><td>0.01</td><td>3.38e-4</td></tr><tr><td>100,000 add & poll</td><td>35.35</td><td>0.04</td><td>8.44e-4</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>avl-tree</span></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>299.03</td><td>3.34</td><td>0.00</td></tr><tr><td>100,000 add randomly</td><td>364.39</td><td>2.74</td><td>0.02</td></tr><tr><td>100,000 get</td><td>1.08</td><td>921.78</td><td>8.08e-5</td></tr><tr><td>100,000 iterator</td><td>27.60</td><td>36.23</td><td>0.00</td></tr><tr><td>100,000 add & delete orderly</td><td>488.82</td><td>2.05</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>649.46</td><td>1.54</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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>302.89</td><td>0.30</td><td>0.01</td></tr><tr><td>100,000 add randomly</td><td>381.83</td><td>0.38</td><td>0.00</td></tr><tr><td>100,000 get</td><td>0.60</td><td>5.95e-4</td><td>2.33e-4</td></tr><tr><td>100,000 getNode</td><td>150.61</td><td>0.15</td><td>0.00</td></tr><tr><td>100,000 iterator</td><td>28.23</td><td>0.03</td><td>0.00</td></tr><tr><td>100,000 add & delete orderly</td><td>505.57</td><td>0.51</td><td>0.01</td></tr><tr><td>100,000 add & delete randomly</td><td>677.36</td><td>0.68</td><td>0.00</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>binary-tree-overall</span></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 RBTree add randomly</td><td>7.76</td><td>128.90</td><td>1.09e-4</td></tr><tr><td>10,000 RBTree get randomly</td><td>0.11</td><td>9328.86</td><td>5.44e-6</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>21.72</td><td>46.04</td><td>2.07e-4</td></tr><tr><td>10,000 AVLTree add randomly</td><td>27.45</td><td>36.43</td><td>3.65e-4</td></tr><tr><td>10,000 AVLTree get randomly</td><td>0.11</td><td>9432.49</td><td>4.04e-7</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>51.03</td><td>19.60</td><td>6.60e-4</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>directed-graph</span></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 addVertex</td><td>0.10</td><td>1.03e+4</td><td>1.04e-6</td></tr><tr><td>1,000 addEdge</td><td>6.01</td><td>166.29</td><td>1.12e-4</td></tr><tr><td>1,000 getVertex</td><td>0.10</td><td>1.04e+4</td><td>1.71e-6</td></tr><tr><td>1,000 getEdge</td><td>23.72</td><td>42.15</td><td>0.00</td></tr><tr><td>tarjan</td><td>194.37</td><td>5.14</td><td>0.00</td></tr><tr><td>topologicalSort</td><td>152.91</td><td>6.54</td><td>0.02</td></tr></table></div>
|
||||
<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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>212.77</td><td>0.21</td><td>9.84e-4</td></tr><tr><td>100,000 add randomly</td><td>163.70</td><td>0.16</td><td>0.00</td></tr><tr><td>100,000 get</td><td>1.19</td><td>0.00</td><td>2.44e-4</td></tr><tr><td>100,000 getNode</td><td>347.39</td><td>0.35</td><td>0.01</td></tr><tr><td>100,000 node mode add randomly</td><td>162.26</td><td>0.16</td><td>0.00</td></tr><tr><td>100,000 node mode get</td><td>344.90</td><td>0.34</td><td>0.00</td></tr><tr><td>100,000 iterator</td><td>27.48</td><td>0.03</td><td>0.00</td></tr><tr><td>100,000 add & delete orderly</td><td>386.33</td><td>0.39</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>520.66</td><td>0.52</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>188.44</td><td>5.31</td><td>0.04</td></tr><tr><td>1,000,000 unshift</td><td>177.48</td><td>5.63</td><td>0.02</td></tr><tr><td>1,000,000 unshift & shift</td><td>161.09</td><td>6.21</td><td>0.04</td></tr><tr><td>1,000,000 addBefore</td><td>277.84</td><td>3.60</td><td>0.08</td></tr></table></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>179.28</td><td>0.18</td><td>0.02</td></tr><tr><td>1,000,000 unshift</td><td>197.22</td><td>0.20</td><td>0.05</td></tr><tr><td>1,000,000 unshift & shift</td><td>153.16</td><td>0.15</td><td>0.00</td></tr><tr><td>1,000,000 addBefore</td><td>247.30</td><td>0.25</td><td>0.03</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>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>193.99</td><td>5.15</td><td>0.05</td></tr><tr><td>10,000 push & pop</td><td>232.82</td><td>4.30</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>285.44</td><td>3.50</td><td>0.02</td></tr></table></div>
|
||||
<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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000 addVertex</td><td>0.10</td><td>9.92e-5</td><td>1.16e-6</td></tr><tr><td>1,000 addEdge</td><td>6.44</td><td>0.01</td><td>0.00</td></tr><tr><td>1,000 getVertex</td><td>0.10</td><td>9.82e-5</td><td>1.13e-6</td></tr><tr><td>1,000 getEdge</td><td>22.60</td><td>0.02</td><td>0.00</td></tr><tr><td>tarjan</td><td>186.56</td><td>0.19</td><td>0.00</td></tr><tr><td>topologicalSort</td><td>145.42</td><td>0.15</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</td><td>30.02</td><td>33.31</td><td>2.68e-4</td></tr><tr><td>100,000 add & poll</td><td>89.21</td><td>11.21</td><td>0.00</td></tr></table></div>
|
||||
<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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>47.74</td><td>0.05</td><td>0.02</td></tr><tr><td>100,000 push & shift</td><td>5.39</td><td>0.01</td><td>1.25e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2225.50</td><td>2.23</td><td>0.10</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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>22.88</td><td>0.02</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>27.95</td><td>0.03</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>29.83</td><td>0.03</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>2.71</td><td>0.00</td><td>9.03e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2182.03</td><td>2.18</td><td>0.04</td></tr><tr><td>100,000 unshift & shift</td><td>2.61</td><td>0.00</td><td>8.71e-4</td></tr><tr><td>Native JS Array 100,000 unshift & shift</td><td>4185.90</td><td>4.19</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'>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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000,000 set</td><td>253.45</td><td>0.25</td><td>0.07</td></tr><tr><td>Native JS Map 1,000,000 set</td><td>228.90</td><td>0.23</td><td>0.02</td></tr><tr><td>Native JS Set 1,000,000 add</td><td>179.65</td><td>0.18</td><td>0.01</td></tr><tr><td>1,000,000 set & get</td><td>234.96</td><td>0.23</td><td>0.06</td></tr><tr><td>Native JS Map 1,000,000 set & get</td><td>284.90</td><td>0.28</td><td>0.01</td></tr><tr><td>Native JS Set 1,000,000 add & has</td><td>254.90</td><td>0.25</td><td>0.03</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>403.74</td><td>0.40</td><td>0.10</td></tr><tr><td>Native JS Map 1,000,000 ObjKey set & get</td><td>340.18</td><td>0.34</td><td>0.07</td></tr><tr><td>Native JS Set 1,000,000 ObjKey add & has</td><td>300.25</td><td>0.30</td><td>0.06</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>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>100,000 push</td><td>44.11</td><td>0.04</td><td>8.55e-4</td></tr><tr><td>100,000 getWords</td><td>86.67</td><td>0.09</td><td>0.00</td></tr></table></div>
|
||||
</div><div class="json-to-html-collapse clearfix 0">
|
||||
<div class='collapsible level0' ><span class='json-to-html-label'>stack</span></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>42.73</td><td>23.40</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>50.52</td><td>19.79</td><td>0.02</td></tr></table></div>
|
||||
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>sample mean (secs)</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>43.18</td><td>0.04</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>48.40</td><td>0.05</td><td>0.02</td></tr></table></div>
|
||||
</div>
|
||||
|
||||
[//]: # (No deletion!!! End of Replace Section)
|
||||
|
|
52
package-lock.json
generated
52
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.53.5",
|
||||
"version": "1.53.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.53.5",
|
||||
"version": "1.53.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.2",
|
||||
|
@ -19,11 +19,11 @@
|
|||
"@typescript-eslint/eslint-plugin": "^8.12.1",
|
||||
"@typescript-eslint/parser": "^8.12.1",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"avl-tree-typed": "^1.53.4",
|
||||
"avl-tree-typed": "^1.53.5",
|
||||
"benchmark": "^2.1.4",
|
||||
"binary-tree-typed": "^1.53.4",
|
||||
"bst-typed": "^1.53.4",
|
||||
"data-structure-typed": "^1.53.4",
|
||||
"binary-tree-typed": "^1.53.5",
|
||||
"bst-typed": "^1.53.5",
|
||||
"data-structure-typed": "^1.53.5",
|
||||
"dependency-cruiser": "^16.5.0",
|
||||
"doctoc": "^2.2.1",
|
||||
"eslint": "^9.13.0",
|
||||
|
@ -32,7 +32,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"heap-typed": "^1.53.4",
|
||||
"heap-typed": "^1.53.5",
|
||||
"istanbul-badges-readme": "^1.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"js-sdsl": "^4.4.2",
|
||||
|
@ -3437,13 +3437,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/avl-tree-typed": {
|
||||
"version": "1.53.4",
|
||||
"resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.4.tgz",
|
||||
"integrity": "sha512-dZMEutl5xMp/n54V6W6SugCXdmfcgy+6esqlpjG47nQfrQ3WMQzx2oytMWRZa6Y+nDtDNefbwlEOO/fLKZUHyQ==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-BkJgD5S4ALEl4ytKKVvqz3Qo/19U2bxnOUyZxy3dMgXNh20vaPZAJ82bFRX2lAoBFBDmIgtWp/klxC0CrDO6nQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.4"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
|
@ -3602,13 +3602,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/binary-tree-typed": {
|
||||
"version": "1.53.4",
|
||||
"resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.4.tgz",
|
||||
"integrity": "sha512-/yDQUWHdT1HQBkMVTGZuo+Nn/nx1JnV5ERBFIKCdDVBvG2qKmTa5RlSC7rnjLzLsQz28qVHvisjoRYBhcQpCWg==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-4DSrFAlykAMRF2w/hqaPG+vzsQLKq8TTnVBTD2c9Ew680+jHcanypDb6mzW0WJWSgA+v5V9IEX7P6Zz6/vS1rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.4"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
|
@ -3691,13 +3691,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/bst-typed": {
|
||||
"version": "1.53.4",
|
||||
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.4.tgz",
|
||||
"integrity": "sha512-hjTK/CcW2hw0yAtazhFOl7lsNq598FuNwCgbcjpFgXk51swLwJ5P5Sm70UzwVns1LSdIulTK49zil9tGs6zv1g==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-poYNwY4rwHinpNCQDsTv631XGUNJh/wvklLzaok+Nt+XV1PsH2e8VATaTCUMdTwLFrDn7jGFoY8mJkJjmr0s2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.4"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
|
@ -4069,9 +4069,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/data-structure-typed": {
|
||||
"version": "1.53.4",
|
||||
"resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.4.tgz",
|
||||
"integrity": "sha512-sMmlWv2wMqUVPnBemYc7RQMYtM0AXQq0m9drVeu0eMtpSvojvFb4hW+hzpd6IBtULguNw2YBG9EyNo7irlpavQ==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-v7gFtZe71/vS/+HR+eK3EZmljvp5ANJK3DiCjdLC5XGmzpLvJVwfYz+An9f8+FEoM6DXrKLAdDkZPU9m/hZW7g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
@ -5946,13 +5946,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/heap-typed": {
|
||||
"version": "1.53.4",
|
||||
"resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.4.tgz",
|
||||
"integrity": "sha512-/lktvgpfH7Y2q8XpMFYCEebRIo5nM85DCR19zUeo3soVrxa2H90vJAKJJ0cPP/DEJ2cDggMk/Cuo4k4LLhbf7A==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-H5Vj0U+fXyvdyW5rSZ44kOI7WeThzaNihhwD7kQ+SkTjM9+sskUvx6JpZf62ca56NRt+57w42gg6A/A3S8W0bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.4"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
|
|
16
package.json
16
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.53.5",
|
||||
"version": "1.53.7",
|
||||
"description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"build:umd": "tsup",
|
||||
"build:docs": "npm run gen:examples && typedoc --out docs ./src",
|
||||
"build:docs-class": "npm run gen:examples && typedoc --out docs ./src/data-structures",
|
||||
"gen:examples": "ts-node testToExample.ts",
|
||||
"gen:examples": "ts-node scripts/testToExample.ts",
|
||||
"test:in-band": "jest --runInBand",
|
||||
"test": "npm run test:in-band",
|
||||
"test:integration": "npm run update:subs && jest --config jest.integration.config.js",
|
||||
|
@ -35,7 +35,7 @@
|
|||
"format:test": "prettier --write 'test/**/*.{js,ts}'",
|
||||
"format": "npm run format:src && npm run format:test",
|
||||
"check:exist-latest": "sh scripts/check_exist_remotely.sh",
|
||||
"ci": "env && git fetch --tags && npm run update:subs && npm run inspect && npm run lint && npm run build && npm run test && npm run changelog",
|
||||
"ci": "env && git fetch --tags && npm run update:subs && npm run inspect && npm run lint && npm run test && npm run changelog",
|
||||
"update:subs": "npm i avl-tree-typed binary-tree-typed bst-typed heap-typed data-structure-typed --save-dev",
|
||||
"install:all-subs": "npm i avl-tree-typed binary-tree-typed bst-typed deque-typed directed-graph-typed doubly-linked-list-typed graph-typed heap-typed linked-list-typed max-heap-typed max-priority-queue-typed min-heap-typed min-priority-queue-typed priority-queue-typed singly-linked-list-typed stack-typed tree-multimap-typed trie-typed undirected-graph-typed queue-typed --save-dev",
|
||||
"changelog": "auto-changelog",
|
||||
|
@ -70,11 +70,11 @@
|
|||
"@typescript-eslint/eslint-plugin": "^8.12.1",
|
||||
"@typescript-eslint/parser": "^8.12.1",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"avl-tree-typed": "^1.53.4",
|
||||
"avl-tree-typed": "^1.53.5",
|
||||
"benchmark": "^2.1.4",
|
||||
"binary-tree-typed": "^1.53.4",
|
||||
"bst-typed": "^1.53.4",
|
||||
"data-structure-typed": "^1.53.4",
|
||||
"binary-tree-typed": "^1.53.5",
|
||||
"bst-typed": "^1.53.5",
|
||||
"data-structure-typed": "^1.53.5",
|
||||
"dependency-cruiser": "^16.5.0",
|
||||
"doctoc": "^2.2.1",
|
||||
"eslint": "^9.13.0",
|
||||
|
@ -83,7 +83,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"heap-typed": "^1.53.4",
|
||||
"heap-typed": "^1.53.5",
|
||||
"istanbul-badges-readme": "^1.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"js-sdsl": "^4.4.2",
|
||||
|
|
|
@ -1,12 +1,52 @@
|
|||
// @ts-ignore
|
||||
import fs from 'fs';
|
||||
// @ts-ignore
|
||||
import path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { toPascalCase } from './test/utils';
|
||||
|
||||
const isReplaceMD = false;
|
||||
const filePath = path.resolve(__dirname, './config.json');
|
||||
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||
const config = JSON.parse(fileContent);
|
||||
|
||||
function toPascalCase(str: string): string {
|
||||
return str
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2') // Add space between lowercase and uppercase letters
|
||||
.replace(/[^a-zA-Z0-9]+/g, ' ') // Replace non-alphanumeric characters with spaces
|
||||
.split(' ') // Separate strings by spaces
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // The first letter is capitalized, the rest are lowercase
|
||||
.join(''); // Combine into a string
|
||||
}
|
||||
|
||||
const isReplaceMD = true;
|
||||
const START_MARKER = '[//]: # (No deletion!!! Start of Example Replace Section)';
|
||||
const END_MARKER = '[//]: # (No deletion!!! End of Example Replace Section)';
|
||||
|
||||
const pkgRootDir = config.individualsDir;
|
||||
const dirMap: Record<string, string | string[]> = {
|
||||
Heap: "heap-typed",
|
||||
AvlTree: "avl-tree-typed",
|
||||
BinaryTree: "binary-tree-typed",
|
||||
BST: "bst-typed",
|
||||
Deque: "deque-typed",
|
||||
DirectedGraph: "directed-graph-typed",
|
||||
DoublyLinkedList: ["doubly-linked-list-typed", "linked-list-typed"],
|
||||
Graph: "graph-typed",
|
||||
LinkedList: "linked-list-typed",
|
||||
MaxHeap: "max-heap-typed",
|
||||
MaxPriorityQueue: "max-priority-queue-typed",
|
||||
MinHeap: "min-heap-typed",
|
||||
MinPriorityQueue: "min-priority-queue-typed",
|
||||
PriorityQueue: "priority-queue-typed",
|
||||
SinglyLinkedList: "singly-linked-list-typed",
|
||||
Queue: "queue-typed",
|
||||
RedBlackTree: "red-black-tree-typed",
|
||||
Stack: "stack-typed",
|
||||
TreeMultimap: "tree-multimap-typed",
|
||||
Trie: "trie-typed",
|
||||
UndirectedGraph: "undirected-graph-typed",
|
||||
};
|
||||
const fileName = 'README.md';
|
||||
|
||||
/**
|
||||
* Recursively retrieve all `.ts` files in a directory.
|
||||
*/
|
||||
|
@ -67,6 +107,7 @@ function extractExamplesFromFile(filePath: string): { name: string; body: string
|
|||
}
|
||||
)
|
||||
.replace(
|
||||
// @ts-ignore
|
||||
/expect\((.*?)\)\.(toEqual|toBe|toStrictEqual|toHaveLength|toMatchObject)\((.*?)\);/gs, // Use `s` flag for multiline
|
||||
(match, actual, method, expected) => {
|
||||
expected = expected.replace(/\n/g, '\n //')
|
||||
|
@ -125,11 +166,15 @@ function addExamplesToSourceFile(
|
|||
// Replace @example part
|
||||
const exampleSection = examples
|
||||
.map(
|
||||
example =>
|
||||
` * @example \n * \/\/ ${example.name} \n${example.body
|
||||
example => {
|
||||
const indentedBody = ' ' + example.body;
|
||||
return ` * @example\n * \/\/ ${example.name}\n${indentedBody
|
||||
.split('\n')
|
||||
.map(line => ` * ${line}`)
|
||||
.map(line => {
|
||||
if (line.trim() === '') return ` *`
|
||||
return ` * ${line}`})
|
||||
.join('\n')}`
|
||||
}
|
||||
)
|
||||
.join('\n') + '\n ';
|
||||
|
||||
|
@ -137,7 +182,7 @@ function addExamplesToSourceFile(
|
|||
if (existingCommentInner.includes('@example')) {
|
||||
newComment = existingCommentInner.replace(/ \* @example[\s\S]*?(?=\*\/|$)/g, exampleSection);
|
||||
} else {
|
||||
newComment = existingCommentInner + `${exampleSection}`;
|
||||
newComment = existingCommentInner + `${exampleSection.trimStart()}`;
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,10 +202,10 @@ function addExamplesToSourceFile(
|
|||
/**
|
||||
* Process all test files and update README.md and source files.
|
||||
*/
|
||||
function updateExamples(testDir: string, readmePath: string, sourceBaseDir: string): void {
|
||||
function updateExamples(testDir: string, sourceBaseDir: string): void {
|
||||
|
||||
const testFiles = getAllTestFiles(testDir);
|
||||
|
||||
let allExamples: string[] = [];
|
||||
for (const file of testFiles) {
|
||||
const examples = extractExamplesFromFile(file);
|
||||
|
||||
|
@ -171,17 +216,37 @@ function updateExamples(testDir: string, readmePath: string, sourceBaseDir: stri
|
|||
|
||||
const relativePath = path.relative(testDir, file);
|
||||
const sourceFilePath = path.resolve(sourceBaseDir, relativePath.replace('.test.ts', '.ts'));
|
||||
const className = path.basename(sourceFilePath, '.ts');
|
||||
let className = toPascalCase(path.basename(sourceFilePath, '.ts'));
|
||||
if (className === 'Bst') className = 'BST';
|
||||
addExamplesToSourceFile(sourceFilePath, className, examples);
|
||||
const dirKey = dirMap[className];
|
||||
|
||||
addExamplesToSourceFile(sourceFilePath, toPascalCase(className), examples);
|
||||
if (!dirKey) {
|
||||
console.warn(`No directory mapping found for class: ${className}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
allExamples = allExamples.concat(
|
||||
examples.map(example => `### ${example.name}\n\`\`\`typescript\n${example.body}\n\`\`\``)
|
||||
|
||||
|
||||
const newExamples = examples.map(
|
||||
example => {
|
||||
const indentedBody = ' ' + example.body;
|
||||
return `### ${example.name}\n\`\`\`typescript\n${indentedBody}\n\`\`\``}
|
||||
);
|
||||
}
|
||||
|
||||
if (isReplaceMD && allExamples.length > 0) {
|
||||
replaceExamplesInReadme(readmePath, allExamples);
|
||||
if (isReplaceMD && newExamples.length > 0) {
|
||||
if (dirKey instanceof Array && dirKey.length > 0) {
|
||||
for (const readmeRoot of dirKey) {
|
||||
const readmePath = path.resolve(pkgRootDir, readmeRoot, fileName);
|
||||
replaceExamplesInReadme(readmePath, newExamples);
|
||||
}
|
||||
}
|
||||
if (typeof dirKey === 'string') {
|
||||
const readmePath = path.resolve(pkgRootDir, dirKey, fileName);
|
||||
replaceExamplesInReadme(readmePath, newExamples);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,8 +273,7 @@ function replaceExamplesInReadme(readmePath: string, newExamples: string[]): voi
|
|||
}
|
||||
|
||||
// Run the script
|
||||
const testDir = path.resolve(__dirname, 'test/unit');
|
||||
const readmePath = path.resolve(__dirname, 'README.md');
|
||||
const sourceBaseDir = path.resolve(__dirname, 'src');
|
||||
const testDir = path.resolve(__dirname, '../test/unit');
|
||||
const sourceBaseDir = path.resolve(__dirname, '../src');
|
||||
|
||||
updateExamples(testDir, readmePath, sourceBaseDir);
|
||||
updateExamples(testDir, sourceBaseDir);
|
19
src/common/index.ts
Normal file
19
src/common/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export enum DFSOperation {
|
||||
VISIT = 0,
|
||||
PROCESS = 1
|
||||
}
|
||||
export class Range<K> {
|
||||
constructor(
|
||||
public low: K,
|
||||
public high: K,
|
||||
public includeLow: boolean = true,
|
||||
public includeHigh: boolean = true
|
||||
) {}
|
||||
|
||||
// Determine whether a key is within the range
|
||||
isInRange(key: K, comparator: (a: K, b: K) => number): boolean {
|
||||
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
|
||||
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
|
||||
return lowCheck && highCheck;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export enum DFSOperation {
|
||||
VISIT = 0,
|
||||
PROCESS = 1
|
||||
}
|
|
@ -143,8 +143,9 @@ export class AVLTreeMultiMap<
|
|||
return new AVLTreeMultiMap<K, V, R, NODE, TREE>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
comparator: this._comparator,
|
||||
extractComparable: this._extractComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
...options
|
||||
}) as TREE;
|
||||
}
|
||||
|
@ -187,17 +188,14 @@ export class AVLTreeMultiMap<
|
|||
return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value];
|
||||
|
||||
if (this.isRaw(keyNodeEntryOrRaw)) {
|
||||
if (this._toEntryFn) {
|
||||
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
return [undefined, undefined];
|
||||
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value];
|
||||
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,9 @@ export class AVLTree<
|
|||
return new AVLTree<K, V, R, NODE, TREE>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
comparator: this._comparator,
|
||||
extractComparable: this._extractComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
...options
|
||||
}) as TREE;
|
||||
}
|
||||
|
@ -434,7 +435,7 @@ export class AVLTree<
|
|||
*/
|
||||
protected _balancePath(node: BTNRep<K, V, NODE> | R): void {
|
||||
node = this.ensureNode(node);
|
||||
const path = this.getPathToRoot(node => node, node, false); // first O(log n) + O(log n)
|
||||
const path = this.getPathToRoot(node, node => node, false); // first O(log n) + O(log n)
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
// second O(log n)
|
||||
const A = path[i];
|
||||
|
|
|
@ -29,7 +29,7 @@ import { IBinaryTree } from '../../interfaces';
|
|||
import { isComparable, trampoline } from '../../utils';
|
||||
import { Queue } from '../queue';
|
||||
import { IterableEntryBase } from '../base';
|
||||
import { DFSOperation } from '../../constants';
|
||||
import { DFSOperation, Range } from '../../common';
|
||||
|
||||
/**
|
||||
* Represents a node in a binary tree.
|
||||
|
@ -233,17 +233,14 @@ export class BinaryTree<
|
|||
return [this.createNode(key, finalValue), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value];
|
||||
|
||||
if (this.isRaw(keyNodeEntryOrRaw)) {
|
||||
if (this._toEntryFn) {
|
||||
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue];
|
||||
}
|
||||
return [undefined, undefined];
|
||||
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value];
|
||||
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
|
@ -275,15 +272,15 @@ export class BinaryTree<
|
|||
const key = keyNodeEntryOrRaw[0];
|
||||
if (key === null) return null;
|
||||
if (key === undefined) return;
|
||||
return this.getNodeByKey(key, iterationType);
|
||||
return this.getNode(key, this._root, iterationType);
|
||||
}
|
||||
|
||||
if (this._toEntryFn) {
|
||||
const [key] = this._toEntryFn(keyNodeEntryOrRaw as R);
|
||||
if (this.isKey(key)) return this.getNodeByKey(key);
|
||||
if (this.isKey(key)) return this.getNode(key);
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return this.getNodeByKey(keyNodeEntryOrRaw, iterationType);
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return this.getNode(keyNodeEntryOrRaw, this._root, iterationType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -302,8 +299,15 @@ export class BinaryTree<
|
|||
return keyNodeEntryOrRaw instanceof BinaryTreeNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `isRaw` checks if the input parameter is of type `R` by verifying if it is an object.
|
||||
* @param {BTNRep<K, V, NODE> | R} keyNodeEntryOrRaw - BTNRep<K, V, NODE> | R
|
||||
* @returns The function `isRaw` is checking if the `keyNodeEntryOrRaw` parameter is of type `R` by
|
||||
* checking if it is an object. If the parameter is an object, the function will return `true`,
|
||||
* indicating that it is of type `R`.
|
||||
*/
|
||||
isRaw(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R): keyNodeEntryOrRaw is R {
|
||||
return typeof keyNodeEntryOrRaw === 'object';
|
||||
return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -345,6 +349,12 @@ export class BinaryTree<
|
|||
return keyNodeEntryOrRaw === this._NIL;
|
||||
}
|
||||
|
||||
isRange(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>
|
||||
): keyNodeEntryRawOrPredicate is Range<K> {
|
||||
return keyNodeEntryRawOrPredicate instanceof Range;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function determines whether a given key, node, entry, or raw data is a leaf node in a binary
|
||||
* tree.
|
||||
|
@ -508,6 +518,18 @@ export class BinaryTree<
|
|||
return inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(k * n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `merge` function in TypeScript merges another binary tree into the current tree by adding all
|
||||
* elements from the other tree.
|
||||
* @param anotherTree - `BinaryTree<K, V, R, NODE, TREE>`
|
||||
*/
|
||||
merge(anotherTree: BinaryTree<K, V, R, NODE, TREE>) {
|
||||
this.addMany(anotherTree, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(k * n)
|
||||
* Space Complexity: O(1)
|
||||
|
@ -585,6 +607,75 @@ export class BinaryTree<
|
|||
return deletedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(k + log n)
|
||||
*
|
||||
* The `search` function in TypeScript performs a depth-first or breadth-first search on a tree
|
||||
* structure based on a given predicate or key, with options to return multiple results or just one.
|
||||
* @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The
|
||||
* `keyNodeEntryRawOrPredicate` parameter in the `search` function can accept three types of values:
|
||||
* @param [onlyOne=false] - The `onlyOne` parameter in the `search` function is a boolean flag that
|
||||
* determines whether the search should stop after finding the first matching node. If `onlyOne` is
|
||||
* set to `true`, the search will return as soon as a matching node is found. If `onlyOne` is
|
||||
* @param {C} callback - The `callback` parameter in the `search` function is a callback function
|
||||
* that will be called on each node that matches the search criteria. It is of type `C`, which
|
||||
* extends `NodeCallback<NODE>`. The default value for `callback` is `this._DEFAULT_NODE_CALLBACK` if
|
||||
* @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the `search` function is
|
||||
* used to specify the node from which the search operation should begin. It represents the starting
|
||||
* point in the binary tree where the search will be performed. If no specific `startNode` is
|
||||
* provided, the search operation will start from the root
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter in the `search` function
|
||||
* specifies the type of iteration to be used when searching for nodes in a binary tree. It can have
|
||||
* two possible values:
|
||||
* @returns The `search` function returns an array of values that match the provided criteria based
|
||||
* on the search algorithm implemented within the function.
|
||||
*/
|
||||
search<C extends NodeCallback<NODE>>(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE>,
|
||||
onlyOne = false,
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, NODE> | R = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
if (keyNodeEntryRawOrPredicate === undefined) return [];
|
||||
if (keyNodeEntryRawOrPredicate === null) return [];
|
||||
startNode = this.ensureNode(startNode);
|
||||
if (!startNode) return [];
|
||||
const predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
|
||||
|
||||
const ans: ReturnType<C>[] = [];
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const dfs = (cur: NODE) => {
|
||||
if (predicate(cur)) {
|
||||
ans.push(callback(cur));
|
||||
if (onlyOne) return;
|
||||
}
|
||||
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
|
||||
if (this.isRealNode(cur.left)) dfs(cur.left);
|
||||
if (this.isRealNode(cur.right)) dfs(cur.right);
|
||||
};
|
||||
|
||||
dfs(startNode);
|
||||
} else {
|
||||
const stack = [startNode];
|
||||
while (stack.length > 0) {
|
||||
const cur = stack.pop();
|
||||
if (this.isRealNode(cur)) {
|
||||
if (predicate(cur)) {
|
||||
ans.push(callback(cur));
|
||||
if (onlyOne) return ans;
|
||||
}
|
||||
if (this.isRealNode(cur.left)) stack.push(cur.left);
|
||||
if (this.isRealNode(cur.right)) stack.push(cur.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(k + log n)
|
||||
|
@ -612,42 +703,7 @@ export class BinaryTree<
|
|||
startNode: BTNRep<K, V, NODE> | R = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE[] {
|
||||
if (keyNodeEntryRawOrPredicate === undefined) return [];
|
||||
if (keyNodeEntryRawOrPredicate === null) return [];
|
||||
startNode = this.ensureNode(startNode);
|
||||
if (!startNode) return [];
|
||||
const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate);
|
||||
|
||||
const ans: NODE[] = [];
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const dfs = (cur: NODE) => {
|
||||
if (callback(cur)) {
|
||||
ans.push(cur);
|
||||
if (onlyOne) return;
|
||||
}
|
||||
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
|
||||
if (this.isRealNode(cur.left)) dfs(cur.left);
|
||||
if (this.isRealNode(cur.right)) dfs(cur.right);
|
||||
};
|
||||
|
||||
dfs(startNode);
|
||||
} else {
|
||||
const stack = [startNode];
|
||||
while (stack.length > 0) {
|
||||
const cur = stack.pop();
|
||||
if (this.isRealNode(cur)) {
|
||||
if (callback(cur)) {
|
||||
ans.push(cur);
|
||||
if (onlyOne) return ans;
|
||||
}
|
||||
if (this.isRealNode(cur.left)) stack.push(cur.left);
|
||||
if (this.isRealNode(cur.right)) stack.push(cur.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ans;
|
||||
return this.search(keyNodeEntryRawOrPredicate, onlyOne, node => node, startNode, iterationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -675,24 +731,7 @@ export class BinaryTree<
|
|||
startNode: BTNRep<K, V, NODE> | R = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): OptNodeOrNull<NODE> {
|
||||
return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function `getNodeByKey` retrieves a node by its key from a binary tree structure.
|
||||
* @param {K} key - The `key` parameter is the value used to search for a specific node in a data
|
||||
* structure.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is a type of iteration that
|
||||
* specifies how the tree nodes should be traversed when searching for a node with the given key. It
|
||||
* is an optional parameter with a default value of `this.iterationType`.
|
||||
* @returns The `getNodeByKey` function is returning an optional binary tree node
|
||||
* (`OptNodeOrNull<NODE>`).
|
||||
*/
|
||||
getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNodeOrNull<NODE> {
|
||||
return this.getNode(key, this._root, iterationType);
|
||||
return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType)[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -723,7 +762,7 @@ export class BinaryTree<
|
|||
iterationType: IterationType = this.iterationType
|
||||
): V | undefined {
|
||||
if (this._isMapMode) {
|
||||
const key = this._getKey(keyNodeEntryRawOrPredicate);
|
||||
const key = this._extractKey(keyNodeEntryRawOrPredicate);
|
||||
if (key === null || key === undefined) return;
|
||||
return this._store.get(key);
|
||||
}
|
||||
|
@ -756,7 +795,7 @@ export class BinaryTree<
|
|||
startNode: BTNRep<K, V, NODE> | R = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): boolean {
|
||||
return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType).length > 0;
|
||||
return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1023,9 +1062,9 @@ export class BinaryTree<
|
|||
* parameter.
|
||||
*/
|
||||
getPathToRoot<C extends NodeCallback<OptNodeOrNull<NODE>>>(
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
beginNode: BTNRep<K, V, NODE> | R,
|
||||
isReverse = true
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
isReverse = false
|
||||
): ReturnType<C>[] {
|
||||
const result: ReturnType<C>[] = [];
|
||||
let beginNodeEnsured = this.ensureNode(beginNode);
|
||||
|
@ -1118,7 +1157,6 @@ export class BinaryTree<
|
|||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C> {
|
||||
if (this.isNIL(startNode)) return callback(undefined);
|
||||
// TODO support get right most by passing key in
|
||||
startNode = this.ensureNode(startNode);
|
||||
if (!startNode) return callback(startNode);
|
||||
|
||||
|
@ -2144,16 +2182,16 @@ export class BinaryTree<
|
|||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function `_getKey` in TypeScript returns the key from a given input, which can be a node,
|
||||
* The function `_extractKey` in TypeScript returns the key from a given input, which can be a node,
|
||||
* entry, raw data, or null/undefined.
|
||||
* @param {BTNRep<K, V, NODE> | R} keyNodeEntryOrRaw - The `_getKey` method you provided is a
|
||||
* @param {BTNRep<K, V, NODE> | R} keyNodeEntryOrRaw - The `_extractKey` method you provided is a
|
||||
* TypeScript method that takes in a parameter `keyNodeEntryOrRaw` of type `BTNRep<K, V, NODE> | R`,
|
||||
* where `BTNRep` is a generic type with keys `K`, `V`, and `NODE`, and `
|
||||
* @returns The `_getKey` method returns the key value extracted from the `keyNodeEntryOrRaw`
|
||||
* @returns The `_extractKey` method returns the key value extracted from the `keyNodeEntryOrRaw`
|
||||
* parameter. The return value can be a key value of type `K`, `null`, or `undefined`, depending on
|
||||
* the conditions checked in the method.
|
||||
*/
|
||||
protected _getKey(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R): K | null | undefined {
|
||||
protected _extractKey(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R): K | null | undefined {
|
||||
if (keyNodeEntryOrRaw === null) return null;
|
||||
if (keyNodeEntryOrRaw === undefined) return;
|
||||
if (keyNodeEntryOrRaw === this._NIL) return;
|
||||
|
@ -2193,6 +2231,9 @@ export class BinaryTree<
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The _clearNodes function sets the root node to undefined and resets the size to 0.
|
||||
*/
|
||||
protected _clearNodes() {
|
||||
|
@ -2201,6 +2242,9 @@ export class BinaryTree<
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The _clearValues function clears all values stored in the _store object.
|
||||
*/
|
||||
protected _clearValues() {
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
|||
BSTNOptKeyOrNode,
|
||||
BSTOptions,
|
||||
BTNRep,
|
||||
Comparable,
|
||||
Comparator,
|
||||
CP,
|
||||
DFSOrderPattern,
|
||||
|
@ -23,6 +24,7 @@ import { BinaryTree, BinaryTreeNode } from './binary-tree';
|
|||
import { IBinaryTree } from '../../interfaces';
|
||||
import { Queue } from '../queue';
|
||||
import { isComparable } from '../../utils';
|
||||
import { Range } from '../../common';
|
||||
|
||||
export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNodeNested<K, V>> extends BinaryTreeNode<
|
||||
K,
|
||||
|
@ -92,6 +94,58 @@ export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNod
|
|||
* 5. Logarithmic Operations: Ideal operations like insertion, deletion, and searching are O(log n) time-efficient.
|
||||
* 6. Balance Variability: Can become unbalanced; special types maintain balance.
|
||||
* 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
|
||||
* @example
|
||||
* // Find kth smallest element
|
||||
* // Create a BST with some elements
|
||||
* const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
|
||||
* const sortedKeys = bst.dfs(node => node.key, 'IN');
|
||||
*
|
||||
* // Helper function to find kth smallest
|
||||
* const findKthSmallest = (k: number): number | undefined => {
|
||||
* return sortedKeys[k - 1];
|
||||
* };
|
||||
*
|
||||
* // Assertions
|
||||
* console.log(findKthSmallest(1)); // 1
|
||||
* console.log(findKthSmallest(3)); // 4
|
||||
* console.log(findKthSmallest(7)); // 8
|
||||
* @example
|
||||
* // Find elements in a range
|
||||
* const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
*
|
||||
* // Helper function to find elements in range
|
||||
* const findElementsInRange = (min: number, max: number): number[] => {
|
||||
* return bst.search(node => node.key >= min && node.key <= max, false, node => node.key);
|
||||
* };
|
||||
*
|
||||
* // Assertions
|
||||
* console.log(findElementsInRange(4, 12)); // [10, 5, 7, 12]
|
||||
* console.log(findElementsInRange(15, 20)); // [15, 18]
|
||||
* @example
|
||||
* // Find lowest common ancestor
|
||||
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
||||
*
|
||||
* function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
|
||||
* for (const num of arr1) {
|
||||
* if (arr2.indexOf(num) !== -1) {
|
||||
* return num;
|
||||
* }
|
||||
* }
|
||||
* return undefined;
|
||||
* }
|
||||
*
|
||||
* // LCA helper function
|
||||
* const findLCA = (num1: number, num2: number): number | undefined => {
|
||||
* const path1 = bst.getPathToRoot(num1);
|
||||
* const path2 = bst.getPathToRoot(num2);
|
||||
* // Find the first common ancestor
|
||||
* return findFirstCommon(path1, path2);
|
||||
* };
|
||||
*
|
||||
* // Assertions
|
||||
* console.log(findLCA(3, 10)); // 7
|
||||
* console.log(findLCA(5, 35)); // 15
|
||||
* console.log(findLCA(20, 30)); // 25
|
||||
*/
|
||||
export class BST<
|
||||
K = any,
|
||||
|
@ -115,8 +169,9 @@ export class BST<
|
|||
super([], options);
|
||||
|
||||
if (options) {
|
||||
const { comparator } = options;
|
||||
if (comparator) this._comparator = comparator;
|
||||
const { extractComparable, isReverse } = options;
|
||||
if (typeof extractComparable === 'function') this._extractComparable = extractComparable;
|
||||
if (isReverse !== undefined) this._isReverse = isReverse;
|
||||
}
|
||||
|
||||
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
||||
|
@ -132,6 +187,17 @@ export class BST<
|
|||
return this._root;
|
||||
}
|
||||
|
||||
protected _isReverse: boolean = false;
|
||||
|
||||
/**
|
||||
* The above function is a getter method in TypeScript that returns the value of the private property
|
||||
* `_isReverse`.
|
||||
* @returns The `isReverse` property of the object, which is a boolean value.
|
||||
*/
|
||||
get isReverse(): boolean {
|
||||
return this._isReverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new BSTNode with the given key and value and returns it.
|
||||
* @param {K} key - The key parameter is of type K, which represents the type of the key for the node
|
||||
|
@ -155,8 +221,9 @@ export class BST<
|
|||
return new BST<K, V, R, NODE, TREE>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
comparator: this._comparator,
|
||||
extractComparable: this._extractComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
...options
|
||||
}) as TREE;
|
||||
}
|
||||
|
@ -217,11 +284,11 @@ export class BST<
|
|||
* @param {any} key - The `key` parameter is a value that will be checked to determine if it is of
|
||||
* type `K`.
|
||||
* @returns The `override isKey(key: any): key is K` function is returning a boolean value based on
|
||||
* the result of the `isComparable` function with the condition `this.comparator !==
|
||||
* the result of the `isComparable` function with the condition `this._compare !==
|
||||
* this._DEFAULT_COMPARATOR`.
|
||||
*/
|
||||
override isKey(key: any): key is K {
|
||||
return isComparable(key, this.comparator !== this._DEFAULT_COMPARATOR);
|
||||
return isComparable(key, this._extractComparable !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,11 +315,11 @@ export class BST<
|
|||
|
||||
let current = this._root;
|
||||
while (current !== undefined) {
|
||||
if (this.comparator(current.key, newNode.key) === 0) {
|
||||
if (this._compare(current.key, newNode.key) === 0) {
|
||||
this._replaceNode(current, newNode);
|
||||
if (this._isMapMode) this._setValue(current.key, newValue);
|
||||
return true;
|
||||
} else if (this.comparator(current.key, newNode.key) > 0) {
|
||||
} else if (this._compare(current.key, newNode.key) > 0) {
|
||||
if (current.left === undefined) {
|
||||
current.left = newNode;
|
||||
if (this._isMapMode) this._setValue(newNode?.key, newValue);
|
||||
|
@ -350,7 +417,7 @@ export class BST<
|
|||
}
|
||||
|
||||
if (keyA !== undefined && keyA !== null && keyB !== undefined && keyB !== null) {
|
||||
return this.comparator(keyA, keyB);
|
||||
return this._compare(keyA, keyB);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
@ -392,64 +459,114 @@ export class BST<
|
|||
return inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `merge` function overrides the base class method by adding elements from another
|
||||
* binary search tree.
|
||||
* @param anotherTree - `anotherTree` is an instance of a Binary Search Tree (BST) with key type `K`,
|
||||
* value type `V`, return type `R`, node type `NODE`, and tree type `TREE`.
|
||||
*/
|
||||
override merge(anotherTree: BST<K, V, R, NODE, TREE>) {
|
||||
this.addMany(anotherTree, [], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(k + log n)
|
||||
*
|
||||
* The function `getNodes` in TypeScript overrides the base class method to retrieve nodes based on a
|
||||
* given keyNodeEntryRawOrPredicate and iteration type.
|
||||
* @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate`
|
||||
* parameter in the `getNodes` method is used to filter the nodes that will be returned. It can be a
|
||||
* key, a node, an entry, or a custom keyNodeEntryRawOrPredicate function that determines whether a node should be
|
||||
* included in the result.
|
||||
* @param [onlyOne=false] - The `onlyOne` parameter in the `getNodes` method is a boolean flag that
|
||||
* determines whether to return only the first node that matches the keyNodeEntryRawOrPredicate (`true`) or all nodes
|
||||
* that match the keyNodeEntryRawOrPredicate (`false`). If `onlyOne` is set to `true`, the method will stop iterating
|
||||
* and
|
||||
* @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the
|
||||
* `getNodes` method is used to specify the starting point for traversing the tree when searching for
|
||||
* nodes that match a given keyNodeEntryRawOrPredicate. It represents the root node of the subtree where the search
|
||||
* should begin. If not explicitly provided, the default value for `begin
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter in the `getNodes` method
|
||||
* specifies the type of iteration to be performed when traversing the nodes of a binary tree. It can
|
||||
* have two possible values:
|
||||
* @returns The `getNodes` method returns an array of nodes that satisfy the given keyNodeEntryRawOrPredicate.
|
||||
* The function `search` in TypeScript overrides the search behavior in a binary tree structure based
|
||||
* on specified criteria.
|
||||
* @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The
|
||||
* `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the
|
||||
* following types:
|
||||
* @param [onlyOne=false] - The `onlyOne` parameter is a boolean flag that determines whether the
|
||||
* search should stop after finding the first matching node. If `onlyOne` is set to `true`, the
|
||||
* search will return as soon as a matching node is found. If `onlyOne` is set to `false`, the
|
||||
* @param {C} callback - The `callback` parameter in the `override search` function is a function
|
||||
* that will be called on each node that matches the search criteria. It is of type `C`, which
|
||||
* extends `NodeCallback<NODE>`. The callback function should accept a node of type `NODE` as its
|
||||
* argument and
|
||||
* @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the `override search`
|
||||
* method represents the node from which the search operation will begin. It is the starting point
|
||||
* for searching within the tree data structure. The method ensures that the `startNode` is a valid
|
||||
* node before proceeding with the search operation. If the `
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter in the `override search`
|
||||
* function determines the type of iteration to be used during the search operation. It can have two
|
||||
* possible values:
|
||||
* @returns The `override search` method returns an array of values that match the search criteria
|
||||
* specified by the input parameters. The method performs a search operation on a binary tree
|
||||
* structure based on the provided key, predicate, and other options. The search results are
|
||||
* collected in an array and returned as the output of the method.
|
||||
*/
|
||||
override getNodes(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE>,
|
||||
override search<C extends NodeCallback<NODE>>(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>,
|
||||
onlyOne = false,
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, NODE> | R = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): NODE[] {
|
||||
): ReturnType<C>[] {
|
||||
if (keyNodeEntryRawOrPredicate === undefined) return [];
|
||||
if (keyNodeEntryRawOrPredicate === null) return [];
|
||||
startNode = this.ensureNode(startNode);
|
||||
if (!startNode) return [];
|
||||
const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate);
|
||||
const ans: NODE[] = [];
|
||||
let predicate: NodePredicate<NODE>;
|
||||
|
||||
const isRange = this.isRange(keyNodeEntryRawOrPredicate);
|
||||
// Set predicate based on parameter type
|
||||
if (isRange) {
|
||||
predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator);
|
||||
} else {
|
||||
predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
|
||||
}
|
||||
const isToLeftByRange = (cur: NODE) => {
|
||||
if (isRange) {
|
||||
const range = keyNodeEntryRawOrPredicate;
|
||||
const leftS = this.isReverse ? range.high : range.low;
|
||||
const leftI = this.isReverse ? range.includeHigh : range.includeLow;
|
||||
return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isToRightByRange = (cur: NODE) => {
|
||||
if (isRange) {
|
||||
const range = keyNodeEntryRawOrPredicate;
|
||||
const rightS = this.isReverse ? range.low : range.high;
|
||||
const rightI = this.isReverse ? range.includeLow : range.includeLow;
|
||||
|
||||
return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const ans: ReturnType<C>[] = [];
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const dfs = (cur: NODE) => {
|
||||
if (callback(cur)) {
|
||||
ans.push(cur);
|
||||
if (predicate(cur)) {
|
||||
ans.push(callback(cur));
|
||||
if (onlyOne) return;
|
||||
}
|
||||
|
||||
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
|
||||
if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
|
||||
|
||||
if (isRange) {
|
||||
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left);
|
||||
if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right);
|
||||
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
|
||||
if (
|
||||
this.isRealNode(cur.left) &&
|
||||
benchmarkKey !== null &&
|
||||
benchmarkKey !== undefined &&
|
||||
this.comparator(cur.key, benchmarkKey) > 0
|
||||
this._compare(cur.key, benchmarkKey) > 0
|
||||
)
|
||||
dfs(cur.left);
|
||||
if (
|
||||
this.isRealNode(cur.right) &&
|
||||
benchmarkKey !== null &&
|
||||
benchmarkKey !== undefined &&
|
||||
this.comparator(cur.key, benchmarkKey) < 0
|
||||
this._compare(cur.key, benchmarkKey) < 0
|
||||
)
|
||||
dfs(cur.right);
|
||||
} else {
|
||||
|
@ -463,24 +580,27 @@ export class BST<
|
|||
const stack = [startNode];
|
||||
while (stack.length > 0) {
|
||||
const cur = stack.pop()!;
|
||||
if (callback(cur)) {
|
||||
ans.push(cur);
|
||||
if (predicate(cur)) {
|
||||
ans.push(callback(cur));
|
||||
if (onlyOne) return ans;
|
||||
}
|
||||
if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
|
||||
if (isRange) {
|
||||
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left);
|
||||
if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right);
|
||||
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
|
||||
if (
|
||||
this.isRealNode(cur.right) &&
|
||||
benchmarkKey !== null &&
|
||||
benchmarkKey !== undefined &&
|
||||
this.comparator(cur.key, benchmarkKey) < 0
|
||||
this._compare(cur.key, benchmarkKey) < 0
|
||||
)
|
||||
stack.push(cur.right);
|
||||
if (
|
||||
this.isRealNode(cur.left) &&
|
||||
benchmarkKey !== null &&
|
||||
benchmarkKey !== undefined &&
|
||||
this.comparator(cur.key, benchmarkKey) > 0
|
||||
this._compare(cur.key, benchmarkKey) > 0
|
||||
)
|
||||
stack.push(cur.left);
|
||||
} else {
|
||||
|
@ -521,23 +641,6 @@ export class BST<
|
|||
return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function `getNodeByKey` returns a node with a specific key from a tree data structure.
|
||||
* @param {K} key - The key parameter is the value used to search for a specific node in the tree. It
|
||||
* is typically a unique identifier or a value that can be used to determine the position of the node
|
||||
* in the tree structure.
|
||||
* @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter is an optional
|
||||
* parameter that specifies the type of iteration to be used when searching for a node in the tree.
|
||||
* It has a default value of `'ITERATIVE'`.
|
||||
* @returns The method is returning a NODE object or undefined.
|
||||
*/
|
||||
override getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNode<NODE> {
|
||||
return this.getNode(key, this._root, iterationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
|
@ -654,9 +757,9 @@ export class BST<
|
|||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const dfs = (cur: NODE) => {
|
||||
const compared = this.comparator(cur.key, targetKey);
|
||||
const compared = this._compare(cur.key, targetKey);
|
||||
if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
|
||||
|
||||
// TODO here can be optimized to O(log n)
|
||||
if (this.isRealNode(cur.left)) dfs(cur.left);
|
||||
if (this.isRealNode(cur.right)) dfs(cur.right);
|
||||
};
|
||||
|
@ -668,7 +771,7 @@ export class BST<
|
|||
while (queue.size > 0) {
|
||||
const cur = queue.shift();
|
||||
if (this.isRealNode(cur)) {
|
||||
const compared = this.comparator(cur.key, targetKey);
|
||||
const compared = this._compare(cur.key, targetKey);
|
||||
if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
|
||||
|
||||
if (this.isRealNode(cur.left)) queue.push(cur.left);
|
||||
|
@ -786,19 +889,26 @@ export class BST<
|
|||
return balanced;
|
||||
}
|
||||
|
||||
protected _DEFAULT_COMPARATOR = (a: K, b: K): number => {
|
||||
protected _comparator: Comparator<K> = (a: K, b: K): number => {
|
||||
if (isComparable(a) && isComparable(b)) {
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
return 0;
|
||||
}
|
||||
if (this._extractComparable) {
|
||||
if (this._extractComparable(a) > this._extractComparable(b)) return 1;
|
||||
if (this._extractComparable(a) < this._extractComparable(b)) return -1;
|
||||
return 0;
|
||||
}
|
||||
if (typeof a === 'object' || typeof b === 'object') {
|
||||
throw TypeError(
|
||||
`When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
|
||||
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
||||
);
|
||||
}
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
protected _comparator: Comparator<K> = this._DEFAULT_COMPARATOR;
|
||||
|
||||
/**
|
||||
* The function returns the value of the _comparator property.
|
||||
* @returns The `_comparator` property is being returned.
|
||||
|
@ -807,6 +917,17 @@ export class BST<
|
|||
return this._comparator;
|
||||
}
|
||||
|
||||
protected _extractComparable?: (key: K) => Comparable;
|
||||
|
||||
/**
|
||||
* This function returns the value of the `_extractComparable` property.
|
||||
* @returns The method `extractComparable()` is being returned, which is a getter method for the
|
||||
* `_extractComparable` property.
|
||||
*/
|
||||
get extractComparable() {
|
||||
return this._extractComparable;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function sets the root of a tree-like structure and updates the parent property of the new
|
||||
* root.
|
||||
|
@ -818,4 +939,8 @@ export class BST<
|
|||
}
|
||||
this._root = v;
|
||||
}
|
||||
|
||||
protected _compare(a: K, b: K) {
|
||||
return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,10 @@ export class RedBlackTreeNode<
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Efficient self-balancing, but not completely balanced. Compared with AVLTree, the addition and deletion efficiency is high but the query efficiency is slightly lower.
|
||||
* 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
|
||||
*/
|
||||
export class RedBlackTree<
|
||||
K = any,
|
||||
V = any,
|
||||
|
@ -119,7 +123,7 @@ export class RedBlackTree<
|
|||
return new RedBlackTree<K, V, R, NODE, TREE>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
comparator: this._comparator,
|
||||
extractComparable: this._extractComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
...options
|
||||
}) as TREE;
|
||||
|
@ -351,7 +355,7 @@ export class RedBlackTree<
|
|||
|
||||
while (this.isRealNode(current)) {
|
||||
parent = current;
|
||||
const compared = this.comparator(node.key, current.key);
|
||||
const compared = this._compare(node.key, current.key);
|
||||
if (compared < 0) {
|
||||
current = current.left ?? this.NIL;
|
||||
} else if (compared > 0) {
|
||||
|
|
|
@ -139,7 +139,7 @@ export class TreeMultiMap<
|
|||
return new TreeMultiMap<K, V, R, NODE, TREE>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
comparator: this._comparator,
|
||||
extractComparable: this._extractComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
...options
|
||||
}) as TREE;
|
||||
|
@ -173,8 +173,8 @@ export class TreeMultiMap<
|
|||
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
|
||||
}
|
||||
|
||||
if (this._toEntryFn) {
|
||||
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R);
|
||||
if (this.isRaw(keyNodeEntryOrRaw)) {
|
||||
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import { IterableElementBase } from '../base';
|
|||
* 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prime's minimum-spanning tree algorithm, which use heaps to improve performance.
|
||||
* @example
|
||||
* // Use Heap to sort an array
|
||||
* function heapSort(arr: number[]): number[] {
|
||||
* function heapSort(arr: number[]): number[] {
|
||||
* const heap = new Heap<number>(arr, { comparator: (a, b) => a - b });
|
||||
* const sorted: number[] = [];
|
||||
* while (!heap.isEmpty()) {
|
||||
|
@ -34,7 +34,7 @@ import { IterableElementBase } from '../base';
|
|||
* console.log(heapSort(array)); // [1, 2, 3, 4, 5, 8]
|
||||
* @example
|
||||
* // Use Heap to solve top k problems
|
||||
* function topKElements(arr: number[], k: number): number[] {
|
||||
* function topKElements(arr: number[], k: number): number[] {
|
||||
* const heap = new Heap<number>([], { comparator: (a, b) => b - a }); // Max heap
|
||||
* arr.forEach(num => {
|
||||
* heap.add(num);
|
||||
|
@ -47,7 +47,7 @@ import { IterableElementBase } from '../base';
|
|||
* console.log(topKElements(numbers, 3)); // [15, 10, 5]
|
||||
* @example
|
||||
* // Use Heap to merge sorted sequences
|
||||
* function mergeSortedSequences(sequences: number[][]): number[] {
|
||||
* function mergeSortedSequences(sequences: number[][]): number[] {
|
||||
* const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], {
|
||||
* comparator: (a, b) => a.value - b.value // Min heap
|
||||
* });
|
||||
|
@ -84,7 +84,7 @@ import { IterableElementBase } from '../base';
|
|||
* console.log(mergeSortedSequences(sequences)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
* @example
|
||||
* // Use Heap to dynamically maintain the median
|
||||
* class MedianFinder {
|
||||
* class MedianFinder {
|
||||
* private low: MaxHeap<number>; // Max heap, stores the smaller half
|
||||
* private high: MinHeap<number>; // Min heap, stores the larger half
|
||||
*
|
||||
|
@ -121,7 +121,7 @@ import { IterableElementBase } from '../base';
|
|||
* console.log(medianFinder.findMedian()); // 30
|
||||
* @example
|
||||
* // Use Heap for load balancing
|
||||
* function loadBalance(requests: number[], servers: number): number[] {
|
||||
* function loadBalance(requests: number[], servers: number): number[] {
|
||||
* const serverHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // min heap
|
||||
* const serverLoads = new Array(servers).fill(0);
|
||||
*
|
||||
|
@ -143,7 +143,7 @@ import { IterableElementBase } from '../base';
|
|||
* console.log(loadBalance(requests, 3)); // [12, 8, 5]
|
||||
* @example
|
||||
* // Use Heap to schedule tasks
|
||||
* type Task = [string, number];
|
||||
* type Task = [string, number];
|
||||
*
|
||||
* function scheduleTasks(tasks: Task[], machines: number): Map<number, Task[]> {
|
||||
* const machineHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // Min heap
|
||||
|
|
|
@ -82,13 +82,13 @@ export class DoublyLinkedListNode<E = any> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 1. Node Structure: Each node contains three parts: a data field, a pointer (or reference) to the previous node, and a pointer to the next node. This structure allows traversal of the linked list in both directions.
|
||||
*1. Node Structure: Each node contains three parts: a data field, a pointer (or reference) to the previous node, and a pointer to the next node. This structure allows traversal of the linked list in both directions.
|
||||
* 2. Bidirectional Traversal: Unlike singly linked lists, doubly linked lists can be easily traversed forwards or backwards. This makes insertions and deletions in the list more flexible and efficient.
|
||||
* 3. No Centralized Index: Unlike arrays, elements in a linked list are not stored contiguously, so there is no centralized index. Accessing elements in a linked list typically requires traversing from the head or tail node.
|
||||
* 4. High Efficiency in Insertion and Deletion: Adding or removing elements in a linked list does not require moving other elements, making these operations more efficient than in arrays.
|
||||
* @example
|
||||
* // text editor operation history
|
||||
* const actions = [
|
||||
* const actions = [
|
||||
* { type: 'insert', content: 'first line of text' },
|
||||
* { type: 'insert', content: 'second line of text' },
|
||||
* { type: 'delete', content: 'delete the first line' }
|
||||
|
@ -100,7 +100,7 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* console.log(editorHistory.last?.type); // 'insert'
|
||||
* @example
|
||||
* // Browser history
|
||||
* const browserHistory = new DoublyLinkedList<string>();
|
||||
* const browserHistory = new DoublyLinkedList<string>();
|
||||
*
|
||||
* browserHistory.push('home page');
|
||||
* browserHistory.push('search page');
|
||||
|
@ -111,7 +111,7 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* console.log(browserHistory.last); // 'search page'
|
||||
* @example
|
||||
* // Use DoublyLinkedList to implement music player
|
||||
* // Define the Song interface
|
||||
* // Define the Song interface
|
||||
* interface Song {
|
||||
* title: string;
|
||||
* artist: string;
|
||||
|
@ -236,7 +236,7 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* // ]
|
||||
* @example
|
||||
* // Use DoublyLinkedList to implement LRU cache
|
||||
* interface CacheEntry<K, V> {
|
||||
* interface CacheEntry<K, V> {
|
||||
* key: K;
|
||||
* value: V;
|
||||
* }
|
||||
|
@ -398,7 +398,7 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* console.log(cache.isEmpty); // true
|
||||
* @example
|
||||
* // finding lyrics by timestamp in Coldplay's "Fix You"
|
||||
* // Create a DoublyLinkedList to store song lyrics with timestamps
|
||||
* // Create a DoublyLinkedList to store song lyrics with timestamps
|
||||
* const lyricsList = new DoublyLinkedList<{ time: number; text: string }>();
|
||||
*
|
||||
* // Detailed lyrics with precise timestamps (in milliseconds)
|
||||
|
@ -438,7 +438,7 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* console.log(lateTimeLyric?.text); // 'And I will try to fix you'
|
||||
* @example
|
||||
* // cpu process schedules
|
||||
* class Process {
|
||||
* class Process {
|
||||
* constructor(
|
||||
* public id: number,
|
||||
* public priority: number
|
||||
|
@ -819,7 +819,9 @@ export class DoublyLinkedList<E = any, R = any> extends IterableElementBase<E, R
|
|||
existingElementOrNode: E | DoublyLinkedListNode<E>,
|
||||
newElementOrNode: E | DoublyLinkedListNode<E>
|
||||
): boolean {
|
||||
const existingNode: DoublyLinkedListNode<E> | undefined = this.getNode(existingElementOrNode);
|
||||
const existingNode: DoublyLinkedListNode<E> | undefined = this.isNode(existingElementOrNode)
|
||||
? existingElementOrNode
|
||||
: this.getNode(existingElementOrNode);
|
||||
|
||||
if (existingNode) {
|
||||
const newNode = this._ensureNode(newElementOrNode);
|
||||
|
@ -856,7 +858,9 @@ export class DoublyLinkedList<E = any, R = any> extends IterableElementBase<E, R
|
|||
* was not found in the linked list.
|
||||
*/
|
||||
addAfter(existingElementOrNode: E | DoublyLinkedListNode<E>, newElementOrNode: E | DoublyLinkedListNode<E>): boolean {
|
||||
const existingNode: DoublyLinkedListNode<E> | undefined = this.getNode(existingElementOrNode);
|
||||
const existingNode: DoublyLinkedListNode<E> | undefined = this.isNode(existingElementOrNode)
|
||||
? existingElementOrNode
|
||||
: this.getNode(existingElementOrNode);
|
||||
|
||||
if (existingNode) {
|
||||
const newNode = this._ensureNode(newElementOrNode);
|
||||
|
@ -967,7 +971,7 @@ export class DoublyLinkedList<E = any, R = any> extends IterableElementBase<E, R
|
|||
* Space Complexity: O(1)
|
||||
*
|
||||
* This function finds the index of a specified element, node, or predicate in a doubly linked list.
|
||||
* @param {E | DoublyLinkedListNode<E> | ((node: DoublyLinkedListNode<E>) => boolean)}
|
||||
* @param {E | DoublyLinkedListNode<E> | ((node: DoublyLinkedListNode<E>) => boolean)} elementNodeOrPredicate
|
||||
* elementNodeOrPredicate - The `indexOf` method takes in a parameter `elementNodeOrPredicate`, which
|
||||
* can be one of the following:
|
||||
* @returns The `indexOf` method returns the index of the element in the doubly linked list that
|
||||
|
@ -999,7 +1003,7 @@ export class DoublyLinkedList<E = any, R = any> extends IterableElementBase<E, R
|
|||
* @returns The `get` method returns the value of the first node in the doubly linked list that
|
||||
* satisfies the provided predicate function. If no such node is found, it returns `undefined`.
|
||||
*/
|
||||
get(
|
||||
search(
|
||||
elementNodeOrPredicate: E | DoublyLinkedListNode<E> | ((node: DoublyLinkedListNode<E>) => boolean)
|
||||
): E | undefined {
|
||||
const predicate = this._ensurePredicate(elementNodeOrPredicate);
|
||||
|
|
|
@ -223,7 +223,7 @@ export class SinglyLinkedList<E = any, R = any> extends IterableElementBase<E, R
|
|||
* @returns The `get` method returns the value of the first node in the singly linked list that
|
||||
* satisfies the provided predicate function. If no such node is found, it returns `undefined`.
|
||||
*/
|
||||
get(
|
||||
search(
|
||||
elementNodeOrPredicate: E | SinglyLinkedListNode<E> | ((node: SinglyLinkedListNode<E>) => boolean)
|
||||
): E | undefined {
|
||||
const predicate = this._ensurePredicate(elementNodeOrPredicate);
|
||||
|
|
|
@ -2,4 +2,4 @@ export * from './data-structures';
|
|||
export * from './utils';
|
||||
export * from './interfaces';
|
||||
export * from './types';
|
||||
export * from './constants';
|
||||
export * from './common';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { BinaryTree, BinaryTreeNode } from '../../../data-structures';
|
||||
import { IterationType, OptValue } from '../../common';
|
||||
import { DFSOperation } from '../../../constants';
|
||||
import { DFSOperation } from '../../../common';
|
||||
|
||||
export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { BST, BSTNode } from '../../../data-structures';
|
||||
import type { BinaryTreeOptions } from './binary-tree';
|
||||
import { Comparator } from '../../common';
|
||||
import { Comparable } from '../../utils';
|
||||
|
||||
export type BSTNodeNested<K, V> = BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
export type BSTNested<K, V, R, NODE extends BSTNode<K, V, NODE>> = BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
export type BSTOptions<K, V, R> = BinaryTreeOptions<K, V, R> & {
|
||||
comparator?: Comparator<K>
|
||||
extractComparable?: (key: K) => Comparable
|
||||
isReverse?: boolean;
|
||||
}
|
||||
|
||||
export type BSTNOptKey<K> = K | undefined;
|
||||
|
|
|
@ -7,4 +7,4 @@ export type RedBlackTreeNodeNested<K, V> = RedBlackTreeNode<K, V, RedBlackTreeNo
|
|||
|
||||
export type RedBlackTreeNested<K, V, R, NODE extends RedBlackTreeNode<K, V, NODE>> = RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
export type RBTreeOptions<K, V, R> = BSTOptions<K, V, R> & {};
|
||||
export type RBTreeOptions<K, V, R> = Omit<BSTOptions<K, V, R>, 'isReverse'> & {};
|
||||
|
|
|
@ -7,17 +7,23 @@ export type SpecifyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T,
|
|||
|
||||
export type Any = string | number | bigint | boolean | symbol | undefined | object;
|
||||
|
||||
export type Arithmetic = number | bigint;
|
||||
|
||||
export type ComparablePrimitive = number | bigint | string | boolean;
|
||||
|
||||
// TODO type safety looks not strict
|
||||
export type ComparableObject = { [key in string]: any } & (
|
||||
| {
|
||||
valueOf: () => ComparablePrimitive | ComparableObject;
|
||||
toString?: () => string;
|
||||
}
|
||||
| {
|
||||
toString: () => string;
|
||||
}
|
||||
);
|
||||
export interface BaseComparableObject {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ValueComparableObject extends BaseComparableObject {
|
||||
valueOf: () => ComparablePrimitive | ValueComparableObject;
|
||||
toString?: () => string;
|
||||
}
|
||||
|
||||
export interface StringComparableObject extends BaseComparableObject {
|
||||
toString: () => string;
|
||||
}
|
||||
|
||||
export type ComparableObject = ValueComparableObject | StringComparableObject;
|
||||
|
||||
export type Comparable = ComparablePrimitive | Date | ComparableObject;
|
||||
|
|
|
@ -226,7 +226,8 @@ export const roundFixed = (num: number, digit: number = 10) => {
|
|||
*/
|
||||
function isPrimitiveComparable(value: unknown): value is ComparablePrimitive {
|
||||
const valueType = typeof value;
|
||||
if (valueType === 'number') return !Number.isNaN(value);
|
||||
if (valueType === 'number') return true;
|
||||
// if (valueType === 'number') return !Number.isNaN(value);
|
||||
return valueType === 'bigint' || valueType === 'string' || valueType === 'boolean';
|
||||
}
|
||||
|
||||
|
@ -274,7 +275,8 @@ export function isComparable(value: unknown, isForceObjectComparable = false): v
|
|||
if (isPrimitiveComparable(value)) return true;
|
||||
|
||||
if (typeof value !== 'object') return false;
|
||||
if (value instanceof Date) return !Number.isNaN(value.getTime());
|
||||
if (value instanceof Date) return true;
|
||||
// if (value instanceof Date) return !Number.isNaN(value.getTime());
|
||||
if (isForceObjectComparable) return true;
|
||||
const comparableValue = tryObjectToPrimitive(value);
|
||||
if (comparableValue === null || comparableValue === undefined) return false;
|
||||
|
|
|
@ -19,6 +19,9 @@ suite
|
|||
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.getNode(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => {
|
||||
const entries = [...avlTree];
|
||||
return entries.length === HUNDRED_THOUSAND;
|
||||
|
|
|
@ -23,9 +23,12 @@ suite
|
|||
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.getNode(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode add randomly`, () => {
|
||||
rbTreeNodeMode.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
|
||||
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.add(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.get(randomArray[i]);
|
||||
|
|
|
@ -8,6 +8,22 @@ import { PerformanceTest } from './types';
|
|||
const args = process.argv.slice(2);
|
||||
|
||||
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
||||
const isOnlyOrdered = true;
|
||||
const runOrder = [
|
||||
'heap',
|
||||
'avl-tree',
|
||||
'rb-tree',
|
||||
'doubly-linked-list',
|
||||
'directed-graph',
|
||||
'queue',
|
||||
'deque',
|
||||
'hash-map',
|
||||
'trie',
|
||||
'stack'
|
||||
// 'singly-linked-list',
|
||||
// 'priority-queue',
|
||||
// 'binary-tree-overall'
|
||||
];
|
||||
|
||||
const getRelativePath = (file: string) => {
|
||||
return path.relative(__dirname, file);
|
||||
|
@ -80,7 +96,7 @@ const composeReport = () => {
|
|||
#json-to-html {
|
||||
padding: 0 10px 20px;
|
||||
}
|
||||
|
||||
|
||||
.json-to-html-label {
|
||||
font-size: 2rem;
|
||||
margin: 2rem 0 0 3px;
|
||||
|
@ -92,19 +108,19 @@ const composeReport = () => {
|
|||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
.content table th,
|
||||
.content table td {
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
|
||||
.content table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.content table tr:nth-child(odd) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
@ -188,46 +204,35 @@ function replaceMarkdownContent(startMarker: string, endMarker: string, newText:
|
|||
});
|
||||
}
|
||||
|
||||
const order = [
|
||||
'heap',
|
||||
'rb-tree',
|
||||
'queue',
|
||||
'deque',
|
||||
'hash-map',
|
||||
'trie',
|
||||
'avl-tree',
|
||||
'binary-tree-overall',
|
||||
'directed-graph',
|
||||
'doubly-linked-list',
|
||||
'singly-linked-list',
|
||||
'priority-queue',
|
||||
'stack'
|
||||
];
|
||||
const sortedPerformanceTests = (
|
||||
isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests]
|
||||
).sort((a, b) => {
|
||||
const indexA = runOrder.indexOf(a.testName);
|
||||
const indexB = runOrder.indexOf(b.testName);
|
||||
|
||||
const sortedPerformanceTests = [...performanceTests].sort((a, b) => {
|
||||
const indexA = order.indexOf(a.testName);
|
||||
const indexB = order.indexOf(b.testName);
|
||||
|
||||
// If both a and b are in the order, sort them according to their indices in the order.
|
||||
// If both a and b are in the runOrder, sort them according to their indices in the runOrder.
|
||||
if (indexA !== -1 && indexB !== -1) {
|
||||
return indexA - indexB;
|
||||
}
|
||||
|
||||
// If there is only 'a' in the order, then place 'b' in front.
|
||||
// If there is only 'a' in the runOrder, then place 'b' in front.
|
||||
if (indexA !== -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If only b is in the order, then a should be placed before it.
|
||||
// If only b is in the runOrder, then a should be placed before it.
|
||||
if (indexB !== -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If neither a nor b are in order, keep their original order
|
||||
// If neither a nor b are in runOrder, keep their original runOrder
|
||||
return 0;
|
||||
});
|
||||
|
||||
console.log(`${GREEN} Found tests${END}: ${sortedPerformanceTests.map(test => test.testName)}`);
|
||||
console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`);
|
||||
console.log(
|
||||
`${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
||||
);
|
||||
|
||||
sortedPerformanceTests.forEach(item => {
|
||||
const { suite, testName, file } = item;
|
||||
|
@ -245,22 +250,22 @@ sortedPerformanceTests.forEach(item => {
|
|||
return {
|
||||
'test name': benchmark.name,
|
||||
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
||||
'executions per sec': numberFix(benchmark.hz, 2),
|
||||
// 'executions per sec': numberFix(benchmark.hz, 2),
|
||||
// 'executed times': numberFix(benchmark.count, 0),
|
||||
// 'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
||||
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
||||
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
||||
};
|
||||
});
|
||||
|
||||
report[testName].testName = testName;
|
||||
const isDone = completedCount === performanceTests.length;
|
||||
const isDone = completedCount === sortedPerformanceTests.length;
|
||||
runTime = Number(runTime.toFixed(2));
|
||||
const isTimeWarn = runTime > 120;
|
||||
console.log(
|
||||
// `Files: ${GREEN}${testFileCount}${END} `,
|
||||
// `Suites: ${GREEN}${performanceTests.length}${END} `,
|
||||
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${performanceTests.length}${END}`,
|
||||
`Time: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
||||
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`,
|
||||
`Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
||||
);
|
||||
if (isDone) {
|
||||
composeReport();
|
||||
|
|
|
@ -736,7 +736,7 @@ describe('AVLTree toEntryFn', () => {
|
|||
{ obj: { id: 5 } }
|
||||
])
|
||||
).toThrowError(
|
||||
`When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
|
||||
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -744,7 +744,7 @@ describe('AVLTree toEntryFn', () => {
|
|||
const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>(
|
||||
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
||||
{
|
||||
comparator: (a, b) => a.obj.id - b.obj.id
|
||||
extractComparable: key => key.obj.id
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -103,9 +103,9 @@ describe('BinaryTree addMany', () => {
|
|||
[undefined, 22, 44, 33]
|
||||
);
|
||||
expect(tree.get(2)).toBe(22);
|
||||
expect(tree.get(tree.getNodeByKey(3))).toBe(33);
|
||||
expect(tree.get(tree.getNodeByKey(4))).toBe(44);
|
||||
expect(tree.get(tree.getNodeByKey(1))).toBe(1);
|
||||
expect(tree.get(tree.getNode(3))).toBe(33);
|
||||
expect(tree.get(tree.getNode(4))).toBe(44);
|
||||
expect(tree.get(tree.getNode(1))).toBe(1);
|
||||
});
|
||||
|
||||
it('should addMany undefined and null', () => {
|
||||
|
@ -349,7 +349,7 @@ describe('BinaryTree', () => {
|
|||
expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true);
|
||||
expect(tree.getNodes(2, false, null)).toEqual([]);
|
||||
expect(tree.getNodes(undefined)).toEqual([]);
|
||||
expect(tree.getNodes(tree.getNodeByKey(2), false, tree.root)).toEqual([tree.getNodeByKey(2)]);
|
||||
expect(tree.getNodes(tree.getNode(2), false, tree.root)).toEqual([tree.getNode(2)]);
|
||||
});
|
||||
|
||||
describe('should isKey', () => {
|
||||
|
@ -362,9 +362,9 @@ describe('BinaryTree', () => {
|
|||
expect(tree.isKey(-Infinity)).toBe(true);
|
||||
});
|
||||
|
||||
it('NaN should not be a key', () => {
|
||||
expect(tree.isKey(NaN)).toBe(false);
|
||||
});
|
||||
// it('NaN should not be a key', () => {
|
||||
// expect(tree.isKey(NaN)).toBe(false);
|
||||
// });
|
||||
|
||||
it('strings should be a key', () => {
|
||||
expect(tree.isKey('hello')).toBe(true);
|
||||
|
@ -400,9 +400,9 @@ describe('BinaryTree', () => {
|
|||
expect(tree.isKey(new Date('2024-01-01'))).toBe(true);
|
||||
});
|
||||
|
||||
it('invalid Date objects should not be a key', () => {
|
||||
expect(tree.isKey(new Date('invalid'))).toBe(false);
|
||||
});
|
||||
// it('invalid Date objects should not be a key', () => {
|
||||
// expect(tree.isKey(new Date('invalid'))).toBe(false);
|
||||
// });
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
|
@ -1155,8 +1155,8 @@ describe('BinaryTree', () => {
|
|||
tree.add([3, 'B']);
|
||||
tree.add([7, 'C']);
|
||||
|
||||
expect(tree.getPathToRoot(undefined, 7)).toEqual([5, 7]);
|
||||
expect(tree.getPathToRoot(undefined, 1)).toEqual([]);
|
||||
expect(tree.getPathToRoot(7)).toEqual([7, 5]);
|
||||
expect(tree.getPathToRoot(1)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should check if the tree is perfectly balanced', () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BinaryTreeNode, BST, BSTNode } from '../../../../src';
|
||||
import { BinaryTreeNode, BST, BSTNode, Range } from '../../../../src';
|
||||
import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
|
@ -974,7 +974,7 @@ describe('BST operations test recursively', () => {
|
|||
|
||||
if (isTestStackOverflow) {
|
||||
it('should getLeftMost', () => {
|
||||
const bst = new BST<number>([], { comparator: (a, b) => b - a });
|
||||
const bst = new BST<number>([], { extractComparable: key => key });
|
||||
for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
|
||||
|
||||
expect(() => {
|
||||
|
@ -1009,7 +1009,7 @@ describe('BST isBST', function () {
|
|||
|
||||
it('isBST when variant is Max', () => {
|
||||
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
||||
comparator: (a, b) => b - a
|
||||
isReverse: true
|
||||
});
|
||||
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
||||
expect(bst.isBST()).toBe(true);
|
||||
|
@ -1529,3 +1529,79 @@ describe('BST iterative methods not map mode test', () => {
|
|||
expect(balanced.leaves(node => balanced.get(node?.key))).toEqual(['a', 'f', 'd', 'i']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('classic use', () => {
|
||||
// Test case for finding the kth smallest element
|
||||
it('@example Find kth smallest element', () => {
|
||||
// Create a BST with some elements
|
||||
const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
|
||||
const sortedKeys = bst.dfs(node => node.key, 'IN');
|
||||
|
||||
// Helper function to find kth smallest
|
||||
const findKthSmallest = (k: number): number | undefined => {
|
||||
return sortedKeys[k - 1];
|
||||
};
|
||||
|
||||
// Assertions
|
||||
expect(findKthSmallest(1)).toBe(1);
|
||||
expect(findKthSmallest(3)).toBe(4);
|
||||
expect(findKthSmallest(7)).toBe(8);
|
||||
});
|
||||
|
||||
// Test case for finding elements in a given range
|
||||
it('@example Find elements in a range', () => {
|
||||
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]);
|
||||
expect(bst.search(new Range(4, 12))).toEqual([10, 12, 5, 7]);
|
||||
expect(bst.search(new Range(4, 12, true, false))).toEqual([10, 5, 7]);
|
||||
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
|
||||
expect(bst.search(new Range(15, 20, false))).toEqual([18]);
|
||||
});
|
||||
|
||||
// Test case for Huffman coding simulation
|
||||
it('Huffman coding frequency simulation', () => {
|
||||
// Create a BST to simulate Huffman tree
|
||||
const frequencyBST = new BST<string, number>([
|
||||
['a', 5],
|
||||
['b', 9],
|
||||
['c', 12],
|
||||
['d', 13],
|
||||
['e', 16],
|
||||
['f', 45]
|
||||
]);
|
||||
|
||||
// Sort nodes by frequency
|
||||
const sortedFrequencies = frequencyBST.dfs(node => ({ char: node.key, freq: node.value }), 'IN');
|
||||
|
||||
// Build Huffman tree simulation
|
||||
expect(sortedFrequencies[0].char).toBe('a');
|
||||
expect(sortedFrequencies[5].char).toBe('f');
|
||||
});
|
||||
|
||||
// Test case for Lowest Common Ancestor (LCA)
|
||||
it('@example Find lowest common ancestor', () => {
|
||||
const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
||||
|
||||
function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
|
||||
for (const num of arr1) {
|
||||
if (arr2.indexOf(num) !== -1) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// LCA helper function
|
||||
const findLCA = (num1: number, num2: number): number | undefined => {
|
||||
const path1 = bst.getPathToRoot(num1);
|
||||
const path2 = bst.getPathToRoot(num2);
|
||||
// Find the first common ancestor
|
||||
return findFirstCommon(path1, path2);
|
||||
};
|
||||
|
||||
// Assertions
|
||||
expect(findLCA(3, 10)).toBe(7);
|
||||
expect(findLCA(5, 35)).toBe(15);
|
||||
expect(findLCA(20, 30)).toBe(25);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -58,11 +58,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
it('Should clone a BST works fine', () => {
|
||||
const bst = new BST<number>([3, 6, 7, 1, 9], {
|
||||
iterationType: 'RECURSIVE',
|
||||
comparator: (a, b) => {
|
||||
if (a > b) return -1;
|
||||
if (a < b) return 1;
|
||||
return 0;
|
||||
}
|
||||
isReverse: true
|
||||
});
|
||||
expect(bst.size).toBe(5);
|
||||
expect(bst.root?.key).toBe(6);
|
||||
|
@ -70,7 +66,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(bst.root?.left?.right?.key).toBe(7);
|
||||
expect(bst.root?.right?.key).toBe(3);
|
||||
expect(bst.root?.right?.right?.key).toBe(1);
|
||||
expect(bst.getNodeByKey(9)?.right?.key).toBe(7);
|
||||
expect(bst.getNode(9)?.right?.key).toBe(7);
|
||||
expect(bst.getHeight()).toBe(2);
|
||||
expect(bst.has(9)).toBe(true);
|
||||
expect(bst.has(7)).toBe(true);
|
||||
|
@ -81,7 +77,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(bst.root?.left?.key).toBe(9);
|
||||
expect(bst.root?.right?.key).toBe(3);
|
||||
expect(bst.root?.right?.right?.key).toBe(1);
|
||||
expect(bst.getNodeByKey(6)?.left?.key).toBe(9);
|
||||
expect(bst.getNode(6)?.left?.key).toBe(9);
|
||||
expect(bst.getHeight()).toBe(2);
|
||||
expect(bst.has(9)).toBe(true);
|
||||
expect(bst.has(7)).toBe(false);
|
||||
|
@ -92,7 +88,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(clonedBST.root?.left?.key).toBe(9);
|
||||
expect(clonedBST.root?.right?.key).toBe(3);
|
||||
expect(clonedBST.root?.right?.right?.key).toBe(1);
|
||||
expect(clonedBST.getNodeByKey(6)?.left?.key).toBe(9);
|
||||
expect(clonedBST.getNode(6)?.left?.key).toBe(9);
|
||||
expect(clonedBST.getHeight()).toBe(2);
|
||||
expect(clonedBST.has(9)).toBe(true);
|
||||
expect(clonedBST.has(7)).toBe(false);
|
||||
|
@ -102,11 +98,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
it('Should clone a AVLTree works fine', () => {
|
||||
const avl = new AVLTree<number>([3, 6, 7, 1, 9], {
|
||||
iterationType: 'RECURSIVE',
|
||||
comparator: (a, b) => {
|
||||
if (a > b) return -1;
|
||||
if (a < b) return 1;
|
||||
return 0;
|
||||
}
|
||||
isReverse: true
|
||||
});
|
||||
expect(avl.size).toBe(5);
|
||||
avl.add(2);
|
||||
|
@ -117,7 +109,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(avl.root?.left?.left?.key).toBe(9);
|
||||
expect(avl.root?.right?.key).toBe(1);
|
||||
expect(avl.root?.right?.left?.key).toBe(2);
|
||||
expect(avl.getNodeByKey(7)?.left?.key).toBe(9);
|
||||
expect(avl.getNode(7)?.left?.key).toBe(9);
|
||||
expect(avl.getHeight()).toBe(3);
|
||||
expect(avl.has(9)).toBe(true);
|
||||
expect(avl.has(7)).toBe(true);
|
||||
|
@ -128,7 +120,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(avl.root?.left?.key).toBe(5);
|
||||
expect(avl.root?.right?.key).toBe(1);
|
||||
expect(avl.root?.right?.left?.key).toBe(2);
|
||||
expect(avl.getNodeByKey(6)?.left?.key).toBe(undefined);
|
||||
expect(avl.getNode(6)?.left?.key).toBe(undefined);
|
||||
expect(avl.getHeight()).toBe(3);
|
||||
expect(avl.has(9)).toBe(true);
|
||||
expect(avl.has(7)).toBe(false);
|
||||
|
@ -139,7 +131,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(clonedAVL.root?.left?.key).toBe(5);
|
||||
expect(clonedAVL.root?.right?.key).toBe(1);
|
||||
expect(clonedAVL.root?.right?.left?.key).toBe(2);
|
||||
expect(clonedAVL.getNodeByKey(6)?.left?.key).toBe(undefined);
|
||||
expect(clonedAVL.getNode(6)?.left?.key).toBe(undefined);
|
||||
expect(clonedAVL.getHeight()).toBe(3);
|
||||
expect(clonedAVL.has(9)).toBe(true);
|
||||
expect(clonedAVL.has(7)).toBe(false);
|
||||
|
@ -162,7 +154,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(tmm.root?.left?.left?.key).toBe(NaN);
|
||||
expect(tmm.root?.right?.key).toBe(7);
|
||||
expect(tmm.root?.right?.left?.key).toBe(5);
|
||||
expect(tmm.getNodeByKey(7)?.left?.key).toBe(5);
|
||||
expect(tmm.getNode(7)?.left?.key).toBe(5);
|
||||
expect(tmm.getHeight()).toBe(3);
|
||||
expect(tmm.has(9)).toBe(true);
|
||||
expect(tmm.has(7)).toBe(true);
|
||||
|
@ -174,7 +166,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(tmm.root?.left?.key).toBe(1);
|
||||
expect(tmm.root?.right?.key).toBe(9);
|
||||
expect(tmm.root?.right?.left?.key).toBe(5);
|
||||
expect(tmm.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(tmm.getNode(6)?.left?.key).toBe(NaN);
|
||||
expect(tmm.getHeight()).toBe(3);
|
||||
expect(tmm.has(9)).toBe(true);
|
||||
expect(tmm.has(7)).toBe(false);
|
||||
|
@ -187,7 +179,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(clonedTMM.root?.left?.key).toBe(1);
|
||||
expect(clonedTMM.root?.right?.key).toBe(5);
|
||||
expect(clonedTMM.root?.right?.left?.key).toBe(4);
|
||||
expect(clonedTMM.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedTMM.getNode(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedTMM.getHeight()).toBe(3);
|
||||
expect(clonedTMM.has(9)).toBe(true);
|
||||
expect(clonedTMM.has(7)).toBe(false);
|
||||
|
@ -209,7 +201,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(rbTree.root?.left?.left?.key).toBe(NaN);
|
||||
expect(rbTree.root?.right?.key).toBe(7);
|
||||
expect(rbTree.root?.right?.left?.key).toBe(5);
|
||||
expect(rbTree.getNodeByKey(7)?.left?.key).toBe(5);
|
||||
expect(rbTree.getNode(7)?.left?.key).toBe(5);
|
||||
expect(rbTree.getHeight()).toBe(3);
|
||||
expect(rbTree.has(9)).toBe(true);
|
||||
expect(rbTree.has(7)).toBe(true);
|
||||
|
@ -220,7 +212,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(rbTree.root?.left?.key).toBe(1);
|
||||
expect(rbTree.root?.right?.key).toBe(9);
|
||||
expect(rbTree.root?.right?.left?.key).toBe(5);
|
||||
expect(rbTree.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(rbTree.getNode(6)?.left?.key).toBe(NaN);
|
||||
expect(rbTree.getHeight()).toBe(3);
|
||||
expect(rbTree.has(9)).toBe(true);
|
||||
expect(rbTree.has(7)).toBe(false);
|
||||
|
@ -232,7 +224,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(clonedRbTree.root?.left?.key).toBe(1);
|
||||
expect(clonedRbTree.root?.right?.key).toBe(5);
|
||||
expect(clonedRbTree.root?.right?.left?.key).toBe(4);
|
||||
expect(clonedRbTree.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedRbTree.getNode(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedRbTree.getHeight()).toBe(3);
|
||||
expect(clonedRbTree.has(9)).toBe(true);
|
||||
expect(clonedRbTree.has(7)).toBe(false);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BinaryTreeNode, BSTNode, RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
||||
import { BinaryTreeNode, BSTNode, Range, RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
||||
import { getRandomInt, getRandomIntArray, magnitude } from '../../../utils';
|
||||
import { OrderedMap } from 'js-sdsl';
|
||||
|
||||
|
@ -819,3 +819,102 @@ describe('RedBlackTree - _deleteFixup', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('classic use', () => {
|
||||
it('Database Index: Add, Search, and Delete Records', () => {
|
||||
const dbIndex = new RedBlackTree<number, string>();
|
||||
|
||||
// Insert records
|
||||
dbIndex.add(1, 'Alice');
|
||||
dbIndex.add(2, 'Bob');
|
||||
dbIndex.add(3, 'Charlie');
|
||||
|
||||
// Search for records
|
||||
expect(dbIndex.get(1)).toBe('Alice');
|
||||
expect(dbIndex.get(2)).toBe('Bob');
|
||||
expect(dbIndex.get(3)).toBe('Charlie');
|
||||
|
||||
// Delete a record
|
||||
dbIndex.delete(2);
|
||||
expect(dbIndex.get(2)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('@example Merge 3 sorted datasets', () => {
|
||||
const dataset1 = new RedBlackTree<number, string>([
|
||||
[1, 'A'],
|
||||
[7, 'G']
|
||||
]);
|
||||
const dataset2 = [
|
||||
[2, 'B'],
|
||||
[6, 'F']
|
||||
];
|
||||
const dataset3 = new RedBlackTree<number, string>([
|
||||
[3, 'C'],
|
||||
[5, 'E'],
|
||||
[4, 'D']
|
||||
]);
|
||||
|
||||
// Merge datasets into a single Red-Black Tree
|
||||
const merged = new RedBlackTree<number, string>(dataset1);
|
||||
merged.addMany(dataset2);
|
||||
merged.merge(dataset3);
|
||||
|
||||
// Verify merged dataset is in sorted order
|
||||
expect([...merged.values()]).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
|
||||
});
|
||||
|
||||
// Test case for finding elements in a given range
|
||||
it('Find elements in a range', () => {
|
||||
const bst = new RedBlackTree<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
expect(bst.search(new Range(5, 10))).toEqual([5, 10, 7]);
|
||||
expect(bst.search(new Range(4, 12))).toEqual([5, 10, 12, 7]);
|
||||
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
|
||||
});
|
||||
|
||||
it('Timer List: Manage Timed Tasks', () => {
|
||||
const timerList = new RedBlackTree<number, string>(); // Key: Time in ms, Value: Task Name
|
||||
|
||||
// Schedule tasks
|
||||
timerList.add(100, 'Task A');
|
||||
timerList.add(200, 'Task B');
|
||||
timerList.add(50, 'Task C');
|
||||
|
||||
// Verify the order of tasks by retrieval
|
||||
expect([...timerList.values()]).toEqual(['Task C', 'Task A', 'Task B']); // Sorted by key (time)
|
||||
|
||||
// Remove the earliest task
|
||||
timerList.delete(50);
|
||||
expect([...timerList.values()]).toEqual(['Task A', 'Task B']);
|
||||
});
|
||||
|
||||
it('Scheduler: Manage Tasks by Priority', () => {
|
||||
const scheduler = new RedBlackTree<number, string>(); // Key: Priority, Value: Task Name
|
||||
|
||||
// Add tasks with different priorities
|
||||
scheduler.add(3, 'Low Priority Task');
|
||||
scheduler.add(1, 'High Priority Task');
|
||||
scheduler.add(2, 'Medium Priority Task');
|
||||
|
||||
// Verify the order of tasks by retrieval
|
||||
expect([...scheduler.values()]).toEqual(['High Priority Task', 'Medium Priority Task', 'Low Priority Task']);
|
||||
|
||||
// Remove the highest priority task
|
||||
scheduler.delete(1);
|
||||
expect([...scheduler.values()]).toEqual(['Medium Priority Task', 'Low Priority Task']);
|
||||
});
|
||||
|
||||
it('Routing Table: Manage IP Routes', () => {
|
||||
const routingTable = new RedBlackTree<number, string>(); // Key: IP Address, Value: Route
|
||||
|
||||
// Add routes
|
||||
routingTable.add(1921680101, 'Route A');
|
||||
routingTable.add(1921680102, 'Route B');
|
||||
routingTable.add(1921680100, 'Route C');
|
||||
|
||||
// Search for a specific route
|
||||
expect(routingTable.get(1921680101)).toBe('Route A');
|
||||
|
||||
// Verify all routes in sorted order
|
||||
expect([...routingTable.values()]).toEqual(['Route C', 'Route A', 'Route B']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,9 +16,9 @@ describe('isComparable', () => {
|
|||
expect(isComparable(-Infinity)).toBe(true);
|
||||
});
|
||||
|
||||
it('NaN should not be comparable', () => {
|
||||
expect(isComparable(NaN)).toBe(false);
|
||||
});
|
||||
// it('NaN should not be comparable', () => {
|
||||
// expect(isComparable(NaN)).toBe(false);
|
||||
// });
|
||||
|
||||
it('strings should be comparable', () => {
|
||||
expect(isComparable('hello')).toBe(true);
|
||||
|
@ -54,9 +54,9 @@ describe('isComparable', () => {
|
|||
expect(isComparable(new Date('2024-01-01'))).toBe(true);
|
||||
});
|
||||
|
||||
it('invalid Date objects should not be comparable', () => {
|
||||
expect(isComparable(new Date('invalid'))).toBe(false);
|
||||
});
|
||||
// it('invalid Date objects should not be comparable', () => {
|
||||
// expect(isComparable(new Date('invalid'))).toBe(false);
|
||||
// });
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
|
|
Loading…
Reference in a new issue