[binary-tree] All traversal methods offer an includeNull parameter to support the output of null nodes.

This commit is contained in:
Revone 2023-11-05 21:19:59 +08:00
parent 3596ca53a7
commit d6b394446a
4 changed files with 249 additions and 76 deletions

View file

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

View file

@ -658,40 +658,40 @@ optimal approach to data structure design.
[//]: # (Start of Replace Section)
<div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>avl-tree</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>30.29</td><td>33.01</td><td>3.64e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>68.10</td><td>14.68</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>39.54</td><td>25.29</td><td>4.07e-4</td></tr><tr><td>10,000 get</td><td>26.72</td><td>37.42</td><td>3.77e-4</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>30.18</td><td>33.13</td><td>2.75e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>65.65</td><td>15.23</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>39.56</td><td>25.28</td><td>3.25e-4</td></tr><tr><td>10,000 get</td><td>26.57</td><td>37.63</td><td>1.92e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>binary-tree</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 add randomly</td><td>12.88</td><td>77.63</td><td>1.02e-4</td></tr><tr><td>1,000 add & delete randomly</td><td>15.90</td><td>62.88</td><td>1.08e-4</td></tr><tr><td>1,000 addMany</td><td>10.59</td><td>94.41</td><td>8.39e-5</td></tr><tr><td>1,000 get</td><td>18.01</td><td>55.53</td><td>1.95e-4</td></tr><tr><td>1,000 dfs</td><td>69.11</td><td>14.47</td><td>6.47e-4</td></tr><tr><td>1,000 bfs</td><td>54.42</td><td>18.38</td><td>4.20e-4</td></tr><tr><td>1,000 morris</td><td>37.14</td><td>26.92</td><td>2.27e-4</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 add randomly</td><td>13.00</td><td>76.93</td><td>3.84e-4</td></tr><tr><td>1,000 add & delete randomly</td><td>16.11</td><td>62.06</td><td>3.76e-4</td></tr><tr><td>1,000 addMany</td><td>10.63</td><td>94.04</td><td>1.30e-4</td></tr><tr><td>1,000 get</td><td>18.24</td><td>54.84</td><td>5.04e-4</td></tr><tr><td>1,000 dfs</td><td>69.77</td><td>14.33</td><td>4.07e-4</td></tr><tr><td>1,000 bfs</td><td>54.49</td><td>18.35</td><td>4.96e-4</td></tr><tr><td>1,000 morris</td><td>37.10</td><td>26.96</td><td>2.35e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>bst</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>28.48</td><td>35.11</td><td>2.29e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>64.82</td><td>15.43</td><td>0.01</td></tr><tr><td>10,000 addMany</td><td>28.74</td><td>34.80</td><td>9.06e-4</td></tr><tr><td>10,000 get</td><td>27.38</td><td>36.52</td><td>1.82e-4</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>31.88</td><td>31.37</td><td>3.10e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>73.08</td><td>13.68</td><td>0.01</td></tr><tr><td>10,000 addMany</td><td>28.80</td><td>34.73</td><td>0.00</td></tr><tr><td>10,000 get</td><td>27.59</td><td>36.25</td><td>2.19e-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><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 randomly</td><td>72.30</td><td>13.83</td><td>0.00</td></tr><tr><td>100,000 add & 1000 delete randomly</td><td>81.37</td><td>12.29</td><td>0.01</td></tr><tr><td>100,000 getNode</td><td>59.48</td><td>16.81</td><td>9.29e-4</td></tr></table></div>
<div class="content"><table><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 randomly</td><td>70.97</td><td>14.09</td><td>0.00</td></tr><tr><td>100,000 add & 1000 delete randomly</td><td>82.03</td><td>12.19</td><td>0.01</td></tr><tr><td>100,000 getNode</td><td>59.75</td><td>16.74</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'>directed-graph</span></div>
<div class="content"><table><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>9786.77</td><td>5.56e-7</td></tr><tr><td>1,000 addEdge</td><td>6.02</td><td>166.04</td><td>1.27e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.18e+4</td><td>3.02e-7</td></tr><tr><td>1,000 getEdge</td><td>23.41</td><td>42.71</td><td>0.00</td></tr><tr><td>tarjan</td><td>223.51</td><td>4.47</td><td>0.01</td></tr><tr><td>tarjan all</td><td>224.89</td><td>4.45</td><td>0.00</td></tr><tr><td>topologicalSort</td><td>181.90</td><td>5.50</td><td>0.00</td></tr></table></div>
<div class="content"><table><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>9894.62</td><td>1.57e-6</td></tr><tr><td>1,000 addEdge</td><td>6.17</td><td>162.15</td><td>0.00</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.17e+4</td><td>4.06e-7</td></tr><tr><td>1,000 getEdge</td><td>23.50</td><td>42.56</td><td>0.00</td></tr><tr><td>tarjan</td><td>223.18</td><td>4.48</td><td>0.01</td></tr><tr><td>tarjan all</td><td>226.10</td><td>4.42</td><td>0.01</td></tr><tr><td>topologicalSort</td><td>186.20</td><td>5.37</td><td>0.02</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>heap</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add & pop</td><td>4.62</td><td>216.33</td><td>3.06e-5</td></tr><tr><td>10,000 fib add & pop</td><td>351.41</td><td>2.85</td><td>0.00</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add & pop</td><td>4.64</td><td>215.30</td><td>4.51e-5</td></tr><tr><td>10,000 fib add & pop</td><td>351.83</td><td>2.84</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><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 unshift</td><td>214.84</td><td>4.65</td><td>0.04</td></tr><tr><td>1,000,000 unshift & shift</td><td>167.11</td><td>5.98</td><td>0.04</td></tr><tr><td>1,000,000 insertBefore</td><td>335.78</td><td>2.98</td><td>0.07</td></tr></table></div>
<div class="content"><table><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 unshift</td><td>216.19</td><td>4.63</td><td>0.04</td></tr><tr><td>1,000,000 unshift & shift</td><td>164.84</td><td>6.07</td><td>0.02</td></tr><tr><td>1,000,000 insertBefore</td><td>325.14</td><td>3.08</td><td>0.07</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>singly-linked-list</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 push & pop</td><td>212.53</td><td>4.71</td><td>0.01</td></tr><tr><td>10,000 insertBefore</td><td>243.94</td><td>4.10</td><td>0.00</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 push & pop</td><td>213.24</td><td>4.69</td><td>0.01</td></tr><tr><td>10,000 insertBefore</td><td>247.69</td><td>4.04</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'>max-priority-queue</span></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 refill & poll</td><td>11.43</td><td>87.50</td><td>1.84e-4</td></tr></table></div>
<div class="content"><table><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 refill & poll</td><td>11.50</td><td>86.97</td><td>1.99e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>deque</span></div>
<div class="content"><table><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>216.07</td><td>4.63</td><td>0.05</td></tr><tr><td>1,000,000 shift</td><td>24.97</td><td>40.05</td><td>0.00</td></tr></table></div>
<div class="content"><table><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>223.30</td><td>4.48</td><td>0.08</td></tr><tr><td>1,000,000 shift</td><td>24.86</td><td>40.23</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><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.57</td><td>23.49</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>79.94</td><td>12.51</td><td>9.99e-4</td></tr></table></div>
<div class="content"><table><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.66</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>79.22</td><td>12.62</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'>trie</span></div>
<div class="content"><table><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>54.02</td><td>18.51</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>82.83</td><td>12.07</td><td>0.00</td></tr></table></div>
<div class="content"><table><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>52.92</td><td>18.90</td><td>0.00</td></tr><tr><td>100,000 getWords</td><td>83.37</td><td>11.99</td><td>0.00</td></tr></table></div>
</div>
[//]: # (End of Replace Section)

View file

@ -108,8 +108,7 @@ export class BinaryTreeNode<V = any, N extends BinaryTreeNode<V, N> = BinaryTree
* @template N - The type of the binary tree's nodes.
*/
export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode<V, BinaryTreeNodeNested<V>>>
implements IBinaryTree<V, N>
{
implements IBinaryTree<V, N> {
iterationType: IterationType = IterationType.ITERATIVE;
/**
@ -391,7 +390,7 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
return -1;
}
const stack: {node: N; depth: number}[] = [{node: beginRoot, depth: 0}];
const stack: { node: N; depth: number }[] = [{node: beginRoot, depth: 0}];
let maxHeight = 0;
while (stack.length > 0) {
@ -846,6 +845,27 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
return this.isSubtreeBST(this.root, iterationType);
}
subTreeTraverse<C extends BTNCallback<N>>(
callback?: C,
beginRoot?: BTNKey | N | null,
iterationType?: IterationType,
includeNull?: false
): ReturnType<C>[]
subTreeTraverse<C extends BTNCallback<N>>(
callback?: C,
beginRoot?: BTNKey | N | null,
iterationType?: IterationType,
includeNull?: undefined
): ReturnType<C>[]
subTreeTraverse<C extends BTNCallback<N | null>>(
callback?: C,
beginRoot?: BTNKey | N | null,
iterationType?: IterationType,
includeNull?: true
): ReturnType<C>[]
/**
* The function `subTreeTraverse` traverses a binary tree and applies a callback function to each
* node, either recursively or iteratively.
@ -858,40 +878,79 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
* start from the root of the tree.
* @param iterationType - The `iterationType` parameter determines the type of traversal to be
* performed on the binary tree. It can have two possible values:
* @param includeNull - The choice to output null values during binary tree traversal should be provided.
* @returns The function `subTreeTraverse` returns an array of `ReturnType<BTNCallback<N>>`.
*/
subTreeTraverse<C extends BTNCallback<N>>(
subTreeTraverse<C extends BTNCallback<N | null>>(
callback: C = this.defaultOneParamCallback as C,
beginRoot: BTNKey | N | null = this.root,
iterationType = this.iterationType
iterationType = this.iterationType,
includeNull = false
): ReturnType<C>[] {
if (typeof beginRoot === 'number') beginRoot = this.getNode(beginRoot);
const ans: ReturnType<BTNCallback<N>>[] = [];
const ans: (ReturnType<BTNCallback<N>> | null)[] = [];
if (!beginRoot) return ans;
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
ans.push(callback(cur));
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
const _traverse = (cur: N | null) => {
if (cur !== undefined) {
ans.push(callback(cur));
if (includeNull) {
cur !== null && cur.left !== undefined && _traverse(cur.left);
cur !== null && cur.right !== undefined && _traverse(cur.right);
} else {
cur !== null && cur.left && _traverse(cur.left);
cur !== null && cur.right && _traverse(cur.right);
}
}
};
_traverse(beginRoot);
} else {
const stack: N[] = [beginRoot];
const stack: (N| null)[] = [beginRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
ans.push(callback(cur));
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
const cur = stack.pop();
if (cur !== undefined) {
ans.push(callback(cur));
if (includeNull) {
cur !== null && cur.right !== undefined && stack.push(cur.right);
cur !== null && cur.left !== undefined && stack.push(cur.left);
} else {
cur !== null && cur.right && stack.push(cur.right);
cur !== null && cur.left && stack.push(cur.left);
}
}
}
}
return ans;
}
dfs<C extends BTNCallback<N>>(
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: false
): ReturnType<C>[]
dfs<C extends BTNCallback<N>>(
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: undefined
): ReturnType<C>[]
dfs<C extends BTNCallback<N | null>>(
callback?: C,
pattern?: DFSOrderPattern,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: true
): ReturnType<C>[]
/**
* The `dfs` function performs a depth-first search traversal on a binary tree, executing a callback
* function on each node according to a specified order pattern.
@ -905,34 +964,53 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
* is `null`, an empty array will be returned.
* @param {IterationType} iterationType - The `iterationType` parameter determines the type of
* iteration used in the depth-first search algorithm. It can have two possible values:
* @param includeNull - The choice to output null values during binary tree traversal should be provided.
* @returns The function `dfs` returns an array of `ReturnType<BTNCallback<N>>` values.
*/
dfs<C extends BTNCallback<N>>(
dfs<C extends BTNCallback<N | null>>(
callback: C = this.defaultOneParamCallback as C,
pattern: DFSOrderPattern = 'in',
beginRoot: N | null = this.root,
iterationType: IterationType = IterationType.ITERATIVE
iterationType: IterationType = IterationType.ITERATIVE,
includeNull = false
): ReturnType<C>[] {
if (!beginRoot) return [];
const ans: ReturnType<BTNCallback<N>>[] = [];
const ans: ReturnType<C>[] = [];
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (node: N) => {
const _traverse = (node: N | null) => {
switch (pattern) {
case 'in':
if (node.left) _traverse(node.left);
ans.push(callback(node));
if (node.right) _traverse(node.right);
if (includeNull) {
if (node !== null && node.left !== undefined) _traverse(node.left);
ans.push(callback(node));
if (node !== null && node.right !== undefined) _traverse(node.right);
} else {
if (node !== null && node.left) _traverse(node.left);
ans.push(callback(node));
if (node !== null && node.right) _traverse(node.right);
}
break;
case 'pre':
ans.push(callback(node));
if (node.left) _traverse(node.left);
if (node.right) _traverse(node.right);
if (includeNull) {
ans.push(callback(node));
if (node !== null && node.left !== undefined) _traverse(node.left);
if (node !== null && node.right !== undefined) _traverse(node.right);
} else {
ans.push(callback(node));
if (node !== null && node.left) _traverse(node.left);
if (node !== null && node.right) _traverse(node.right);
}
break;
case 'post':
if (node.left) _traverse(node.left);
if (node.right) _traverse(node.right);
ans.push(callback(node));
if (includeNull) {
if (node !== null && node.left !== undefined) _traverse(node.left);
if (node !== null && node.right !== undefined) _traverse(node.right);
ans.push(callback(node));
} else {
if (node !== null && node.left) _traverse(node.left);
if (node !== null && node.right) _traverse(node.right);
ans.push(callback(node));
}
break;
}
@ -941,34 +1019,39 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
_traverse(beginRoot);
} else {
// 0: visit, 1: print
const stack: {opt: 0 | 1; node: N | null | undefined}[] = [{opt: 0, node: beginRoot}];
const stack: { opt: 0 | 1; node: N | null | undefined }[] = [{opt: 0, node: beginRoot}];
while (stack.length > 0) {
const cur = stack.pop();
if (!cur || !cur.node) continue;
if (cur === undefined) 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':
stack.push({opt: 0, node: cur.node.right});
cur.node && stack.push({opt: 0, node: cur.node.right});
stack.push({opt: 1, node: cur.node});
stack.push({opt: 0, node: cur.node.left});
cur.node && stack.push({opt: 0, node: cur.node.left});
break;
case 'pre':
stack.push({opt: 0, node: cur.node.right});
stack.push({opt: 0, node: cur.node.left});
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});
stack.push({opt: 0, node: cur.node.right});
stack.push({opt: 0, node: cur.node.left});
cur.node && stack.push({opt: 0, node: cur.node.right});
cur.node && stack.push({opt: 0, node: cur.node.left});
break;
default:
stack.push({opt: 0, node: cur.node.right});
cur.node && stack.push({opt: 0, node: cur.node.right});
stack.push({opt: 1, node: cur.node});
stack.push({opt: 0, node: cur.node.left});
cur.node && stack.push({opt: 0, node: cur.node.left});
break;
}
}
@ -978,6 +1061,27 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
return ans;
}
bfs<C extends BTNCallback<N>>(
callback?: C,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: false
): ReturnType<C>[]
bfs<C extends BTNCallback<N>>(
callback?: C,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: undefined
): ReturnType<C>[]
bfs<C extends BTNCallback<N | null>>(
callback?: C,
beginRoot?: N | null,
iterationType?: IterationType,
includeNull?: true
): ReturnType<C>[]
/**
* The bfs function performs a breadth-first search traversal on a binary tree, executing a callback
* function on each node.
@ -989,19 +1093,21 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
* will not be performed and an empty array will be returned.
* @param iterationType - The `iterationType` parameter determines the type of iteration to be used
* in the breadth-first search (BFS) algorithm. It can have two possible values:
* @param includeNull - The choice to output null values during binary tree traversal should be provided.
* @returns The function `bfs` returns an array of `ReturnType<BTNCallback<N>>[]`.
*/
bfs<C extends BTNCallback<N>>(
bfs<C extends BTNCallback<N | null>>(
callback: C = this.defaultOneParamCallback as C,
beginRoot: N | null = this.root,
iterationType = this.iterationType
iterationType = this.iterationType,
includeNull = false
): ReturnType<C>[] {
if (!beginRoot) return [];
const ans: ReturnType<BTNCallback<N>>[] = [];
if (iterationType === IterationType.RECURSIVE) {
const queue = new Queue<N>([beginRoot]);
const queue: Queue<N | null> = new Queue<N | null>([beginRoot]);
const traverse = (level: number) => {
if (queue.size === 0) return;
@ -1009,15 +1115,21 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
const current = queue.shift()!;
ans.push(callback(current));
if (current.left) queue.push(current.left);
if (current.right) queue.push(current.right);
if (includeNull) {
if (current && current.left !== undefined) queue.push(current.left);
if (current && current.right !== undefined) queue.push(current.right);
} else {
if (current.left) queue.push(current.left);
if (current.right) queue.push(current.right);
}
traverse(level + 1);
};
traverse(0);
} else {
const queue = new Queue<N>([beginRoot]);
const queue = new Queue<N | null>([beginRoot]);
while (queue.size > 0) {
const levelSize = queue.size;
@ -1025,14 +1137,41 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
const current = queue.shift()!;
ans.push(callback(current));
if (current.left) queue.push(current.left);
if (current.right) queue.push(current.right);
if (includeNull) {
if (current !== null && current.left !== undefined) queue.push(current.left);
if (current !== null && current.right !== undefined) queue.push(current.right);
} else {
if (current.left) queue.push(current.left);
if (current.right) queue.push(current.right);
}
}
}
}
return ans;
}
listLevels<C extends BTNCallback<N>>(
callback?: C ,
beginRoot?: N | null ,
iterationType?: IterationType,
includeNull?: false
): ReturnType<C>[][]
listLevels<C extends BTNCallback<N>>(
callback?: C ,
beginRoot?: N | null ,
iterationType?: IterationType,
includeNull?: undefined
): ReturnType<C>[][]
listLevels<C extends BTNCallback<N | null>>(
callback?: C ,
beginRoot?: N | null ,
iterationType?: IterationType,
includeNull?: true
): ReturnType<C>[][]
/**
* The `listLevels` function takes a binary tree node and a callback function, and returns an array
* of arrays representing the levels of the tree.
@ -1044,29 +1183,36 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
* from the root node of the binary tree.
* @param iterationType - The `iterationType` parameter determines whether the tree traversal is done
* recursively or iteratively. It can have two possible values:
* @param includeNull - The choice to output null values during binary tree traversal should be provided.
* @returns The function `listLevels` returns an array of arrays, where each inner array represents a
* level in a binary tree. Each inner array contains the return type of the provided callback
* function `C` applied to the nodes at that level.
*/
listLevels<C extends BTNCallback<N>>(
listLevels<C extends BTNCallback<N | null>>(
callback: C = this.defaultOneParamCallback as C,
beginRoot: N | null = this.root,
iterationType = this.iterationType
iterationType = this.iterationType,
includeNull = false
): ReturnType<C>[][] {
if (!beginRoot) return [];
const levelsNodes: ReturnType<C>[][] = [];
if (iterationType === IterationType.RECURSIVE) {
const _recursive = (node: N, level: number) => {
const _recursive = (node: N | null, level: number) => {
if (!levelsNodes[level]) levelsNodes[level] = [];
levelsNodes[level].push(callback(node));
if (node.left) _recursive(node.left, level + 1);
if (node.right) _recursive(node.right, level + 1);
if (includeNull) {
if (node && node.left !== undefined) _recursive(node.left, level + 1);
if (node && node.right !== undefined) _recursive(node.right, level + 1);
} else {
if (node && node.left) _recursive(node.left, level + 1);
if (node && node.right) _recursive(node.right, level + 1);
}
};
_recursive(beginRoot, 0);
} else {
const stack: [N, number][] = [[beginRoot, 0]];
const stack: [N | null, number][] = [[beginRoot, 0]];
while (stack.length > 0) {
const head = stack.pop()!;
@ -1074,8 +1220,14 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
if (!levelsNodes[level]) levelsNodes[level] = [];
levelsNodes[level].push(callback(node));
if (node.right) stack.push([node.right, level + 1]);
if (node.left) stack.push([node.left, level + 1]);
if (includeNull) {
if (node && node.right !== undefined) stack.push([node.right, level + 1]);
if (node && node.left !== undefined) stack.push([node.left, level + 1]);
} else {
if (node && node.right) stack.push([node.right, level + 1]);
if (node && node.left) stack.push([node.left, level + 1]);
}
}
}
@ -1231,7 +1383,7 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
* @returns The `*[Symbol.iterator]` method returns a generator object that yields the keys of the
* binary tree nodes in a specific order.
*/
*[Symbol.iterator](node = this.root): Generator<BTNKey, void, undefined> {
* [Symbol.iterator](node = this.root): Generator<BTNKey, void, undefined> {
if (!node) {
return;
}

View file

@ -174,8 +174,11 @@ describe('BinaryTree', () => {
});
it('should subTreeTraverse', () => {
tree.addMany([4, 2, 6, 1, 3, 5, 7]);
expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.RECURSIVE)).toEqual([6, 5, 7]);
tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]);
expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.ITERATIVE)).toEqual([6,3, 7]);
expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.RECURSIVE)).toEqual([6,3, 7]);
expect(tree.subTreeTraverse(node => node === null? null : node.key, tree.getNode(6), IterationType.ITERATIVE, true)).toEqual([6, 3, 7, null]);
expect(tree.subTreeTraverse(node => node === null? null : node.key, tree.getNode(6), IterationType.RECURSIVE, true)).toEqual([6, 3, 7, null]);
});
it('should clear the tree', () => {
@ -266,10 +269,18 @@ describe('BinaryTree traversals', () => {
const arr = [35, 20, 40, 15, 29, null, 50, null, 16, 28, 30, 45, 55];
tree.refill(arr);
expect(tree.bfs(node => node , tree.root, IterationType.ITERATIVE, true).map(node => node === null ? null : node.key)).toEqual([35, 20, 40, 15, 29, null, 50, null, 16, 28, 30, 45, 55]);
expect(tree.bfs(node => node , tree.root, IterationType.RECURSIVE, true).map(node => node === null ? null : node.key)).toEqual([35, 20, 40, 15, 29, null, 50, null, 16, 28, 30, 45, 55]);
expect(tree.bfs(node => node , tree.root, IterationType.ITERATIVE).map(node => node === null ? null : node.key)).toEqual([35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55]);
expect(tree.bfs(node => node , tree.root, IterationType.RECURSIVE).map(node => node === null ? null : node.key)).toEqual([35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55]);
expect(tree.dfs(node => node.key, 'pre')).toEqual([35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55]);
expect(tree.dfs(node => node.key, 'pre', tree.root, IterationType.RECURSIVE)).toEqual([
35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55
]);
expect(tree.dfs(node => node, 'pre', tree.root, IterationType.ITERATIVE, true).map(node => node === null ? null : node.key)).toEqual([35, 20, 15, null, 16, 29, 28, 30, 40, null, 50, 45, 55]);
expect(tree.dfs(node => node, 'pre', tree.root, IterationType.RECURSIVE, true).map(node => node === null ? null : node.key)).toEqual([35, 20, 15, null, 16, 29, 28, 30, 40, null, 50, 45, 55]);
expect(tree.dfs(node => node.key, 'in')).toEqual([15, 16, 20, 28, 29, 30, 35, 40, 45, 50, 55]);
expect(tree.dfs(node => node.key, 'post')).toEqual([16, 15, 28, 30, 29, 20, 45, 55, 50, 40, 35]);
expect(tree.dfs(node => node.key, 'post', tree.root, IterationType.RECURSIVE)).toEqual([
@ -282,9 +293,7 @@ describe('BinaryTree traversals', () => {
35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55
]);
const levels = tree.listLevels(node => node.key);
expect(levels).toEqual([[35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55]]);
isDebug && console.log(levels);
expect(tree.listLevels(node => node.key)).toEqual([[35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55]]);
expect(tree.listLevels(node => node.key, tree.root, IterationType.RECURSIVE)).toEqual([
[35],
@ -292,9 +301,21 @@ describe('BinaryTree traversals', () => {
[15, 29, 50],
[16, 28, 30, 45, 55]
]);
isDebug && console.log(levels);
expect(tree.listLevels(node => node === null ? null :node.key, tree.root, IterationType.ITERATIVE, true)).toEqual([
[35],
[20, 40],
[15, 29, null,50],
[null, 16, 28, 30, 45, 55]
]);
expect(tree.listLevels(node => node === null ? null :node.key, tree.root, IterationType.RECURSIVE, true)).toEqual([
[35],
[20, 40],
[15, 29, null,50],
[null, 16, 28, 30, 45, 55]
]);
});
describe('BinaryTree', () => {
let tree: BinaryTree<string>;