feat: Refactor the BinaryTree's dfs method into a private helper function _dfs to prepare for a more generic implementation.

This commit is contained in:
Revone 2024-10-21 19:14:36 +13:00
parent 42235802c3
commit e271f73de0
5 changed files with 340 additions and 110 deletions

View file

@ -823,43 +823,43 @@ 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>8.07</td><td>123.93</td><td>0.00</td></tr><tr><td>100,000 add & poll</td><td>45.32</td><td>22.07</td><td>0.01</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>7.60</td><td>131.64</td><td>2.41e-4</td></tr><tr><td>100,000 add & poll</td><td>44.04</td><td>22.71</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'>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>77.03</td><td>12.98</td><td>0.00</td></tr><tr><td>100,000 add randomly</td><td>83.26</td><td>12.01</td><td>0.00</td></tr><tr><td>100,000 get</td><td>120.25</td><td>8.32</td><td>0.01</td></tr><tr><td>100,000 iterator</td><td>26.32</td><td>37.99</td><td>0.01</td></tr><tr><td>100,000 add & delete orderly</td><td>161.68</td><td>6.19</td><td>0.01</td></tr><tr><td>100,000 add & delete randomly</td><td>268.63</td><td>3.72</td><td>0.03</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>77.11</td><td>12.97</td><td>0.00</td></tr><tr><td>100,000 add randomly</td><td>81.38</td><td>12.29</td><td>0.00</td></tr><tr><td>100,000 get</td><td>112.22</td><td>8.91</td><td>0.00</td></tr><tr><td>100,000 iterator</td><td>28.64</td><td>34.91</td><td>0.00</td></tr><tr><td>100,000 add & delete orderly</td><td>158.80</td><td>6.30</td><td>0.02</td></tr><tr><td>100,000 add & delete randomly</td><td>230.07</td><td>4.35</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.55</td><td>21.96</td><td>0.02</td></tr><tr><td>100,000 push & shift</td><td>5.54</td><td>180.36</td><td>0.00</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2467.71</td><td>0.41</td><td>0.20</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>42.26</td><td>23.67</td><td>0.01</td></tr><tr><td>100,000 push & shift</td><td>5.07</td><td>197.32</td><td>5.97e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2252.79</td><td>0.44</td><td>0.17</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>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>25.36</td><td>39.43</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>32.13</td><td>31.12</td><td>0.00</td></tr><tr><td>1,000,000 push & shift</td><td>33.42</td><td>29.92</td><td>0.00</td></tr><tr><td>100,000 push & shift</td><td>3.59</td><td>278.92</td><td>3.21e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2427.03</td><td>0.41</td><td>0.21</td></tr><tr><td>100,000 unshift & shift</td><td>3.56</td><td>280.81</td><td>3.81e-4</td></tr><tr><td>Native JS Array 100,000 unshift & shift</td><td>4464.18</td><td>0.22</td><td>0.07</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>23.30</td><td>42.92</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>31.90</td><td>31.34</td><td>0.00</td></tr><tr><td>1,000,000 push & shift</td><td>32.70</td><td>30.58</td><td>0.00</td></tr><tr><td>100,000 push & shift</td><td>3.40</td><td>293.73</td><td>2.64e-4</td></tr><tr><td>Native JS Array 100,000 push & shift</td><td>2225.02</td><td>0.45</td><td>0.08</td></tr><tr><td>100,000 unshift & shift</td><td>3.34</td><td>299.68</td><td>2.48e-4</td></tr><tr><td>Native JS Array 100,000 unshift & shift</td><td>3993.22</td><td>0.25</td><td>0.13</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>313.05</td><td>3.19</td><td>0.07</td></tr><tr><td>Native JS Map 1,000,000 set</td><td>224.48</td><td>4.45</td><td>0.05</td></tr><tr><td>Native JS Set 1,000,000 add</td><td>203.46</td><td>4.91</td><td>0.05</td></tr><tr><td>1,000,000 set & get</td><td>281.79</td><td>3.55</td><td>0.07</td></tr><tr><td>Native JS Map 1,000,000 set & get</td><td>289.57</td><td>3.45</td><td>0.03</td></tr><tr><td>Native JS Set 1,000,000 add & has</td><td>195.73</td><td>5.11</td><td>0.02</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>348.39</td><td>2.87</td><td>0.03</td></tr><tr><td>Native JS Map 1,000,000 ObjKey set & get</td><td>315.23</td><td>3.17</td><td>0.04</td></tr><tr><td>Native JS Set 1,000,000 ObjKey add & has</td><td>335.83</td><td>2.98</td><td>0.06</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 set</td><td>106.42</td><td>9.40</td><td>0.01</td></tr><tr><td>Native JS Map 1,000,000 set</td><td>204.68</td><td>4.89</td><td>0.01</td></tr><tr><td>Native JS Set 1,000,000 add</td><td>166.12</td><td>6.02</td><td>0.01</td></tr><tr><td>1,000,000 set & get</td><td>118.98</td><td>8.40</td><td>0.02</td></tr><tr><td>Native JS Map 1,000,000 set & get</td><td>265.16</td><td>3.77</td><td>0.01</td></tr><tr><td>Native JS Set 1,000,000 add & has</td><td>169.18</td><td>5.91</td><td>0.01</td></tr><tr><td>1,000,000 ObjKey set & get</td><td>315.90</td><td>3.17</td><td>0.03</td></tr><tr><td>Native JS Map 1,000,000 ObjKey set & get</td><td>291.11</td><td>3.44</td><td>0.03</td></tr><tr><td>Native JS Set 1,000,000 ObjKey add & has</td><td>272.99</td><td>3.66</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'>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>48.09</td><td>20.79</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>93.17</td><td>10.73</td><td>0.01</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 push</td><td>44.81</td><td>22.31</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>83.75</td><td>11.94</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'>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>278.91</td><td>3.59</td><td>0.01</td></tr><tr><td>100,000 add randomly</td><td>350.65</td><td>2.85</td><td>0.02</td></tr><tr><td>100,000 get</td><td>154.97</td><td>6.45</td><td>0.03</td></tr><tr><td>100,000 iterator</td><td>34.78</td><td>28.75</td><td>0.02</td></tr><tr><td>100,000 add & delete orderly</td><td>445.45</td><td>2.24</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>616.61</td><td>1.62</td><td>0.03</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>270.91</td><td>3.69</td><td>0.02</td></tr><tr><td>100,000 add randomly</td><td>344.71</td><td>2.90</td><td>0.00</td></tr><tr><td>100,000 get</td><td>128.80</td><td>7.76</td><td>0.00</td></tr><tr><td>100,000 iterator</td><td>32.88</td><td>30.41</td><td>0.01</td></tr><tr><td>100,000 add & delete orderly</td><td>456.46</td><td>2.19</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>604.25</td><td>1.65</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.07</td><td>141.45</td><td>7.19e-4</td></tr><tr><td>10,000 RBTree get randomly</td><td>9.33</td><td>107.21</td><td>1.48e-4</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>18.60</td><td>53.77</td><td>3.93e-4</td></tr><tr><td>10,000 AVLTree add randomly</td><td>30.39</td><td>32.91</td><td>0.01</td></tr><tr><td>10,000 AVLTree get randomly</td><td>10.70</td><td>93.49</td><td>0.00</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>46.04</td><td>21.72</td><td>0.00</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 RBTree add randomly</td><td>6.64</td><td>150.66</td><td>8.06e-5</td></tr><tr><td>10,000 RBTree get randomly</td><td>9.24</td><td>108.23</td><td>1.40e-4</td></tr><tr><td>10,000 RBTree add & delete randomly</td><td>18.25</td><td>54.79</td><td>2.59e-4</td></tr><tr><td>10,000 AVLTree add randomly</td><td>23.57</td><td>42.43</td><td>1.78e-4</td></tr><tr><td>10,000 AVLTree get randomly</td><td>9.69</td><td>103.21</td><td>8.94e-5</td></tr><tr><td>10,000 AVLTree add & delete randomly</td><td>44.37</td><td>22.54</td><td>4.23e-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.13</td><td>7502.53</td><td>6.69e-5</td></tr><tr><td>1,000 addEdge</td><td>7.93</td><td>126.13</td><td>0.00</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.19e+4</td><td>1.17e-5</td></tr><tr><td>1,000 getEdge</td><td>18.65</td><td>53.61</td><td>0.00</td></tr><tr><td>tarjan</td><td>158.86</td><td>6.30</td><td>0.01</td></tr><tr><td>topologicalSort</td><td>141.91</td><td>7.05</td><td>0.01</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 addVertex</td><td>0.10</td><td>9718.49</td><td>1.33e-6</td></tr><tr><td>1,000 addEdge</td><td>6.28</td><td>159.34</td><td>1.59e-4</td></tr><tr><td>1,000 getVertex</td><td>0.04</td><td>2.57e+4</td><td>5.33e-7</td></tr><tr><td>1,000 getEdge</td><td>22.43</td><td>44.58</td><td>0.00</td></tr><tr><td>tarjan</td><td>200.86</td><td>4.98</td><td>0.01</td></tr><tr><td>topologicalSort</td><td>176.95</td><td>5.65</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'>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>211.26</td><td>4.73</td><td>0.03</td></tr><tr><td>1,000,000 unshift</td><td>216.90</td><td>4.61</td><td>0.07</td></tr><tr><td>1,000,000 unshift & shift</td><td>208.45</td><td>4.80</td><td>0.05</td></tr><tr><td>1,000,000 addBefore</td><td>316.55</td><td>3.16</td><td>0.06</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>205.86</td><td>4.86</td><td>0.07</td></tr><tr><td>1,000,000 unshift</td><td>199.96</td><td>5.00</td><td>0.03</td></tr><tr><td>1,000,000 unshift & shift</td><td>180.94</td><td>5.53</td><td>0.02</td></tr><tr><td>1,000,000 addBefore</td><td>308.98</td><td>3.24</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'>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>222.81</td><td>4.49</td><td>0.08</td></tr><tr><td>10,000 push & pop</td><td>235.91</td><td>4.24</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>250.57</td><td>3.99</td><td>0.01</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push & shift</td><td>206.48</td><td>4.84</td><td>0.09</td></tr><tr><td>10,000 push & pop</td><td>224.13</td><td>4.46</td><td>0.01</td></tr><tr><td>10,000 addBefore</td><td>246.92</td><td>4.05</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'>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>27.64</td><td>36.17</td><td>8.81e-4</td></tr><tr><td>100,000 add & poll</td><td>75.52</td><td>13.24</td><td>9.91e-4</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>27.02</td><td>37.01</td><td>2.69e-4</td></tr><tr><td>100,000 add & poll</td><td>75.97</td><td>13.16</td><td>5.61e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>stack</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>40.47</td><td>24.71</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>47.47</td><td>21.07</td><td>0.01</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>38.52</td><td>25.96</td><td>0.00</td></tr><tr><td>1,000,000 push & pop</td><td>46.95</td><td>21.30</td><td>0.01</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -27,6 +27,7 @@ import { trampoline } from '../../utils';
import { Queue } from '../queue';
import { IterableEntryBase } from '../base';
import * as console from 'console';
import { DFSOperation, DFSStackItem } from '../../types';
/**
* Represents a node in a binary tree.
@ -1373,8 +1374,7 @@ export class BinaryTree<
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: R | BTNKeyOrNodeOrEntry<K, V, NODE>,
iterationType?: IterationType,
includeNull?: false
iterationType?: IterationType
): ReturnType<C>[];
dfs<C extends BTNCallback<NODE | null>>(
@ -1382,7 +1382,7 @@ export class BinaryTree<
pattern?: DFSOrderPattern,
beginRoot?: R | BTNKeyOrNodeOrEntry<K, V, NODE>,
iterationType?: IterationType,
includeNull?: true
includeNull?: boolean
): ReturnType<C>[];
/**
@ -1422,90 +1422,7 @@ export class BinaryTree<
): ReturnType<C>[] {
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return [];
const ans: ReturnType<C>[] = [];
if (iterationType === 'RECURSIVE') {
const dfs = (node: OptBTNOrNull<NODE>) => {
switch (pattern) {
case 'IN':
if (includeNull) {
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) dfs(node.left);
this.isNodeOrNull(node) && ans.push(callback(node));
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) dfs(node.right);
} else {
if (this.isRealNode(node) && this.isRealNode(node.left)) dfs(node.left);
this.isRealNode(node) && ans.push(callback(node));
if (this.isRealNode(node) && this.isRealNode(node.right)) dfs(node.right);
}
break;
case 'PRE':
if (includeNull) {
this.isNodeOrNull(node) && ans.push(callback(node));
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) dfs(node.left);
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) dfs(node.right);
} else {
this.isRealNode(node) && ans.push(callback(node));
if (this.isRealNode(node) && this.isRealNode(node.left)) dfs(node.left);
if (this.isRealNode(node) && this.isRealNode(node.right)) dfs(node.right);
}
break;
case 'POST':
if (includeNull) {
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) dfs(node.left);
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) dfs(node.right);
this.isNodeOrNull(node) && ans.push(callback(node));
} else {
if (this.isRealNode(node) && this.isRealNode(node.left)) dfs(node.left);
if (this.isRealNode(node) && this.isRealNode(node.right)) dfs(node.right);
this.isRealNode(node) && ans.push(callback(node));
}
break;
}
};
dfs(beginRoot);
} else {
// 0: visit, 1: print
const stack: { opt: 0 | 1; node: OptBTNOrNull<NODE> }[] = [{ opt: 0, node: beginRoot }];
while (stack.length > 0) {
const cur = stack.pop();
if (cur === undefined || this.isNIL(cur.node)) continue;
if (includeNull) {
if (cur.node === undefined) continue;
} else {
if (cur.node === null || cur.node === undefined) continue;
}
if (cur.opt === 1) {
ans.push(callback(cur.node));
} else {
switch (pattern) {
case 'IN':
cur.node && stack.push({ opt: 0, node: cur.node.right });
stack.push({ opt: 1, node: cur.node });
cur.node && stack.push({ opt: 0, node: cur.node.left });
break;
case 'PRE':
cur.node && stack.push({ opt: 0, node: cur.node.right });
cur.node && stack.push({ opt: 0, node: cur.node.left });
stack.push({ opt: 1, node: cur.node });
break;
case 'POST':
stack.push({ opt: 1, node: cur.node });
cur.node && stack.push({ opt: 0, node: cur.node.right });
cur.node && stack.push({ opt: 0, node: cur.node.left });
break;
default:
cur.node && stack.push({ opt: 0, node: cur.node.right });
stack.push({ opt: 1, node: cur.node });
cur.node && stack.push({ opt: 0, node: cur.node.left });
break;
}
}
}
}
return ans;
return this._dfs(callback, pattern, beginRoot, iterationType, includeNull);
}
bfs<C extends BTNCallback<NODE>>(
@ -2004,6 +1921,163 @@ export class BinaryTree<
display(beginRoot);
}
protected _dfs<C extends BTNCallback<NODE>>(
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: R | BTNKeyOrNodeOrEntry<K, V, NODE>,
iterationType?: IterationType
): ReturnType<C>[];
protected _dfs<C extends BTNCallback<NODE | null>>(
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: R | BTNKeyOrNodeOrEntry<K, V, NODE>,
iterationType?: IterationType,
includeNull?: boolean
): ReturnType<C>[];
/**
* Time complexity: O(n)
* Space complexity: O(n)
*/
/**
* Time complexity: O(n)
* Space complexity: O(n)
*
* The `dfs` function performs a depth-first search traversal on a binary tree, executing a callback
* function on each node according to a specified pattern and iteration type.
* @param {C} callback - The `callback` parameter is a function that will be called for each node
* visited during the depth-first search. It takes a node as an argument and returns a value. The
* return type of the callback function is determined by the generic type `C`.
* @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter determines the order in which the
* nodes are visited during the depth-first search. It can have one of the following values:
* @param {R | BTNKeyOrNodeOrEntry<K, V, NODE>} beginRoot - The `beginRoot` parameter is the starting
* point of the depth-first search. It can be either a node object, a key-value pair, or a key. If it
* is a key or key-value pair, the method will find the corresponding node in the tree and start the
* search from there.
* @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter determines the
* type of iteration to use during the depth-first search. It can have two possible values:
* @param [includeNull=false] - The `includeNull` parameter is a boolean value that determines
* whether or not to include null values in the depth-first search traversal. If `includeNull` is set
* to `true`, null values will be included in the traversal. If `includeNull` is set to `false`, null
* values will
* @returns an array of the return types of the callback function.
*/
protected _dfs<C extends BTNCallback<OptBTNOrNull<NODE>>>(
callback: C = this._DEFAULT_CALLBACK as C,
pattern: DFSOrderPattern = 'IN',
beginRoot: R | BTNKeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType: IterationType = this.iterationType,
includeNull = false
): ReturnType<C>[] {
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return [];
const ans: ReturnType<C>[] = [];
if (iterationType === 'RECURSIVE') {
const visitNullableLeft = (node: OptBTNOrNull<NODE>) => {
if (node && this.isNodeOrNull(node.left)) dfs(node.left);
};
const visitLeft = (node: OptBTNOrNull<NODE>) => {
if (node && this.isRealNode(node.left)) dfs(node.left);
};
const visitNullableRight = (node: OptBTNOrNull<NODE>) => {
if (node && this.isNodeOrNull(node.right)) dfs(node.right);
};
const visitRight = (node: OptBTNOrNull<NODE>) => {
if (node && this.isRealNode(node.right)) dfs(node.right);
};
const dfs = (node: OptBTNOrNull<NODE>) => {
switch (pattern) {
case 'IN':
if (includeNull) {
visitNullableLeft(node);
ans.push(callback(node));
visitNullableRight(node);
} else {
visitLeft(node);
ans.push(callback(node));
visitRight(node);
}
break;
case 'PRE':
if (includeNull) {
ans.push(callback(node));
visitNullableLeft(node);
visitNullableRight(node);
} else {
ans.push(callback(node));
visitLeft(node);
visitRight(node);
}
break;
case 'POST':
if (includeNull) {
visitNullableLeft(node);
visitNullableRight(node);
ans.push(callback(node));
} else {
visitLeft(node);
visitRight(node);
ans.push(callback(node));
}
break;
}
};
dfs(beginRoot);
} else {
const stack: DFSStackItem<NODE>[] = [{ opt: DFSOperation.VISIT, node: beginRoot }];
const pushLeft = (cur: DFSStackItem<NODE>) => {
cur.node && stack.push({ opt: DFSOperation.VISIT, node: cur.node.left });
};
const pushRight = (cur: DFSStackItem<NODE>) => {
cur.node && stack.push({ opt: DFSOperation.VISIT, node: cur.node.right });
};
const pushParent = (cur: DFSStackItem<NODE>) => {
stack.push({ opt: DFSOperation.PROCESS, node: cur.node });
};
while (stack.length > 0) {
const cur = stack.pop();
if (cur === undefined) continue;
if (includeNull) {
if (!this.isNodeOrNull(cur.node)) continue;
} else {
if (!this.isRealNode(cur.node)) continue;
}
if (cur.opt === 1) {
ans.push(callback(cur.node));
} else {
switch (pattern) {
case 'IN':
pushRight(cur);
pushParent(cur);
pushLeft(cur);
break;
case 'PRE':
pushRight(cur);
pushLeft(cur);
pushParent(cur);
break;
case 'POST':
pushParent(cur);
pushRight(cur);
pushLeft(cur);
break;
default:
pushRight(cur);
pushParent(cur);
pushLeft(cur);
break;
}
}
}
}
return ans;
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)

View file

@ -557,7 +557,7 @@ export class BST<
beginRoot: R | BTNKeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType: IterationType = this.iterationType
): ReturnType<C>[] {
return super.dfs(callback, pattern, beginRoot, iterationType, false);
return super.dfs(callback, pattern, beginRoot, iterationType);
}
/**

View file

@ -28,4 +28,11 @@ export type BTNPureKeyOrNodeOrEntry<K, V, NODE> = [K, OptValue<V>] | BTNPureKeyO
export type BinaryTreeDeleteResult<NODE> = { deleted: OptBTNOrNull<NODE>; needBalanced: OptBTNOrNull<NODE> };
export type BTNCallback<NODE, D = any> = (node: NODE) => D;
export type BTNCallback<NODE, D = any> = (node: NODE) => D;
export enum DFSOperation {
VISIT = 0,
PROCESS = 1,
}
export type DFSStackItem<NODE> = { opt: DFSOperation; node: OptBTNOrNull<NODE> }

View file

@ -292,29 +292,178 @@ describe('BinaryTree', () => {
expect(tree.getNodes(tree.getNodeByKey(2), undefined, false, tree.root)).toEqual([tree.getNodeByKey(2)]);
});
it('should tree traverse', () => {
tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
expect(tree.dfs(node => node.key, 'PRE', undefined, 'ITERATIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', false)).toEqual([
4, 2, 1, 5, 6, 3, 7
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', true)).toEqual([
4,
2,
null,
1,
5,
null,
6,
3,
7,
null
]);
expect(tree.dfs(node => node.key, 'PRE', undefined, 'RECURSIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', false)).toEqual([
4, 2, 1, 5, 6, 3, 7
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', true)).toEqual([
4,
2,
null,
1,
5,
null,
6,
3,
7,
null
]);
expect(tree.dfs(node => node.key, 'IN', undefined, 'ITERATIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', false)).toEqual([
2, 5, 1, 4, 7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', true)).toEqual([
null,
2,
5,
1,
null,
4,
7,
3,
6,
null
]);
expect(tree.dfs(node => node.key, 'IN', undefined, 'RECURSIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', false)).toEqual([
2, 5, 1, 4, 7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', true)).toEqual([
null,
2,
5,
1,
null,
4,
7,
3,
6,
null
]);
expect(tree.dfs(node => node.key, 'POST', undefined, 'ITERATIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', false)).toEqual([
5, 1, 2, 7, 3, 6, 4
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', true)).toEqual([
null,
5,
null,
1,
2,
7,
3,
null,
6,
4
]);
expect(tree.dfs(node => node.key, 'POST', undefined, 'RECURSIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', false)).toEqual([
5, 1, 2, 7, 3, 6, 4
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', true)).toEqual([
null,
5,
null,
1,
2,
7,
3,
null,
6,
4
]);
});
it('should sub tree traverse', () => {
tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
expect(tree.dfs(node => node.key, 'PRE', tree.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]);
expect(tree.dfs(node => node.key, 'PRE', tree.getNode(6), 'ITERATIVE', false)).toEqual([6, 3, 7]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'ITERATIVE', false)).toEqual([
6, 3, 7
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'ITERATIVE', true)).toEqual([
6,
3,
7,
null
]);
expect(tree.dfs(node => node.key, 'PRE', tree.getNode(6), 'RECURSIVE')).toEqual([6, 3, 7]);
expect(tree.dfs(node => (node ? node.key : null), 'PRE', tree.getNode(6), 'ITERATIVE', true)).toEqual([
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'RECURSIVE', false)).toEqual([
6, 3, 7
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'RECURSIVE', true)).toEqual([
6,
3,
7,
null
]);
expect(tree.dfs(node => (node ? node.key : node), 'PRE', tree.getNode(6), 'ITERATIVE', true)).toEqual([
6,
3,
expect(tree.dfs(node => node.key, 'IN', tree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'ITERATIVE', false)).toEqual([
7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'ITERATIVE', true)).toEqual([
7,
3,
6,
null
]);
expect(tree.dfs(node => (node ? node.key : null), 'PRE', tree.getNode(6), 'RECURSIVE', true)).toEqual([
6,
3,
expect(tree.dfs(node => node.key, 'IN', tree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'RECURSIVE', false)).toEqual([
7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'RECURSIVE', true)).toEqual([
7,
3,
6,
null
]);
expect(tree.dfs(node => node.key, 'POST', tree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'ITERATIVE', false)).toEqual([
7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'ITERATIVE', true)).toEqual([
7,
3,
null,
6
]);
expect(tree.dfs(node => node.key, 'POST', tree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'RECURSIVE', false)).toEqual([
7, 3, 6
]);
expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'RECURSIVE', true)).toEqual([
7,
3,
null,
6
]);
});
it('should clear the tree', () => {