tree-set-typed 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/.eslintrc.js +61 -0
  2. package/.prettierignore +6 -0
  3. package/.prettierrc.js +16 -0
  4. package/LICENSE +21 -0
  5. package/README.md +482 -0
  6. package/coverage/clover.xml +13 -0
  7. package/coverage/coverage-final.json +96 -0
  8. package/coverage/coverage-summary.json +60 -0
  9. package/coverage/lcov-report/base.css +403 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/favicon.png +0 -0
  12. package/coverage/lcov-report/index.html +119 -0
  13. package/coverage/lcov-report/index.ts.html +109 -0
  14. package/coverage/lcov-report/prettify.css +1 -0
  15. package/coverage/lcov-report/prettify.js +2 -0
  16. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  17. package/coverage/lcov-report/sorter.js +206 -0
  18. package/coverage/lcov.info +14 -0
  19. package/dist/cjs/index.cjs +12 -0
  20. package/dist/cjs/index.cjs.map +1 -0
  21. package/dist/cjs-legacy/index.cjs +12 -0
  22. package/dist/cjs-legacy/index.cjs.map +1 -0
  23. package/dist/esm/index.mjs +3 -0
  24. package/dist/esm/index.mjs.map +1 -0
  25. package/dist/esm-legacy/index.mjs +3 -0
  26. package/dist/esm-legacy/index.mjs.map +1 -0
  27. package/dist/types/common/index.d.ts +12 -0
  28. package/dist/types/constants/index.d.ts +4 -0
  29. package/dist/types/data-structures/base/index.d.ts +2 -0
  30. package/dist/types/data-structures/base/iterable-element-base.d.ts +219 -0
  31. package/dist/types/data-structures/base/iterable-entry-base.d.ts +150 -0
  32. package/dist/types/data-structures/base/linear-base.d.ts +335 -0
  33. package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +236 -0
  34. package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +197 -0
  35. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +440 -0
  36. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +174 -0
  37. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +807 -0
  38. package/dist/types/data-structures/binary-tree/bst.d.ts +645 -0
  39. package/dist/types/data-structures/binary-tree/index.d.ts +10 -0
  40. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +312 -0
  41. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +160 -0
  42. package/dist/types/data-structures/binary-tree/tree-counter.d.ts +243 -0
  43. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +333 -0
  44. package/dist/types/data-structures/graph/abstract-graph.d.ts +340 -0
  45. package/dist/types/data-structures/graph/directed-graph.d.ts +332 -0
  46. package/dist/types/data-structures/graph/index.d.ts +4 -0
  47. package/dist/types/data-structures/graph/map-graph.d.ts +78 -0
  48. package/dist/types/data-structures/graph/undirected-graph.d.ts +347 -0
  49. package/dist/types/data-structures/hash/hash-map.d.ts +428 -0
  50. package/dist/types/data-structures/hash/index.d.ts +1 -0
  51. package/dist/types/data-structures/heap/heap.d.ts +552 -0
  52. package/dist/types/data-structures/heap/index.d.ts +3 -0
  53. package/dist/types/data-structures/heap/max-heap.d.ts +32 -0
  54. package/dist/types/data-structures/heap/min-heap.d.ts +33 -0
  55. package/dist/types/data-structures/index.d.ts +12 -0
  56. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +437 -0
  57. package/dist/types/data-structures/linked-list/index.d.ts +3 -0
  58. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +567 -0
  59. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +27 -0
  60. package/dist/types/data-structures/matrix/index.d.ts +2 -0
  61. package/dist/types/data-structures/matrix/matrix.d.ts +168 -0
  62. package/dist/types/data-structures/matrix/navigator.d.ts +55 -0
  63. package/dist/types/data-structures/priority-queue/index.d.ts +3 -0
  64. package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +27 -0
  65. package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +26 -0
  66. package/dist/types/data-structures/priority-queue/priority-queue.d.ts +15 -0
  67. package/dist/types/data-structures/queue/deque.d.ts +459 -0
  68. package/dist/types/data-structures/queue/index.d.ts +2 -0
  69. package/dist/types/data-structures/queue/queue.d.ts +364 -0
  70. package/dist/types/data-structures/stack/index.d.ts +1 -0
  71. package/dist/types/data-structures/stack/stack.d.ts +324 -0
  72. package/dist/types/data-structures/tree/index.d.ts +1 -0
  73. package/dist/types/data-structures/tree/tree.d.ts +62 -0
  74. package/dist/types/data-structures/trie/index.d.ts +1 -0
  75. package/dist/types/data-structures/trie/trie.d.ts +412 -0
  76. package/dist/types/index.d.ts +23 -0
  77. package/dist/types/interfaces/binary-tree.d.ts +60 -0
  78. package/dist/types/interfaces/doubly-linked-list.d.ts +1 -0
  79. package/dist/types/interfaces/graph.d.ts +21 -0
  80. package/dist/types/interfaces/heap.d.ts +1 -0
  81. package/dist/types/interfaces/index.d.ts +8 -0
  82. package/dist/types/interfaces/navigator.d.ts +1 -0
  83. package/dist/types/interfaces/priority-queue.d.ts +1 -0
  84. package/dist/types/interfaces/segment-tree.d.ts +1 -0
  85. package/dist/types/interfaces/singly-linked-list.d.ts +1 -0
  86. package/dist/types/types/common.d.ts +15 -0
  87. package/dist/types/types/data-structures/base/base.d.ts +13 -0
  88. package/dist/types/types/data-structures/base/index.d.ts +1 -0
  89. package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -0
  90. package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +2 -0
  91. package/dist/types/types/data-structures/binary-tree/avl-tree.d.ts +2 -0
  92. package/dist/types/types/data-structures/binary-tree/binary-indexed-tree.d.ts +1 -0
  93. package/dist/types/types/data-structures/binary-tree/binary-tree.d.ts +29 -0
  94. package/dist/types/types/data-structures/binary-tree/bst.d.ts +12 -0
  95. package/dist/types/types/data-structures/binary-tree/index.d.ts +9 -0
  96. package/dist/types/types/data-structures/binary-tree/red-black-tree.d.ts +3 -0
  97. package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -0
  98. package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +2 -0
  99. package/dist/types/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -0
  100. package/dist/types/types/data-structures/graph/abstract-graph.d.ts +14 -0
  101. package/dist/types/types/data-structures/graph/directed-graph.d.ts +1 -0
  102. package/dist/types/types/data-structures/graph/index.d.ts +3 -0
  103. package/dist/types/types/data-structures/graph/map-graph.d.ts +1 -0
  104. package/dist/types/types/data-structures/graph/undirected-graph.d.ts +1 -0
  105. package/dist/types/types/data-structures/hash/hash-map.d.ts +19 -0
  106. package/dist/types/types/data-structures/hash/index.d.ts +2 -0
  107. package/dist/types/types/data-structures/heap/heap.d.ts +5 -0
  108. package/dist/types/types/data-structures/heap/index.d.ts +1 -0
  109. package/dist/types/types/data-structures/heap/max-heap.d.ts +1 -0
  110. package/dist/types/types/data-structures/heap/min-heap.d.ts +1 -0
  111. package/dist/types/types/data-structures/index.d.ts +12 -0
  112. package/dist/types/types/data-structures/linked-list/doubly-linked-list.d.ts +2 -0
  113. package/dist/types/types/data-structures/linked-list/index.d.ts +3 -0
  114. package/dist/types/types/data-structures/linked-list/singly-linked-list.d.ts +2 -0
  115. package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +4 -0
  116. package/dist/types/types/data-structures/matrix/index.d.ts +2 -0
  117. package/dist/types/types/data-structures/matrix/matrix.d.ts +7 -0
  118. package/dist/types/types/data-structures/matrix/navigator.d.ts +14 -0
  119. package/dist/types/types/data-structures/priority-queue/index.d.ts +3 -0
  120. package/dist/types/types/data-structures/priority-queue/max-priority-queue.d.ts +1 -0
  121. package/dist/types/types/data-structures/priority-queue/min-priority-queue.d.ts +1 -0
  122. package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +2 -0
  123. package/dist/types/types/data-structures/queue/deque.d.ts +4 -0
  124. package/dist/types/types/data-structures/queue/index.d.ts +2 -0
  125. package/dist/types/types/data-structures/queue/queue.d.ts +4 -0
  126. package/dist/types/types/data-structures/stack/index.d.ts +1 -0
  127. package/dist/types/types/data-structures/stack/stack.d.ts +2 -0
  128. package/dist/types/types/data-structures/tree/index.d.ts +1 -0
  129. package/dist/types/types/data-structures/tree/tree.d.ts +1 -0
  130. package/dist/types/types/data-structures/trie/index.d.ts +1 -0
  131. package/dist/types/types/data-structures/trie/trie.d.ts +4 -0
  132. package/dist/types/types/index.d.ts +3 -0
  133. package/dist/types/types/utils/index.d.ts +2 -0
  134. package/dist/types/types/utils/utils.d.ts +22 -0
  135. package/dist/types/types/utils/validate-type.d.ts +19 -0
  136. package/dist/types/utils/index.d.ts +2 -0
  137. package/dist/types/utils/number.d.ts +14 -0
  138. package/dist/types/utils/utils.d.ts +209 -0
  139. package/dist/umd/red-black-tree-typed.js +14578 -0
  140. package/dist/umd/red-black-tree-typed.js.map +1 -0
  141. package/dist/umd/red-black-tree-typed.min.js +44 -0
  142. package/dist/umd/red-black-tree-typed.min.js.map +1 -0
  143. package/docs/.nojekyll +1 -0
  144. package/docs/assets/highlight.css +92 -0
  145. package/docs/assets/main.js +59 -0
  146. package/docs/assets/navigation.js +1 -0
  147. package/docs/assets/search.js +1 -0
  148. package/docs/assets/style.css +1383 -0
  149. package/docs/classes/AVLTree.html +2046 -0
  150. package/docs/classes/AVLTreeNode.html +263 -0
  151. package/docs/index.html +523 -0
  152. package/docs/modules.html +45 -0
  153. package/jest.config.js +8 -0
  154. package/package.json +113 -0
  155. package/src/common/index.ts +23 -0
  156. package/src/constants/index.ts +4 -0
  157. package/src/data-structures/base/index.ts +2 -0
  158. package/src/data-structures/base/iterable-element-base.ts +352 -0
  159. package/src/data-structures/base/iterable-entry-base.ts +246 -0
  160. package/src/data-structures/base/linear-base.ts +643 -0
  161. package/src/data-structures/binary-tree/avl-tree-counter.ts +539 -0
  162. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +438 -0
  163. package/src/data-structures/binary-tree/avl-tree.ts +840 -0
  164. package/src/data-structures/binary-tree/binary-indexed-tree.ts +331 -0
  165. package/src/data-structures/binary-tree/binary-tree.ts +2492 -0
  166. package/src/data-structures/binary-tree/bst.ts +2024 -0
  167. package/src/data-structures/binary-tree/index.ts +10 -0
  168. package/src/data-structures/binary-tree/red-black-tree.ts +767 -0
  169. package/src/data-structures/binary-tree/segment-tree.ts +324 -0
  170. package/src/data-structures/binary-tree/tree-counter.ts +575 -0
  171. package/src/data-structures/binary-tree/tree-multi-map.ts +549 -0
  172. package/src/data-structures/graph/abstract-graph.ts +1081 -0
  173. package/src/data-structures/graph/directed-graph.ts +715 -0
  174. package/src/data-structures/graph/index.ts +4 -0
  175. package/src/data-structures/graph/map-graph.ts +132 -0
  176. package/src/data-structures/graph/undirected-graph.ts +626 -0
  177. package/src/data-structures/hash/hash-map.ts +813 -0
  178. package/src/data-structures/hash/index.ts +1 -0
  179. package/src/data-structures/heap/heap.ts +1020 -0
  180. package/src/data-structures/heap/index.ts +3 -0
  181. package/src/data-structures/heap/max-heap.ts +47 -0
  182. package/src/data-structures/heap/min-heap.ts +36 -0
  183. package/src/data-structures/index.ts +12 -0
  184. package/src/data-structures/linked-list/doubly-linked-list.ts +876 -0
  185. package/src/data-structures/linked-list/index.ts +3 -0
  186. package/src/data-structures/linked-list/singly-linked-list.ts +1050 -0
  187. package/src/data-structures/linked-list/skip-linked-list.ts +173 -0
  188. package/src/data-structures/matrix/index.ts +2 -0
  189. package/src/data-structures/matrix/matrix.ts +491 -0
  190. package/src/data-structures/matrix/navigator.ts +124 -0
  191. package/src/data-structures/priority-queue/index.ts +3 -0
  192. package/src/data-structures/priority-queue/max-priority-queue.ts +42 -0
  193. package/src/data-structures/priority-queue/min-priority-queue.ts +29 -0
  194. package/src/data-structures/priority-queue/priority-queue.ts +19 -0
  195. package/src/data-structures/queue/deque.ts +1001 -0
  196. package/src/data-structures/queue/index.ts +2 -0
  197. package/src/data-structures/queue/queue.ts +592 -0
  198. package/src/data-structures/stack/index.ts +1 -0
  199. package/src/data-structures/stack/stack.ts +469 -0
  200. package/src/data-structures/tree/index.ts +1 -0
  201. package/src/data-structures/tree/tree.ts +115 -0
  202. package/src/data-structures/trie/index.ts +1 -0
  203. package/src/data-structures/trie/trie.ts +756 -0
  204. package/src/index.ts +24 -0
  205. package/src/interfaces/binary-tree.ts +252 -0
  206. package/src/interfaces/doubly-linked-list.ts +1 -0
  207. package/src/interfaces/graph.ts +44 -0
  208. package/src/interfaces/heap.ts +1 -0
  209. package/src/interfaces/index.ts +8 -0
  210. package/src/interfaces/navigator.ts +1 -0
  211. package/src/interfaces/priority-queue.ts +1 -0
  212. package/src/interfaces/segment-tree.ts +1 -0
  213. package/src/interfaces/singly-linked-list.ts +1 -0
  214. package/src/types/common.ts +25 -0
  215. package/src/types/data-structures/base/base.ts +34 -0
  216. package/src/types/data-structures/base/index.ts +1 -0
  217. package/src/types/data-structures/binary-tree/avl-tree-counter.ts +3 -0
  218. package/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +3 -0
  219. package/src/types/data-structures/binary-tree/avl-tree.ts +3 -0
  220. package/src/types/data-structures/binary-tree/binary-indexed-tree.ts +1 -0
  221. package/src/types/data-structures/binary-tree/binary-tree.ts +31 -0
  222. package/src/types/data-structures/binary-tree/bst.ts +19 -0
  223. package/src/types/data-structures/binary-tree/index.ts +9 -0
  224. package/src/types/data-structures/binary-tree/red-black-tree.ts +5 -0
  225. package/src/types/data-structures/binary-tree/segment-tree.ts +1 -0
  226. package/src/types/data-structures/binary-tree/tree-counter.ts +3 -0
  227. package/src/types/data-structures/binary-tree/tree-multi-map.ts +3 -0
  228. package/src/types/data-structures/graph/abstract-graph.ts +18 -0
  229. package/src/types/data-structures/graph/directed-graph.ts +2 -0
  230. package/src/types/data-structures/graph/index.ts +3 -0
  231. package/src/types/data-structures/graph/map-graph.ts +1 -0
  232. package/src/types/data-structures/graph/undirected-graph.ts +1 -0
  233. package/src/types/data-structures/hash/hash-map.ts +19 -0
  234. package/src/types/data-structures/hash/index.ts +3 -0
  235. package/src/types/data-structures/heap/heap.ts +6 -0
  236. package/src/types/data-structures/heap/index.ts +1 -0
  237. package/src/types/data-structures/heap/max-heap.ts +1 -0
  238. package/src/types/data-structures/heap/min-heap.ts +1 -0
  239. package/src/types/data-structures/index.ts +12 -0
  240. package/src/types/data-structures/linked-list/doubly-linked-list.ts +3 -0
  241. package/src/types/data-structures/linked-list/index.ts +3 -0
  242. package/src/types/data-structures/linked-list/singly-linked-list.ts +3 -0
  243. package/src/types/data-structures/linked-list/skip-linked-list.ts +1 -0
  244. package/src/types/data-structures/matrix/index.ts +2 -0
  245. package/src/types/data-structures/matrix/matrix.ts +7 -0
  246. package/src/types/data-structures/matrix/navigator.ts +14 -0
  247. package/src/types/data-structures/priority-queue/index.ts +3 -0
  248. package/src/types/data-structures/priority-queue/max-priority-queue.ts +1 -0
  249. package/src/types/data-structures/priority-queue/min-priority-queue.ts +1 -0
  250. package/src/types/data-structures/priority-queue/priority-queue.ts +3 -0
  251. package/src/types/data-structures/queue/deque.ts +5 -0
  252. package/src/types/data-structures/queue/index.ts +2 -0
  253. package/src/types/data-structures/queue/queue.ts +5 -0
  254. package/src/types/data-structures/stack/index.ts +1 -0
  255. package/src/types/data-structures/stack/stack.ts +3 -0
  256. package/src/types/data-structures/tree/index.ts +1 -0
  257. package/src/types/data-structures/tree/tree.ts +1 -0
  258. package/src/types/data-structures/trie/index.ts +1 -0
  259. package/src/types/data-structures/trie/trie.ts +3 -0
  260. package/src/types/index.ts +3 -0
  261. package/src/types/utils/index.ts +2 -0
  262. package/src/types/utils/utils.ts +33 -0
  263. package/src/types/utils/validate-type.ts +35 -0
  264. package/src/utils/index.ts +2 -0
  265. package/src/utils/number.ts +22 -0
  266. package/src/utils/utils.ts +350 -0
  267. package/test/index.test.ts +111 -0
  268. package/tsconfig.base.json +23 -0
  269. package/tsconfig.json +12 -0
  270. package/tsconfig.test.json +8 -0
  271. package/tsconfig.types.json +15 -0
  272. package/tsup.config.js +28 -0
  273. package/tsup.node.config.js +71 -0
@@ -0,0 +1,2492 @@
1
+ /**
2
+ * data-structure-typed
3
+ *
4
+ * @author Pablo Zeng
5
+ * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
+ * @license MIT License
7
+ */
8
+
9
+ import type {
10
+ BinaryTreeDeleteResult,
11
+ BinaryTreeOptions,
12
+ BinaryTreePrintOptions,
13
+ BTNEntry,
14
+ DFSOrderPattern,
15
+ DFSStackItem,
16
+ EntryCallback,
17
+ FamilyPosition,
18
+ IterationType,
19
+ NodeCallback,
20
+ NodeDisplayLayout,
21
+ NodePredicate,
22
+ OptNodeOrNull,
23
+ RBTNColor,
24
+ ToEntryFn,
25
+ Trampoline
26
+ } from '../../types';
27
+ import { IBinaryTree } from '../../interfaces';
28
+ import { isComparable, makeTrampoline, makeTrampolineThunk } from '../../utils';
29
+ import { Queue } from '../queue';
30
+ import { IterableEntryBase } from '../base';
31
+ import { DFSOperation, Range } from '../../common';
32
+
33
+ /**
34
+ * @template K - The type of the key.
35
+ * @template V - The type of the value.
36
+ */
37
+ export class BinaryTreeNode<K = any, V = any> {
38
+ key: K;
39
+ value?: V;
40
+ parent?: BinaryTreeNode<K, V> = undefined;
41
+
42
+ /**
43
+ * Creates an instance of BinaryTreeNode.
44
+ * @remarks Time O(1), Space O(1)
45
+ *
46
+ * @param key - The key of the node.
47
+ * @param [value] - The value associated with the key.
48
+ */
49
+ constructor(key: K, value?: V) {
50
+ this.key = key;
51
+ this.value = value;
52
+ }
53
+
54
+ _left?: BinaryTreeNode<K, V> | null | undefined = undefined;
55
+
56
+ /**
57
+ * Gets the left child of the node.
58
+ * @remarks Time O(1), Space O(1)
59
+ *
60
+ * @returns The left child.
61
+ */
62
+ get left(): BinaryTreeNode<K, V> | null | undefined {
63
+ return this._left;
64
+ }
65
+
66
+ /**
67
+ * Sets the left child of the node and updates its parent reference.
68
+ * @remarks Time O(1), Space O(1)
69
+ *
70
+ * @param v - The node to set as the left child.
71
+ */
72
+ set left(v: BinaryTreeNode<K, V> | null | undefined) {
73
+ if (v) {
74
+ v.parent = this;
75
+ }
76
+ this._left = v;
77
+ }
78
+
79
+ _right?: BinaryTreeNode<K, V> | null | undefined = undefined;
80
+
81
+ /**
82
+ * Gets the right child of the node.
83
+ * @remarks Time O(1), Space O(1)
84
+ *
85
+ * @returns The right child.
86
+ */
87
+ get right(): BinaryTreeNode<K, V> | null | undefined {
88
+ return this._right;
89
+ }
90
+
91
+ /**
92
+ * Sets the right child of the node and updates its parent reference.
93
+ * @remarks Time O(1), Space O(1)
94
+ *
95
+ * @param v - The node to set as the right child.
96
+ */
97
+ set right(v: BinaryTreeNode<K, V> | null | undefined) {
98
+ if (v) {
99
+ v.parent = this;
100
+ }
101
+ this._right = v;
102
+ }
103
+
104
+ _height: number = 0;
105
+
106
+ /**
107
+ * Gets the height of the node (used in self-balancing trees).
108
+ * @remarks Time O(1), Space O(1)
109
+ *
110
+ * @returns The height.
111
+ */
112
+ get height(): number {
113
+ return this._height;
114
+ }
115
+
116
+ /**
117
+ * Sets the height of the node.
118
+ * @remarks Time O(1), Space O(1)
119
+ *
120
+ * @param value - The new height.
121
+ */
122
+ set height(value: number) {
123
+ this._height = value;
124
+ }
125
+
126
+ _color: RBTNColor = 'BLACK';
127
+
128
+ /**
129
+ * Gets the color of the node (used in Red-Black trees).
130
+ * @remarks Time O(1), Space O(1)
131
+ *
132
+ * @returns The node's color.
133
+ */
134
+ get color(): RBTNColor {
135
+ return this._color;
136
+ }
137
+
138
+ /**
139
+ * Sets the color of the node.
140
+ * @remarks Time O(1), Space O(1)
141
+ *
142
+ * @param value - The new color.
143
+ */
144
+ set color(value: RBTNColor) {
145
+ this._color = value;
146
+ }
147
+
148
+ _count: number = 1;
149
+
150
+ /**
151
+ * Gets the count of nodes in the subtree rooted at this node (used in order-statistic trees).
152
+ * @remarks Time O(1), Space O(1)
153
+ *
154
+ * @returns The subtree node count.
155
+ */
156
+ get count(): number {
157
+ return this._count;
158
+ }
159
+
160
+ /**
161
+ * Sets the count of nodes in the subtree.
162
+ * @remarks Time O(1), Space O(1)
163
+ *
164
+ * @param value - The new count.
165
+ */
166
+ set count(value: number) {
167
+ this._count = value;
168
+ }
169
+
170
+ /**
171
+ * Gets the position of the node relative to its parent.
172
+ * @remarks Time O(1), Space O(1)
173
+ *
174
+ * @returns The family position (e.g., 'ROOT', 'LEFT', 'RIGHT').
175
+ */
176
+ get familyPosition(): FamilyPosition {
177
+ if (!this.parent) {
178
+ return this.left || this.right ? 'ROOT' : 'ISOLATED';
179
+ }
180
+
181
+ if (this.parent.left === this) {
182
+ return this.left || this.right ? 'ROOT_LEFT' : 'LEFT';
183
+ } else if (this.parent.right === this) {
184
+ return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
185
+ }
186
+
187
+ return 'MAL_NODE';
188
+ }
189
+ }
190
+
191
+ /**
192
+ * A general Binary Tree implementation.
193
+ *
194
+ * @remarks
195
+ * This class implements a basic Binary Tree, not a Binary Search Tree.
196
+ * The `set` operation inserts nodes level-by-level (BFS) into the first available slot.
197
+ *
198
+ * @template K - The type of the key.
199
+ * @template V - The type of the value.
200
+ * @template R - The type of the raw data object (if using `toEntryFn`).
201
+ * 1. Two Children Maximum: Each node has at most two children.
202
+ * 2. Left and Right Children: Nodes have distinct left and right children.
203
+ * 3. Depth and Height: Depth is the number of edges from the root to a node; height is the maximum depth in the tree.
204
+ * 4. Subtrees: Each child of a node forms the root of a subtree.
205
+ * 5. Leaf Nodes: Nodes without children are leaves.
206
+ *
207
+ * @example
208
+ * // basic BinaryTree creation and insertion
209
+ * // Create a BinaryTree with entries
210
+ * const entries: [number, string][] = [
211
+ * [6, 'six'],
212
+ * [1, 'one'],
213
+ * [2, 'two'],
214
+ * [7, 'seven'],
215
+ * [5, 'five'],
216
+ * [3, 'three'],
217
+ * [4, 'four'],
218
+ * [9, 'nine'],
219
+ * [8, 'eight']
220
+ * ];
221
+ *
222
+ * const tree = new BinaryTree(entries);
223
+ *
224
+ * // Verify size
225
+ * console.log(tree.size); // 9;
226
+ *
227
+ * // Add new element
228
+ * tree.set(10, 'ten');
229
+ * console.log(tree.size); // 10;
230
+ * @example
231
+ * // BinaryTree get and has operations
232
+ * const tree = new BinaryTree(
233
+ * [
234
+ * [5, 'five'],
235
+ * [3, 'three'],
236
+ * [7, 'seven'],
237
+ * [1, 'one'],
238
+ * [4, 'four'],
239
+ * [6, 'six'],
240
+ * [8, 'eight']
241
+ * ],
242
+ * { isMapMode: false }
243
+ * );
244
+ *
245
+ * // Check if key exists
246
+ * console.log(tree.has(5)); // true;
247
+ * console.log(tree.has(10)); // false;
248
+ *
249
+ * // Get value by key
250
+ * console.log(tree.get(3)); // 'three';
251
+ * console.log(tree.get(7)); // 'seven';
252
+ * console.log(tree.get(100)); // undefined;
253
+ *
254
+ * // Get node structure
255
+ * const node = tree.getNode(5);
256
+ * console.log(node?.key); // 5;
257
+ * console.log(node?.value); // 'five';
258
+ * @example
259
+ * // BinaryTree level-order traversal
260
+ * const tree = new BinaryTree([
261
+ * [1, 'one'],
262
+ * [2, 'two'],
263
+ * [3, 'three'],
264
+ * [4, 'four'],
265
+ * [5, 'five'],
266
+ * [6, 'six'],
267
+ * [7, 'seven']
268
+ * ]);
269
+ *
270
+ * // Binary tree maintains level-order insertion
271
+ * // Complete binary tree structure
272
+ * console.log(tree.size); // 7;
273
+ *
274
+ * // Verify all keys are present
275
+ * console.log(tree.has(1)); // true;
276
+ * console.log(tree.has(4)); // true;
277
+ * console.log(tree.has(7)); // true;
278
+ *
279
+ * // Iterate through tree
280
+ * const keys: number[] = [];
281
+ * for (const [key] of tree) {
282
+ * keys.push(key);
283
+ * }
284
+ * console.log(keys.length); // 7;
285
+ * @example
286
+ * // determine loan approval using a decision tree
287
+ * // Decision tree structure
288
+ * const loanDecisionTree = new BinaryTree<string>(
289
+ * ['stableIncome', 'goodCredit', 'Rejected', 'Approved', 'Rejected'],
290
+ * { isDuplicate: true }
291
+ * );
292
+ *
293
+ * function determineLoanApproval(
294
+ * node?: BinaryTreeNode<string> | null,
295
+ * conditions?: { [key: string]: boolean }
296
+ * ): string {
297
+ * if (!node) throw new Error('Invalid node');
298
+ *
299
+ * // If it's a leaf node, return the decision result
300
+ * if (!node.left && !node.right) return node.key;
301
+ *
302
+ * // Check if a valid condition exists for the current node's key
303
+ * return conditions?.[node.key]
304
+ * ? determineLoanApproval(node.left, conditions)
305
+ * : determineLoanApproval(node.right, conditions);
306
+ * }
307
+ *
308
+ * // Test case 1: Stable income and good credit score
309
+ * console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: true })); // 'Approved';
310
+ *
311
+ * // Test case 2: Stable income but poor credit score
312
+ * console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: false })); // 'Rejected';
313
+ *
314
+ * // Test case 3: No stable income
315
+ * console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: true })); // 'Rejected';
316
+ *
317
+ * // Test case 4: No stable income and poor credit score
318
+ * console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: false })); // 'Rejected';
319
+ * @example
320
+ * // evaluate the arithmetic expression represented by the binary tree
321
+ * const expressionTree = new BinaryTree<number | string>(['+', 3, '*', null, null, 5, '-', null, null, 2, 8]);
322
+ *
323
+ * function evaluate(node?: BinaryTreeNode<number | string> | null): number {
324
+ * if (!node) return 0;
325
+ *
326
+ * if (typeof node.key === 'number') return node.key;
327
+ *
328
+ * const leftValue = evaluate(node.left); // Evaluate the left subtree
329
+ * const rightValue = evaluate(node.right); // Evaluate the right subtree
330
+ *
331
+ * // Perform the operation based on the current node's operator
332
+ * switch (node.key) {
333
+ * case '+':
334
+ * return leftValue + rightValue;
335
+ * case '-':
336
+ * return leftValue - rightValue;
337
+ * case '*':
338
+ * return leftValue * rightValue;
339
+ * case '/':
340
+ * return rightValue !== 0 ? leftValue / rightValue : 0; // Handle division by zero
341
+ * default:
342
+ * throw new Error(`Unsupported operator: ${node.key}`);
343
+ * }
344
+ * }
345
+ *
346
+ * console.log(evaluate(expressionTree.root)); // -27;
347
+ */
348
+ export class BinaryTree<K = any, V = any, R = any>
349
+ extends IterableEntryBase<K, V | undefined>
350
+ implements IBinaryTree<K, V, R>
351
+ {
352
+ iterationType: IterationType = 'ITERATIVE';
353
+
354
+ /**
355
+ * Creates an instance of BinaryTree.
356
+ * @remarks Time O(N * M), where N is the number of items in `keysNodesEntriesOrRaws` and M is the tree size at insertion time (due to O(M) `set` operation). Space O(N) for storing the nodes.
357
+ *
358
+ * @param [keysNodesEntriesOrRaws=[]] - An iterable of items to set.
359
+ * @param [options] - Configuration options for the tree.
360
+ */
361
+ constructor(
362
+ keysNodesEntriesOrRaws: Iterable<
363
+ K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
364
+ > = [],
365
+ options?: BinaryTreeOptions<K, V, R>
366
+ ) {
367
+ super();
368
+ if (options) {
369
+ const { iterationType, toEntryFn, isMapMode, isDuplicate } = options;
370
+ if (iterationType) this.iterationType = iterationType;
371
+ if (isMapMode !== undefined) this._isMapMode = isMapMode;
372
+ if (isDuplicate !== undefined) this._isDuplicate = isDuplicate;
373
+ if (typeof toEntryFn === 'function') this._toEntryFn = toEntryFn;
374
+ else if (toEntryFn) throw TypeError('toEntryFn must be a function type');
375
+ }
376
+
377
+ if (keysNodesEntriesOrRaws) this.setMany(keysNodesEntriesOrRaws);
378
+ }
379
+
380
+ protected _isMapMode = true;
381
+
382
+ /**
383
+ * Gets whether the tree is in Map mode.
384
+ * @remarks In Map mode (default), values are stored in an external Map, and nodes only hold keys. If false, values are stored directly on the nodes. Time O(1)
385
+ *
386
+ * @returns True if in Map mode, false otherwise.
387
+ */
388
+ get isMapMode() {
389
+ return this._isMapMode;
390
+ }
391
+
392
+ protected _isDuplicate = false;
393
+
394
+ /**
395
+ * Gets whether the tree allows duplicate keys.
396
+ * @remarks Time O(1)
397
+ *
398
+ * @returns True if duplicates are allowed, false otherwise.
399
+ */
400
+ get isDuplicate() {
401
+ return this._isDuplicate;
402
+ }
403
+
404
+ protected _store = new Map<K, V | undefined>();
405
+
406
+ /**
407
+ * Gets the external value store (used in Map mode).
408
+ * @remarks Time O(1)
409
+ *
410
+ * @returns The map storing key-value pairs.
411
+ */
412
+ get store() {
413
+ return this._store;
414
+ }
415
+
416
+ protected _root?: BinaryTreeNode<K, V> | null | undefined;
417
+
418
+ /**
419
+ * Gets the root node of the tree.
420
+ * @remarks Time O(1)
421
+ *
422
+ * @returns The root node.
423
+ */
424
+ get root(): BinaryTreeNode<K, V> | null | undefined {
425
+ return this._root;
426
+ }
427
+
428
+ protected _size: number = 0;
429
+
430
+ /**
431
+ * Gets the number of nodes in the tree.
432
+ * @remarks Time O(1)
433
+ *
434
+ * @returns The size of the tree.
435
+ */
436
+ get size(): number {
437
+ return this._size;
438
+ }
439
+
440
+ protected _NIL: BinaryTreeNode<K, V> = new BinaryTreeNode<K, V>(NaN as K) as unknown as BinaryTreeNode<K, V>;
441
+
442
+ /**
443
+ * Gets the sentinel NIL node (used in self-balancing trees like Red-Black Tree).
444
+ * @remarks Time O(1)
445
+ *
446
+ * @returns The NIL node.
447
+ */
448
+ get NIL(): BinaryTreeNode<K, V> {
449
+ return this._NIL;
450
+ }
451
+
452
+ protected _toEntryFn?: ToEntryFn<K, V, R>;
453
+
454
+ /**
455
+ * Gets the function used to convert raw data objects (R) into [key, value] entries.
456
+ * @remarks Time O(1)
457
+ *
458
+ * @returns The conversion function.
459
+ */
460
+ get toEntryFn() {
461
+ return this._toEntryFn;
462
+ }
463
+
464
+ /**
465
+ * (Protected) Creates a new node.
466
+ * @remarks Time O(1), Space O(1)
467
+ *
468
+ * @param key - The key for the new node.
469
+ * @param [value] - The value for the new node (used if not in Map mode).
470
+ * @returns The newly created node.
471
+ */
472
+ createNode(key: K, value?: V): BinaryTreeNode<K, V> {
473
+ return new BinaryTreeNode<K, V>(key, this._isMapMode ? undefined : value);
474
+ }
475
+
476
+ /**
477
+ * Creates a new, empty tree of the same type and configuration.
478
+ * @remarks Time O(1) (excluding options cloning), Space O(1)
479
+ *
480
+ * @param [options] - Optional overrides for the new tree's options.
481
+ * @returns A new, empty tree instance.
482
+ */
483
+ createTree(options?: Partial<BinaryTreeOptions<K, V, R>>): this {
484
+ return this._createInstance<K, V, R>(options);
485
+ }
486
+
487
+ /**
488
+ * Ensures the input is a node. If it's a key or entry, it searches for the node.
489
+ * @remarks Time O(1) if a node is passed. O(N) if a key or entry is passed (due to `getNode` performing a full search). Space O(1) if iterative search, O(H) if recursive (where H is height, O(N) worst-case).
490
+ *
491
+ * @param keyNodeOrEntry - The item to resolve to a node.
492
+ * @param [iterationType=this.iterationType] - The traversal method to use if searching.
493
+ * @returns The resolved node, or null/undefined if not found or input is null/undefined.
494
+ */
495
+ ensureNode(
496
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
497
+ iterationType: IterationType = this.iterationType
498
+ ): BinaryTreeNode<K, V> | null | undefined {
499
+ if (keyNodeOrEntry === null) return null;
500
+ if (keyNodeOrEntry === undefined) return;
501
+ if (keyNodeOrEntry === this._NIL) return;
502
+
503
+ if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry;
504
+
505
+ if (this.isEntry(keyNodeOrEntry)) {
506
+ const key = keyNodeOrEntry[0];
507
+ if (key === null) return null;
508
+ if (key === undefined) return;
509
+ return this.getNode(key, this._root, iterationType);
510
+ }
511
+
512
+ return this.getNode(keyNodeOrEntry, this._root, iterationType);
513
+ }
514
+
515
+ /**
516
+ * Checks if the given item is a `BinaryTreeNode` instance.
517
+ * @remarks Time O(1), Space O(1)
518
+ *
519
+ * @param keyNodeOrEntry - The item to check.
520
+ * @returns True if it's a node, false otherwise.
521
+ */
522
+ isNode(
523
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
524
+ ): keyNodeOrEntry is BinaryTreeNode<K, V> {
525
+ return keyNodeOrEntry instanceof BinaryTreeNode;
526
+ }
527
+
528
+ /**
529
+ * Checks if the given item is a raw data object (R) that needs conversion via `toEntryFn`.
530
+ * @remarks Time O(1), Space O(1)
531
+ *
532
+ * @param keyNodeEntryOrRaw - The item to check.
533
+ * @returns True if it's a raw object, false otherwise.
534
+ */
535
+ isRaw(
536
+ keyNodeEntryOrRaw: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
537
+ ): keyNodeEntryOrRaw is R {
538
+ return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object';
539
+ }
540
+
541
+ /**
542
+ * Checks if the given item is a "real" node (i.e., not null, undefined, or NIL).
543
+ * @remarks Time O(1), Space O(1)
544
+ *
545
+ * @param keyNodeOrEntry - The item to check.
546
+ * @returns True if it's a real node, false otherwise.
547
+ */
548
+ isRealNode(
549
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
550
+ ): keyNodeOrEntry is BinaryTreeNode<K, V> {
551
+ if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false;
552
+ return this.isNode(keyNodeOrEntry);
553
+ }
554
+
555
+ /**
556
+ * Checks if the given item is either a "real" node or null.
557
+ * @remarks Time O(1), Space O(1)
558
+ *
559
+ * @param keyNodeOrEntry - The item to check.
560
+ * @returns True if it's a real node or null, false otherwise.
561
+ */
562
+ isRealNodeOrNull(
563
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
564
+ ): keyNodeOrEntry is BinaryTreeNode<K, V> | null {
565
+ return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry);
566
+ }
567
+
568
+ /**
569
+ * Checks if the given item is the sentinel NIL node.
570
+ * @remarks Time O(1), Space O(1)
571
+ *
572
+ * @param keyNodeOrEntry - The item to check.
573
+ * @returns True if it's the NIL node, false otherwise.
574
+ */
575
+ isNIL(keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined): boolean {
576
+ return keyNodeOrEntry === this._NIL;
577
+ }
578
+
579
+ /**
580
+ * Checks if the given item is a `Range` object.
581
+ * @remarks Time O(1), Space O(1)
582
+ *
583
+ * @param keyNodeEntryOrPredicate - The item to check.
584
+ * @returns True if it's a Range, false otherwise.
585
+ */
586
+ isRange(
587
+ keyNodeEntryOrPredicate:
588
+ | K
589
+ | BinaryTreeNode<K, V>
590
+ | [K | null | undefined, V | undefined]
591
+ | null
592
+ | undefined
593
+ | NodePredicate<BinaryTreeNode<K, V>>
594
+ | Range<K>
595
+ ): keyNodeEntryOrPredicate is Range<K> {
596
+ return keyNodeEntryOrPredicate instanceof Range;
597
+ }
598
+
599
+ /**
600
+ * Checks if a node is a leaf (has no real children).
601
+ * @remarks Time O(N) if a key/entry is passed (due to `ensureNode`). O(1) if a node is passed. Space O(1) or O(H) (from `ensureNode`).
602
+ *
603
+ * @param keyNodeOrEntry - The node to check.
604
+ * @returns True if the node is a leaf, false otherwise.
605
+ */
606
+ isLeaf(keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined): boolean {
607
+ keyNodeOrEntry = this.ensureNode(keyNodeOrEntry);
608
+ if (keyNodeOrEntry === undefined) return false;
609
+ if (keyNodeOrEntry === null) return true; // A null spot is considered a leaf
610
+ return !this.isRealNode(keyNodeOrEntry.left) && !this.isRealNode(keyNodeOrEntry.right);
611
+ }
612
+
613
+ /**
614
+ * Checks if the given item is a [key, value] entry pair.
615
+ * @remarks Time O(1), Space O(1)
616
+ *
617
+ * @param keyNodeOrEntry - The item to check.
618
+ * @returns True if it's an entry, false otherwise.
619
+ */
620
+ isEntry(
621
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
622
+ ): keyNodeOrEntry is BTNEntry<K, V> {
623
+ return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2;
624
+ }
625
+
626
+ /**
627
+ * Checks if the given key is valid (comparable or null).
628
+ * @remarks Time O(1), Space O(1)
629
+ *
630
+ * @param key - The key to validate.
631
+ * @returns True if the key is valid, false otherwise.
632
+ */
633
+ isValidKey(key: any): key is K {
634
+ if (key === null) return true;
635
+ return isComparable(key);
636
+ }
637
+
638
+ /**
639
+ * Adds a new node to the tree.
640
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation adds the node at the first available position in a level-order (BFS) traversal. This is NOT a Binary Search Tree insertion. Time O(N), where N is the number of nodes. It must traverse level-by-level to find an empty slot. Space O(N) in the worst case for the BFS queue (e.g., a full last level).
641
+ *
642
+ * @param keyNodeOrEntry - The key, node, or entry to add.
643
+ * @returns True if the addition was successful, false otherwise.
644
+ */
645
+ add(
646
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
647
+ ): boolean {
648
+ return this.set(keyNodeOrEntry);
649
+ }
650
+
651
+ /**
652
+ * Adds or updates a new node to the tree.
653
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation sets the node at the first available position in a level-order (BFS) traversal. This is NOT a Binary Search Tree insertion. Time O(N), where N is the number of nodes. It must traverse level-by-level to find an empty slot. Space O(N) in the worst case for the BFS queue (e.g., a full last level).
654
+ *
655
+ * @param keyNodeOrEntry - The key, node, or entry to set or update.
656
+ * @param [value] - The value, if providing just a key.
657
+ * @returns True if the addition was successful, false otherwise.
658
+ */
659
+ set(
660
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
661
+ value?: V
662
+ ): boolean {
663
+
664
+ const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
665
+ if (newNode === undefined) return false;
666
+
667
+ if (!this._root) {
668
+ this._setRoot(newNode);
669
+ if (this._isMapMode) this._setValue(newNode?.key, newValue);
670
+ this._size = 1;
671
+ return true;
672
+ }
673
+
674
+ const queue = new Queue<BinaryTreeNode<K, V>>([this._root]);
675
+ let potentialParent: BinaryTreeNode<K, V> | undefined;
676
+ while (queue.length > 0) {
677
+ const cur = queue.shift();
678
+
679
+ if (!cur) continue;
680
+
681
+ if (!this._isDuplicate) {
682
+ if (newNode !== null && cur.key === newNode.key) {
683
+ this._replaceNode(cur, newNode);
684
+ if (this._isMapMode) this._setValue(cur.key, newValue);
685
+ return true; // Replaced existing node
686
+ }
687
+ }
688
+
689
+ if (potentialParent === undefined && (cur.left === undefined || cur.right === undefined)) {
690
+ potentialParent = cur;
691
+ }
692
+
693
+ if (cur.left !== null) {
694
+ if (cur.left) queue.push(cur.left);
695
+ }
696
+ if (cur.right !== null) {
697
+ if (cur.right) queue.push(cur.right);
698
+ }
699
+ }
700
+
701
+ if (potentialParent) {
702
+ if (potentialParent.left === undefined) {
703
+ potentialParent.left = newNode;
704
+ } else if (potentialParent.right === undefined) {
705
+ potentialParent.right = newNode;
706
+ }
707
+ if (this._isMapMode) this._setValue(newNode?.key, newValue);
708
+ this._size++;
709
+ return true;
710
+ }
711
+
712
+ return false; // Should not happen if tree is not full?
713
+ }
714
+
715
+ /**
716
+ * Adds multiple items to the tree.
717
+ * @remarks Time O(N * M), where N is the number of items to set and M is the size of the tree at insertion (due to O(M) `set` operation). Space O(M) (from `set`) + O(N) (for the `inserted` array).
718
+ *
719
+ * @param keysNodesEntriesOrRaws - An iterable of items to set.
720
+ * @param [values] - An optional parallel iterable of values.
721
+ * @returns An array of booleans indicating the success of each individual `set` operation.
722
+ */
723
+ addMany(
724
+ keysNodesEntriesOrRaws: Iterable<
725
+ K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
726
+ >
727
+ ): boolean[] {
728
+ return this.setMany(keysNodesEntriesOrRaws);
729
+ }
730
+
731
+ /**
732
+ * Adds or updates multiple items to the tree.
733
+ * @remarks Time O(N * M), where N is the number of items to set and M is the size of the tree at insertion (due to O(M) `set` operation). Space O(M) (from `set`) + O(N) (for the `inserted` array).
734
+ *
735
+ * @param keysNodesEntriesOrRaws - An iterable of items to set or update.
736
+ * @param [values] - An optional parallel iterable of values.
737
+ * @returns An array of booleans indicating the success of each individual `set` operation.
738
+ */
739
+ setMany(
740
+ keysNodesEntriesOrRaws: Iterable<
741
+ K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
742
+ >,
743
+ values?: Iterable<V | undefined>
744
+ ): boolean[] {
745
+ const inserted: boolean[] = [];
746
+
747
+ let valuesIterator: Iterator<V | undefined> | undefined;
748
+ if (values) {
749
+ valuesIterator = values[Symbol.iterator]();
750
+ }
751
+
752
+ for (let keyNodeEntryOrRaw of keysNodesEntriesOrRaws) {
753
+ let value: V | undefined | null = undefined;
754
+
755
+ if (valuesIterator) {
756
+ const valueResult = valuesIterator.next();
757
+ if (!valueResult.done) {
758
+ value = valueResult.value;
759
+ }
760
+ }
761
+ if (this.isRaw(keyNodeEntryOrRaw)) keyNodeEntryOrRaw = this._toEntryFn!(keyNodeEntryOrRaw);
762
+ inserted.push(this.set(keyNodeEntryOrRaw, value));
763
+ }
764
+
765
+ return inserted;
766
+ }
767
+
768
+ /**
769
+ * Merges another tree into this one by seting all its nodes.
770
+ * @remarks Time O(N * M), same as `setMany`, where N is the size of `anotherTree` and M is the size of this tree. Space O(M) (from `set`).
771
+ *
772
+ * @param anotherTree - The tree to merge.
773
+ */
774
+ merge(anotherTree: BinaryTree<K, V, R>) {
775
+ this.setMany(anotherTree, []);
776
+ }
777
+
778
+ /**
779
+ * Clears the tree and refills it with new items.
780
+ * @remarks Time O(N) (for `clear`) + O(N * M) (for `setMany`) = O(N * M). Space O(M) (from `setMany`).
781
+ *
782
+ * @param keysNodesEntriesOrRaws - An iterable of items to set.
783
+ * @param [values] - An optional parallel iterable of values.
784
+ */
785
+ refill(
786
+ keysNodesEntriesOrRaws: Iterable<
787
+ K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
788
+ >,
789
+ values?: Iterable<V | undefined>
790
+ ): void {
791
+ this.clear();
792
+ this.setMany(keysNodesEntriesOrRaws, values);
793
+ }
794
+
795
+ /**
796
+ * Deletes a node from the tree.
797
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation finds the node, and if it has two children, swaps it with the rightmost node of its left subtree (in-order predecessor) before deleting. Time O(N) in the worst case. O(N) to find the node (`getNode`) and O(H) (which is O(N) worst-case) to find the rightmost node. Space O(1) (if `getNode` is iterative, which it is).
798
+ *
799
+ * @param keyNodeOrEntry - The node to delete.
800
+ * @returns An array containing deletion results (for compatibility with self-balancing trees).
801
+ */
802
+ delete(
803
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
804
+ ): BinaryTreeDeleteResult<BinaryTreeNode<K, V>>[] {
805
+ const deletedResult: BinaryTreeDeleteResult<BinaryTreeNode<K, V>>[] = [];
806
+ if (!this._root) return deletedResult;
807
+
808
+ const curr = this.getNode(keyNodeOrEntry);
809
+ if (!curr) return deletedResult;
810
+
811
+ const parent: BinaryTreeNode<K, V> | undefined = curr?.parent;
812
+ let needBalanced: BinaryTreeNode<K, V> | undefined;
813
+ let orgCurrent: BinaryTreeNode<K, V> | undefined = curr;
814
+
815
+ if (!curr.left && !curr.right && !parent) {
816
+ // Deleting the root with no children
817
+ this._setRoot(undefined);
818
+ } else if (curr.left) {
819
+ // Node has a left child (or two children)
820
+ // Find the rightmost node in the left subtree
821
+ const leftSubTreeRightMost = this.getRightMost(node => node, curr.left);
822
+ if (leftSubTreeRightMost) {
823
+ const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
824
+ // Swap properties
825
+ orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
826
+ // `orgCurrent` is now the node to be physically deleted (which was the rightmost)
827
+ if (parentOfLeftSubTreeMax) {
828
+ // Unlink the rightmost node
829
+ if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost)
830
+ parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
831
+ else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
832
+ needBalanced = parentOfLeftSubTreeMax;
833
+ }
834
+ }
835
+ } else if (parent) {
836
+ // Node has no left child, but has a parent
837
+ // Promote the right child (which could be null)
838
+ const { familyPosition: fp } = curr;
839
+ if (fp === 'LEFT' || fp === 'ROOT_LEFT') {
840
+ parent.left = curr.right;
841
+ } else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') {
842
+ parent.right = curr.right;
843
+ }
844
+ needBalanced = parent;
845
+ } else {
846
+ // Deleting the root, which has no left child
847
+ // Promote the right child as the new root
848
+ this._setRoot(curr.right);
849
+ curr.right = undefined;
850
+ }
851
+
852
+ this._size = this._size - 1;
853
+
854
+ deletedResult.push({ deleted: orgCurrent, needBalanced });
855
+ if (this._isMapMode && orgCurrent) this._store.delete(orgCurrent.key);
856
+ return deletedResult;
857
+ }
858
+
859
+ search(
860
+ keyNodeEntryOrPredicate:
861
+ | K
862
+ | BinaryTreeNode<K, V>
863
+ | [K | null | undefined, V | undefined]
864
+ | null
865
+ | undefined
866
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
867
+ onlyOne?: boolean
868
+ ): (K | undefined)[];
869
+
870
+ search<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
871
+ keyNodeEntryOrPredicate:
872
+ | K
873
+ | BinaryTreeNode<K, V>
874
+ | [K | null | undefined, V | undefined]
875
+ | null
876
+ | undefined
877
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
878
+ onlyOne: boolean,
879
+ callback: C,
880
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
881
+ iterationType?: IterationType
882
+ ): ReturnType<C>[];
883
+
884
+ /**
885
+ * Searches the tree for nodes matching a predicate.
886
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Performs a full DFS (pre-order) scan of the tree. Time O(N), as it may visit every node. Space O(H) for the call stack (recursive) or explicit stack (iterative), where H is the tree height (O(N) worst-case).
887
+ *
888
+ * @template C - The type of the callback function.
889
+ * @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
890
+ * @param [onlyOne=false] - If true, stops after finding the first match.
891
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on matching nodes.
892
+ * @param [startNode=this._root] - The node to start the search from.
893
+ * @param [iterationType=this.iterationType] - Whether to use 'RECURSIVE' or 'ITERATIVE' search.
894
+ * @returns An array of results from the callback function for each matching node.
895
+ */
896
+ search<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
897
+ keyNodeEntryOrPredicate:
898
+ | K
899
+ | BinaryTreeNode<K, V>
900
+ | [K | null | undefined, V | undefined]
901
+ | null
902
+ | undefined
903
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
904
+ onlyOne = false,
905
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
906
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
907
+ iterationType: IterationType = this.iterationType
908
+ ): ReturnType<C>[] {
909
+ if (keyNodeEntryOrPredicate === undefined) return [];
910
+ if (keyNodeEntryOrPredicate === null) return [];
911
+ startNode = this.ensureNode(startNode);
912
+ if (!startNode) return [];
913
+ const predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
914
+
915
+ const ans: ReturnType<C>[] = [];
916
+
917
+ if (iterationType === 'RECURSIVE') {
918
+ const dfs = (cur: BinaryTreeNode<K, V>) => {
919
+ if (predicate(cur)) {
920
+ ans.push(callback(cur));
921
+ if (onlyOne) return;
922
+ }
923
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
924
+ if (this.isRealNode(cur.left)) dfs(cur.left);
925
+ if (this.isRealNode(cur.right)) dfs(cur.right);
926
+ };
927
+
928
+ dfs(startNode);
929
+ } else {
930
+ const stack = [startNode];
931
+
932
+ while (stack.length > 0) {
933
+ const cur = stack.pop();
934
+ if (this.isRealNode(cur)) {
935
+ if (predicate(cur)) {
936
+ ans.push(callback(cur));
937
+ if (onlyOne) return ans;
938
+ }
939
+ if (this.isRealNode(cur.left)) stack.push(cur.left);
940
+ if (this.isRealNode(cur.right)) stack.push(cur.right);
941
+ }
942
+ }
943
+ }
944
+
945
+ return ans;
946
+ }
947
+
948
+ /**
949
+ * Gets all nodes matching a predicate.
950
+ * @remarks Time O(N) (via `search`). Space O(H) or O(N) (via `search`).
951
+ *
952
+ * @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
953
+ * @param [onlyOne=false] - If true, stops after finding the first match.
954
+ * @param [startNode=this._root] - The node to start the search from.
955
+ * @param [iterationType=this.iterationType] - The traversal method.
956
+ * @returns An array of matching nodes.
957
+ */
958
+ getNodes(
959
+ keyNodeEntryOrPredicate:
960
+ | K
961
+ | BinaryTreeNode<K, V>
962
+ | [K | null | undefined, V | undefined]
963
+ | null
964
+ | undefined
965
+ | NodePredicate<BinaryTreeNode<K, V>>,
966
+ onlyOne?: boolean,
967
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
968
+ iterationType?: IterationType
969
+ ): BinaryTreeNode<K, V>[];
970
+
971
+ getNodes(
972
+ keyNodeEntryOrPredicate:
973
+ | K
974
+ | BinaryTreeNode<K, V>
975
+ | [K | null | undefined, V | undefined]
976
+ | null
977
+ | undefined
978
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
979
+ onlyOne = false,
980
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
981
+ iterationType: IterationType = this.iterationType
982
+ ): (BinaryTreeNode<K, V> | null)[] {
983
+ return this.search(keyNodeEntryOrPredicate, onlyOne, node => node, startNode, iterationType);
984
+ }
985
+
986
+ /**
987
+ * Gets the first node matching a predicate.
988
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Time O(N) in the worst case (via `search`). Space O(H) or O(N) (via `search`).
989
+ *
990
+ * @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
991
+ * @param [startNode=this._root] - The node to start the search from.
992
+ * @param [iterationType=this.iterationType] - The traversal method.
993
+ * @returns The first matching node, or undefined if not found.
994
+ */
995
+ getNode(
996
+ keyNodeEntryOrPredicate:
997
+ | K
998
+ | BinaryTreeNode<K, V>
999
+ | [K | null | undefined, V | undefined]
1000
+ | null
1001
+ | undefined
1002
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
1003
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1004
+ iterationType: IterationType = this.iterationType
1005
+ ): BinaryTreeNode<K, V> | null | undefined {
1006
+ return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType)[0];
1007
+ }
1008
+
1009
+ /**
1010
+ * Gets the value associated with a key.
1011
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Time O(1) if in Map mode. O(N) if not in Map mode (uses `getNode`). Space O(1) if in Map mode. O(H) or O(N) otherwise.
1012
+ *
1013
+ * @param keyNodeEntryOrPredicate - The key, node, or entry to get the value for.
1014
+ * @param [startNode=this._root] - The node to start searching from (if not in Map mode).
1015
+ * @param [iterationType=this.iterationType] - The traversal method (if not in Map mode).
1016
+ * @returns The associated value, or undefined.
1017
+ */
1018
+ override get(
1019
+ keyNodeEntryOrPredicate: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1020
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1021
+ iterationType: IterationType = this.iterationType
1022
+ ): V | undefined {
1023
+ if (this._isMapMode) {
1024
+ const key = this._extractKey(keyNodeEntryOrPredicate);
1025
+ if (key === null || key === undefined) return;
1026
+ return this._store.get(key);
1027
+ }
1028
+ return this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)?.value;
1029
+ }
1030
+
1031
+ /**
1032
+ * Checks if a node matching the predicate exists in the tree.
1033
+ * @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Time O(N) in the worst case (via `search`). Space O(H) or O(N) (via `search`).
1034
+ *
1035
+ * @param [keyNodeEntryOrPredicate] - The key, node, entry, or predicate to check for.
1036
+ * @param [startNode] - The node to start the search from.
1037
+ * @param [iterationType] - The traversal method.
1038
+ * @returns True if a matching node exists, false otherwise.
1039
+ */
1040
+ override has(
1041
+ keyNodeEntryOrPredicate?:
1042
+ | K
1043
+ | BinaryTreeNode<K, V>
1044
+ | [K | null | undefined, V | undefined]
1045
+ | null
1046
+ | undefined
1047
+ | NodePredicate<BinaryTreeNode<K, V>>,
1048
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1049
+ iterationType?: IterationType
1050
+ ): boolean;
1051
+
1052
+ override has(
1053
+ keyNodeEntryOrPredicate:
1054
+ | K
1055
+ | BinaryTreeNode<K, V>
1056
+ | [K | null | undefined, V | undefined]
1057
+ | null
1058
+ | undefined
1059
+ | NodePredicate<BinaryTreeNode<K, V> | null>,
1060
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1061
+ iterationType: IterationType = this.iterationType
1062
+ ): boolean {
1063
+ return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType).length > 0;
1064
+ }
1065
+
1066
+ /**
1067
+ * Clears the tree of all nodes and values.
1068
+ * @remarks Time O(N) if in Map mode (due to `_store.clear()`), O(1) otherwise. Space O(1)
1069
+ */
1070
+ clear() {
1071
+ this._clearNodes();
1072
+ if (this._isMapMode) this._clearValues();
1073
+ }
1074
+
1075
+ /**
1076
+ * Checks if the tree is empty.
1077
+ * @remarks Time O(1), Space O(1)
1078
+ *
1079
+ * @returns True if the tree has no nodes, false otherwise.
1080
+ */
1081
+ isEmpty(): boolean {
1082
+ return this._size === 0;
1083
+ }
1084
+
1085
+ /**
1086
+ * Checks if the tree is perfectly balanced.
1087
+ * @remarks A tree is perfectly balanced if the difference between min and max height is at most 1. Time O(N), as it requires two full traversals (`getMinHeight` and `getHeight`). Space O(H) or O(N) (from height calculation).
1088
+ *
1089
+ * @param [startNode=this._root] - The node to start checking from.
1090
+ * @returns True if perfectly balanced, false otherwise.
1091
+ */
1092
+ isPerfectlyBalanced(
1093
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root
1094
+ ): boolean {
1095
+ return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode);
1096
+ }
1097
+
1098
+ /**
1099
+ * Checks if the tree is a valid Binary Search Tree (BST).
1100
+ * @remarks Time O(N), as it must visit every node. Space O(H) for the call stack (recursive) or explicit stack (iterative), where H is the tree height (O(N) worst-case).
1101
+ *
1102
+ * @param [startNode=this._root] - The node to start checking from.
1103
+ * @param [iterationType=this.iterationType] - The traversal method.
1104
+ * @returns True if it's a valid BST, false otherwise.
1105
+ */
1106
+ isBST(
1107
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1108
+ iterationType: IterationType = this.iterationType
1109
+ ): boolean {
1110
+ const startNodeSired = this.ensureNode(startNode);
1111
+ if (!startNodeSired) return true;
1112
+
1113
+ if (iterationType === 'RECURSIVE') {
1114
+ const dfs = (cur: BinaryTreeNode<K, V> | null | undefined, min: number, max: number): boolean => {
1115
+ if (!this.isRealNode(cur)) return true;
1116
+ const numKey = Number(cur.key);
1117
+ if (numKey <= min || numKey >= max) return false;
1118
+ return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max);
1119
+ };
1120
+
1121
+ const isStandardBST = dfs(startNodeSired, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
1122
+ const isInverseBST = dfs(startNodeSired, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); // Check for reverse BST
1123
+ return isStandardBST || isInverseBST;
1124
+ } else {
1125
+ // Iterative in-order traversal check
1126
+ const checkBST = (checkMax = false) => {
1127
+ const stack: BinaryTreeNode<K, V>[] = [];
1128
+ let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER;
1129
+ let curr: BinaryTreeNode<K, V> | null | undefined = startNodeSired;
1130
+ while (this.isRealNode(curr) || stack.length > 0) {
1131
+ while (this.isRealNode(curr)) {
1132
+ stack.push(curr);
1133
+ curr = curr.left;
1134
+ }
1135
+ curr = stack.pop()!;
1136
+ const numKey = Number(curr.key);
1137
+ if (!this.isRealNode(curr) || (!checkMax && prev >= numKey) || (checkMax && prev <= numKey)) return false;
1138
+ prev = numKey;
1139
+ curr = curr.right;
1140
+ }
1141
+ return true;
1142
+ };
1143
+ const isStandardBST = checkBST(false);
1144
+ const isInverseBST = checkBST(true);
1145
+ return isStandardBST || isInverseBST;
1146
+ }
1147
+ }
1148
+
1149
+ /**
1150
+ * Gets the depth of a node (distance from `startNode`).
1151
+ * @remarks Time O(H), where H is the depth of the `dist` node relative to `startNode`. O(N) worst-case. Space O(1).
1152
+ *
1153
+ * @param dist - The node to find the depth of.
1154
+ * @param [startNode=this._root] - The node to measure depth from (defaults to root).
1155
+ * @returns The depth (0 if `dist` is `startNode`).
1156
+ */
1157
+ getDepth(
1158
+ dist: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1159
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root
1160
+ ): number {
1161
+ let distEnsured = this.ensureNode(dist);
1162
+ const beginRootEnsured = this.ensureNode(startNode);
1163
+ let depth = 0;
1164
+ while (distEnsured?.parent) {
1165
+ if (distEnsured === beginRootEnsured) {
1166
+ return depth;
1167
+ }
1168
+ depth++;
1169
+ distEnsured = distEnsured.parent;
1170
+ }
1171
+ return depth;
1172
+ }
1173
+
1174
+ /**
1175
+ * Gets the maximum height of the tree (longest path from startNode to a leaf).
1176
+ * @remarks Time O(N), as it must visit every node. Space O(H) for recursive stack (O(N) worst-case) or O(N) for iterative stack (storing node + depth).
1177
+ *
1178
+ * @param [startNode=this._root] - The node to start measuring from.
1179
+ * @param [iterationType=this.iterationType] - The traversal method.
1180
+ * @returns The height ( -1 for an empty tree, 0 for a single-node tree).
1181
+ */
1182
+ getHeight(
1183
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1184
+ iterationType: IterationType = this.iterationType
1185
+ ): number {
1186
+ startNode = this.ensureNode(startNode);
1187
+ if (!this.isRealNode(startNode)) return -1;
1188
+
1189
+ if (iterationType === 'RECURSIVE') {
1190
+ const _getMaxHeight = (cur: BinaryTreeNode<K, V> | null | undefined): number => {
1191
+ if (!this.isRealNode(cur)) return -1;
1192
+ const leftHeight = _getMaxHeight(cur.left);
1193
+ const rightHeight = _getMaxHeight(cur.right);
1194
+ return Math.max(leftHeight, rightHeight) + 1;
1195
+ };
1196
+
1197
+ return _getMaxHeight(startNode);
1198
+ } else {
1199
+ // Iterative (using DFS)
1200
+ const stack: { node: BinaryTreeNode<K, V>; depth: number }[] = [{ node: startNode, depth: 0 }];
1201
+ let maxHeight = 0;
1202
+
1203
+ while (stack.length > 0) {
1204
+ const { node, depth } = stack.pop()!;
1205
+
1206
+ if (this.isRealNode(node.left)) stack.push({ node: node.left, depth: depth + 1 });
1207
+ if (this.isRealNode(node.right)) stack.push({ node: node.right, depth: depth + 1 });
1208
+
1209
+ maxHeight = Math.max(maxHeight, depth);
1210
+ }
1211
+
1212
+ return maxHeight;
1213
+ }
1214
+ }
1215
+
1216
+ /**
1217
+ * Gets the minimum height of the tree (shortest path from startNode to a leaf).
1218
+ * @remarks Time O(N), as it must visit every node. Space O(H) for recursive stack (O(N) worst-case) or O(N) for iterative (due to `depths` Map).
1219
+ *
1220
+ * @param [startNode=this._root] - The node to start measuring from.
1221
+ * @param [iterationType=this.iterationType] - The traversal method.
1222
+ * @returns The minimum height (-1 for empty, 0 for single node).
1223
+ */
1224
+ getMinHeight(
1225
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1226
+ iterationType: IterationType = this.iterationType
1227
+ ): number {
1228
+ startNode = this.ensureNode(startNode);
1229
+ if (!startNode) return -1;
1230
+
1231
+ if (iterationType === 'RECURSIVE') {
1232
+ const _getMinHeight = (cur: BinaryTreeNode<K, V> | null | undefined): number => {
1233
+ if (!this.isRealNode(cur)) return 0;
1234
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0; // Leaf node
1235
+ const leftMinHeight = _getMinHeight(cur.left);
1236
+ const rightMinHeight = _getMinHeight(cur.right);
1237
+ return Math.min(leftMinHeight, rightMinHeight) + 1;
1238
+ };
1239
+
1240
+ return _getMinHeight(startNode);
1241
+ } else {
1242
+ // Iterative (using post-order DFS)
1243
+ const stack: BinaryTreeNode<K, V>[] = [];
1244
+ let node: BinaryTreeNode<K, V> | null | undefined = startNode,
1245
+ last: BinaryTreeNode<K, V> | null | undefined = null;
1246
+ const depths: Map<BinaryTreeNode<K, V>, number> = new Map();
1247
+
1248
+ while (stack.length > 0 || node) {
1249
+ if (this.isRealNode(node)) {
1250
+ stack.push(node);
1251
+ node = node.left;
1252
+ } else {
1253
+ node = stack[stack.length - 1];
1254
+ if (!this.isRealNode(node.right) || last === node.right) {
1255
+ node = stack.pop();
1256
+ if (this.isRealNode(node)) {
1257
+ const leftMinHeight = this.isRealNode(node.left) ? depths.get(node.left)! : -1;
1258
+ const rightMinHeight = this.isRealNode(node.right) ? depths.get(node.right)! : -1;
1259
+ depths.set(node, 1 + Math.min(leftMinHeight, rightMinHeight));
1260
+ last = node;
1261
+ node = null;
1262
+ }
1263
+ } else node = node.right;
1264
+ }
1265
+ }
1266
+
1267
+ return depths.get(startNode)!;
1268
+ }
1269
+ }
1270
+
1271
+ getPathToRoot(
1272
+ beginNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
1273
+ ): (K | undefined)[];
1274
+
1275
+ getPathToRoot<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1276
+ beginNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1277
+ callback: C,
1278
+ isReverse?: boolean
1279
+ ): ReturnType<C>[];
1280
+
1281
+ /**
1282
+ * Gets the path from a given node up to the root.
1283
+ * @remarks Time O(H), where H is the depth of the `beginNode`. O(N) worst-case. Space O(H) for the result array.
1284
+ *
1285
+ * @template C - The type of the callback function.
1286
+ * @param beginNode - The node to start the path from.
1287
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on each node in the path.
1288
+ * @param [isReverse=false] - If true, returns the path from root-to-node.
1289
+ * @returns An array of callback results.
1290
+ */
1291
+ getPathToRoot<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1292
+ beginNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1293
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1294
+ isReverse = false
1295
+ ): ReturnType<C>[] {
1296
+ const result: ReturnType<C>[] = [];
1297
+ let beginNodeEnsured = this.ensureNode(beginNode);
1298
+
1299
+ if (!beginNodeEnsured) return result;
1300
+
1301
+ while (beginNodeEnsured.parent) {
1302
+ result.push(callback(beginNodeEnsured));
1303
+ beginNodeEnsured = beginNodeEnsured.parent;
1304
+ }
1305
+ result.push(callback(beginNodeEnsured)); // Add the root
1306
+ return isReverse ? result.reverse() : result;
1307
+ }
1308
+
1309
+ getLeftMost(): K | undefined;
1310
+
1311
+ getLeftMost<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1312
+ callback: C,
1313
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1314
+ iterationType?: IterationType
1315
+ ): ReturnType<C>;
1316
+
1317
+ /**
1318
+ * Finds the leftmost node in a subtree (the node with the smallest key in a BST).
1319
+ * @remarks Time O(H), where H is the height of the left spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
1320
+ *
1321
+ * @template C - The type of the callback function.
1322
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the leftmost node.
1323
+ * @param [startNode=this._root] - The subtree root to search from.
1324
+ * @param [iterationType=this.iterationType] - The traversal method.
1325
+ * @returns The callback result for the leftmost node.
1326
+ */
1327
+ getLeftMost<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1328
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1329
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1330
+ iterationType: IterationType = this.iterationType
1331
+ ): ReturnType<C> {
1332
+ if (this.isNIL(startNode)) return callback(undefined);
1333
+ const ensuredStartNode = this.ensureNode(startNode);
1334
+
1335
+ if (!this.isRealNode(ensuredStartNode)) return callback(undefined);
1336
+ if (iterationType === 'RECURSIVE') {
1337
+ const dfs = (cur: BinaryTreeNode<K, V>): BinaryTreeNode<K, V> => {
1338
+ const { left } = cur;
1339
+ if (!this.isRealNode(left)) return cur;
1340
+ return dfs(left);
1341
+ };
1342
+
1343
+ return callback(dfs(ensuredStartNode));
1344
+ } else {
1345
+ // Iterative (trampolined to prevent stack overflow, though 'ITERATIVE' usually means a loop)
1346
+ const dfs = makeTrampoline((cur: BinaryTreeNode<K, V>): Trampoline<BinaryTreeNode<K, V>> => {
1347
+ const { left } = cur;
1348
+ if (!this.isRealNode(left)) return cur;
1349
+ return makeTrampolineThunk(() => dfs(left));
1350
+ });
1351
+
1352
+ return callback(dfs(ensuredStartNode));
1353
+ }
1354
+ }
1355
+
1356
+ getRightMost(): K | undefined;
1357
+
1358
+ getRightMost<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1359
+ callback: C,
1360
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1361
+ iterationType?: IterationType
1362
+ ): ReturnType<C>;
1363
+
1364
+ /**
1365
+ * Finds the rightmost node in a subtree (the node with the largest key in a BST).
1366
+ * @remarks Time O(H), where H is the height of the right spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
1367
+ *
1368
+ * @template C - The type of the callback function.
1369
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the rightmost node.
1370
+ * @param [startNode=this._root] - The subtree root to search from.
1371
+ * @param [iterationType=this.iterationType] - The traversal method.
1372
+ * @returns The callback result for the rightmost node.
1373
+ */
1374
+ getRightMost<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1375
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1376
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1377
+ iterationType: IterationType = this.iterationType
1378
+ ): ReturnType<C> {
1379
+ if (this.isNIL(startNode)) return callback(undefined);
1380
+ startNode = this.ensureNode(startNode);
1381
+ if (!startNode) return callback(undefined);
1382
+
1383
+ if (iterationType === 'RECURSIVE') {
1384
+ const dfs = (cur: BinaryTreeNode<K, V>): BinaryTreeNode<K, V> => {
1385
+ const { right } = cur;
1386
+ if (!this.isRealNode(right)) return cur;
1387
+ return dfs(right);
1388
+ };
1389
+
1390
+ return callback(dfs(startNode));
1391
+ } else {
1392
+ const dfs = makeTrampoline((cur: BinaryTreeNode<K, V>): Trampoline<BinaryTreeNode<K, V>> => {
1393
+ const { right } = cur;
1394
+ if (!this.isRealNode(right)) return cur;
1395
+ return makeTrampolineThunk(() => dfs(right));
1396
+ });
1397
+
1398
+ return callback(dfs(startNode));
1399
+ }
1400
+ }
1401
+
1402
+ /**
1403
+ * Gets the Morris traversal predecessor (rightmost node in the left subtree, or node itself).
1404
+ * @remarks This is primarily a helper for Morris traversal. Time O(H), where H is the height of the left subtree. O(N) worst-case. Space O(1).
1405
+ *
1406
+ * @param node - The node to find the predecessor for.
1407
+ * @returns The Morris predecessor.
1408
+ */
1409
+ getPredecessor(node: BinaryTreeNode<K, V>): BinaryTreeNode<K, V> {
1410
+ if (this.isRealNode(node.left)) {
1411
+ let predecessor: BinaryTreeNode<K, V> | null | undefined = node.left;
1412
+ while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) {
1413
+ if (this.isRealNode(predecessor)) {
1414
+ predecessor = predecessor.right;
1415
+ }
1416
+ }
1417
+ return predecessor;
1418
+ } else {
1419
+ return node;
1420
+ }
1421
+ }
1422
+
1423
+ /**
1424
+ * Gets the in-order successor of a node in a BST.
1425
+ * @remarks Time O(H), where H is the tree height. O(N) worst-case. Space O(H) (due to `getLeftMost` stack).
1426
+ *
1427
+ * @param [x] - The node to find the successor of.
1428
+ * @returns The successor node, or null/undefined if none exists.
1429
+ */
1430
+ getSuccessor(x?: K | BinaryTreeNode<K, V> | null): BinaryTreeNode<K, V> | null | undefined {
1431
+ x = this.ensureNode(x);
1432
+ if (!this.isRealNode(x)) return undefined;
1433
+
1434
+ if (this.isRealNode(x.right)) {
1435
+ return this.getLeftMost(node => node, x.right);
1436
+ }
1437
+
1438
+ let y: BinaryTreeNode<K, V> | null | undefined = x.parent;
1439
+ while (this.isRealNode(y) && x === y.right) {
1440
+ x = y;
1441
+ y = y.parent;
1442
+ }
1443
+ return y;
1444
+ }
1445
+
1446
+ dfs(): (K | undefined)[];
1447
+
1448
+ dfs<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1449
+ callback?: C,
1450
+ pattern?: DFSOrderPattern,
1451
+ onlyOne?: boolean,
1452
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1453
+ iterationType?: IterationType
1454
+ ): ReturnType<C>[];
1455
+
1456
+ dfs<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1457
+ callback?: C,
1458
+ pattern?: DFSOrderPattern,
1459
+ onlyOne?: boolean,
1460
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1461
+ iterationType?: IterationType,
1462
+ includeNull?: boolean
1463
+ ): ReturnType<C>[];
1464
+
1465
+ /**
1466
+ * Performs a Depth-First Search (DFS) traversal.
1467
+ * @remarks Time O(N), visits every node. Space O(H) for the call/explicit stack. O(N) worst-case.
1468
+ *
1469
+ * @template C - The type of the callback function.
1470
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
1471
+ * @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
1472
+ * @param [onlyOne=false] - If true, stops after the first callback.
1473
+ * @param [startNode=this._root] - The node to start from.
1474
+ * @param [iterationType=this.iterationType] - The traversal method.
1475
+ * @param [includeNull=false] - If true, includes null nodes in the traversal.
1476
+ * @returns An array of callback results.
1477
+ */
1478
+ dfs<C extends NodeCallback<BinaryTreeNode<K, V> | undefined>>(
1479
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1480
+ pattern: DFSOrderPattern = 'IN',
1481
+ onlyOne: boolean = false,
1482
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1483
+ iterationType: IterationType = this.iterationType,
1484
+ includeNull = false
1485
+ ): ReturnType<C>[] {
1486
+ startNode = this.ensureNode(startNode);
1487
+ if (!startNode) return [];
1488
+ return this._dfs(callback, pattern, onlyOne, startNode, iterationType, includeNull);
1489
+ }
1490
+
1491
+ bfs(): (K | undefined)[];
1492
+
1493
+ bfs<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1494
+ callback?: C,
1495
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1496
+ iterationType?: IterationType,
1497
+ includeNull?: false
1498
+ ): ReturnType<C>[];
1499
+
1500
+ bfs<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1501
+ callback?: C,
1502
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1503
+ iterationType?: IterationType,
1504
+ includeNull?: true
1505
+ ): ReturnType<C>[];
1506
+
1507
+ /**
1508
+ * Performs a Breadth-First Search (BFS) or Level-Order traversal.
1509
+ * @remarks Time O(N), visits every node. Space O(N) in the worst case for the queue (e.g., a full last level).
1510
+ *
1511
+ * @template C - The type of the callback function.
1512
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
1513
+ * @param [startNode=this._root] - The node to start from.
1514
+ * @param [iterationType=this.iterationType] - The traversal method ('RECURSIVE' BFS is less common but supported here).
1515
+ * @param [includeNull=false] - If true, includes null nodes in the traversal.
1516
+ * @returns An array of callback results.
1517
+ */
1518
+ bfs<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1519
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1520
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1521
+ iterationType: IterationType = this.iterationType,
1522
+ includeNull = false
1523
+ ): ReturnType<C>[] {
1524
+ startNode = this.ensureNode(startNode);
1525
+ if (!startNode) return [];
1526
+
1527
+ const ans: ReturnType<NodeCallback<BinaryTreeNode<K, V> | null>>[] = [];
1528
+
1529
+ if (iterationType === 'RECURSIVE') {
1530
+ // This is a "recursive" BFS, which is atypical. It uses a queue but calls itself.
1531
+ const queue: Queue<OptNodeOrNull<BinaryTreeNode<K, V>>> = new Queue<OptNodeOrNull<BinaryTreeNode<K, V>>>([
1532
+ startNode
1533
+ ]);
1534
+
1535
+ const dfs = (level: number) => {
1536
+ if (queue.length === 0) return;
1537
+
1538
+ const current = queue.shift()!;
1539
+ ans.push(callback(current));
1540
+
1541
+ if (includeNull) {
1542
+ if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
1543
+ if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
1544
+ } else {
1545
+ if (this.isRealNode(current.left)) queue.push(current.left);
1546
+ if (this.isRealNode(current.right)) queue.push(current.right);
1547
+ }
1548
+
1549
+ dfs(level + 1);
1550
+ };
1551
+
1552
+ dfs(0);
1553
+ } else {
1554
+ // Standard iterative BFS
1555
+ const queue = new Queue<OptNodeOrNull<BinaryTreeNode<K, V>>>([startNode]);
1556
+ while (queue.length > 0) {
1557
+ const levelSize = queue.length; // Not strictly needed here, but good for level-by-level
1558
+ for (let i = 0; i < levelSize; i++) {
1559
+ const current = queue.shift()!;
1560
+ ans.push(callback(current));
1561
+
1562
+ if (includeNull) {
1563
+ if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
1564
+ if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
1565
+ } else {
1566
+ if (this.isRealNode(current.left)) queue.push(current.left);
1567
+ if (this.isRealNode(current.right)) queue.push(current.right);
1568
+ }
1569
+ }
1570
+ }
1571
+ }
1572
+ return ans;
1573
+ }
1574
+
1575
+ leaves(): (K | undefined)[];
1576
+
1577
+ leaves<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1578
+ callback: C,
1579
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1580
+ iterationType?: IterationType
1581
+ ): ReturnType<C>[];
1582
+
1583
+ /**
1584
+ * Finds all leaf nodes in the tree.
1585
+ * @remarks Time O(N), visits every node. Space O(H) for recursive stack or O(N) for iterative queue.
1586
+ *
1587
+ * @template C - The type of the callback function.
1588
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each leaf node.
1589
+ * @param [startNode=this._root] - The node to start from.
1590
+ * @param [iterationType=this.iterationType] - The traversal method.
1591
+ * @returns An array of callback results.
1592
+ */
1593
+ leaves<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1594
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1595
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1596
+ iterationType: IterationType = this.iterationType
1597
+ ): ReturnType<C>[] {
1598
+ startNode = this.ensureNode(startNode);
1599
+ const leaves: ReturnType<NodeCallback<BinaryTreeNode<K, V> | null>>[] = [];
1600
+
1601
+ if (!this.isRealNode(startNode)) return [];
1602
+
1603
+ if (iterationType === 'RECURSIVE') {
1604
+ // DFS-based
1605
+ const dfs = (cur: BinaryTreeNode<K, V>) => {
1606
+ if (this.isLeaf(cur)) {
1607
+ leaves.push(callback(cur));
1608
+ }
1609
+ if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
1610
+ if (this.isRealNode(cur.left)) dfs(cur.left);
1611
+ if (this.isRealNode(cur.right)) dfs(cur.right);
1612
+ };
1613
+
1614
+ dfs(startNode);
1615
+ } else {
1616
+ // BFS-based
1617
+ const queue = new Queue([startNode]);
1618
+
1619
+ while (queue.length > 0) {
1620
+ const cur = queue.shift();
1621
+ if (this.isRealNode(cur)) {
1622
+ if (this.isLeaf(cur)) {
1623
+ leaves.push(callback(cur));
1624
+ }
1625
+ if (this.isRealNode(cur.left)) queue.push(cur.left);
1626
+ if (this.isRealNode(cur.right)) queue.push(cur.right);
1627
+ }
1628
+ }
1629
+ }
1630
+
1631
+ return leaves;
1632
+ }
1633
+
1634
+ listLevels(): (K | undefined)[][];
1635
+
1636
+ listLevels<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1637
+ callback?: C,
1638
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1639
+ iterationType?: IterationType,
1640
+ includeNull?: false
1641
+ ): ReturnType<C>[][];
1642
+
1643
+ listLevels<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1644
+ callback?: C,
1645
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1646
+ iterationType?: IterationType,
1647
+ includeNull?: true
1648
+ ): ReturnType<C>[][];
1649
+
1650
+ /**
1651
+ * Returns a 2D array of nodes, grouped by level.
1652
+ * @remarks Time O(N), visits every node. Space O(N) for the result array and the queue/stack.
1653
+ *
1654
+ * @template C - The type of the callback function.
1655
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
1656
+ * @param [startNode=this._root] - The node to start from.
1657
+ * @param [iterationType=this.iterationType] - The traversal method.
1658
+ * @param [includeNull=false] - If true, includes null nodes.
1659
+ * @returns A 2D array of callback results.
1660
+ */
1661
+ listLevels<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1662
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1663
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1664
+ iterationType: IterationType = this.iterationType,
1665
+ includeNull = false
1666
+ ): ReturnType<C>[][] {
1667
+ startNode = this.ensureNode(startNode);
1668
+ const levelsNodes: ReturnType<C>[][] = [];
1669
+
1670
+ if (!startNode) return levelsNodes;
1671
+
1672
+ if (iterationType === 'RECURSIVE') {
1673
+ // Pre-order DFS based level listing
1674
+ const _recursive = (node: BinaryTreeNode<K, V> | null, level: number) => {
1675
+ if (!levelsNodes[level]) levelsNodes[level] = [];
1676
+ levelsNodes[level].push(callback(node));
1677
+ if (includeNull) {
1678
+ if (node && this.isRealNodeOrNull(node.left)) _recursive(node.left, level + 1);
1679
+ if (node && this.isRealNodeOrNull(node.right)) _recursive(node.right, level + 1);
1680
+ } else {
1681
+ if (node && node.left) _recursive(node.left, level + 1);
1682
+ if (node && node.right) _recursive(node.right, level + 1);
1683
+ }
1684
+ };
1685
+
1686
+ _recursive(startNode, 0);
1687
+ } else {
1688
+ // Iterative DFS based level listing
1689
+ const stack: [BinaryTreeNode<K, V> | null, number][] = [[startNode, 0]];
1690
+
1691
+ while (stack.length > 0) {
1692
+ const head = stack.pop()!;
1693
+ const [node, level] = head;
1694
+
1695
+ if (!levelsNodes[level]) levelsNodes[level] = [];
1696
+ levelsNodes[level].push(callback(node));
1697
+
1698
+ if (includeNull) {
1699
+ if (node && this.isRealNodeOrNull(node.right)) stack.push([node.right, level + 1]);
1700
+ if (node && this.isRealNodeOrNull(node.left)) stack.push([node.left, level + 1]);
1701
+ } else {
1702
+ if (node && node.right) stack.push([node.right, level + 1]);
1703
+ if (node && node.left) stack.push([node.left, level + 1]);
1704
+ }
1705
+ }
1706
+ }
1707
+
1708
+ return levelsNodes;
1709
+ }
1710
+
1711
+ morris(): (K | undefined)[];
1712
+
1713
+ morris<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1714
+ callback?: C,
1715
+ pattern?: DFSOrderPattern,
1716
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
1717
+ ): ReturnType<C>[];
1718
+
1719
+ /**
1720
+ * Performs a Morris (threaded) traversal.
1721
+ * @remarks This traversal uses O(1) extra space (excluding the result array) by temporarily modifying the tree's right child pointers. Time O(N), as each node is visited a constant number of times. Space O(1) (excluding the `ans` array).
1722
+ *
1723
+ * @template C - The type of the callback function.
1724
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
1725
+ * @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
1726
+ * @param [startNode=this._root] - The node to start from.
1727
+ * @returns An array of callback results.
1728
+ */
1729
+ morris<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1730
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1731
+ pattern: DFSOrderPattern = 'IN',
1732
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root
1733
+ ): ReturnType<C>[] {
1734
+ startNode = this.ensureNode(startNode);
1735
+
1736
+ if (!startNode) return [];
1737
+ const ans: ReturnType<NodeCallback<BinaryTreeNode<K, V> | null>>[] = [];
1738
+
1739
+ let cur: BinaryTreeNode<K, V> | null | undefined = startNode;
1740
+
1741
+ // Helper to reverse a linked list (formed by right pointers)
1742
+ const _reverseEdge = (node: BinaryTreeNode<K, V> | null | undefined) => {
1743
+ let pre: BinaryTreeNode<K, V> | null | undefined = null;
1744
+ let next: BinaryTreeNode<K, V> | null | undefined = null;
1745
+ while (node) {
1746
+ next = node.right;
1747
+ node.right = pre;
1748
+ pre = node;
1749
+ node = next;
1750
+ }
1751
+ return pre;
1752
+ };
1753
+
1754
+ // Helper to print the reversed edge (for post-order)
1755
+ const _printEdge = (node: BinaryTreeNode<K, V> | null | undefined) => {
1756
+ const tail: BinaryTreeNode<K, V> | null | undefined = _reverseEdge(node);
1757
+ let cur: BinaryTreeNode<K, V> | null | undefined = tail;
1758
+
1759
+ while (cur) {
1760
+ ans.push(callback(cur));
1761
+ cur = cur.right;
1762
+ }
1763
+
1764
+ _reverseEdge(tail); // Restore the edge
1765
+ };
1766
+
1767
+ switch (pattern) {
1768
+ case 'IN':
1769
+ while (cur) {
1770
+ if (cur.left) {
1771
+ const predecessor = this.getPredecessor(cur);
1772
+ if (!predecessor.right) {
1773
+ // Create thread
1774
+ predecessor.right = cur;
1775
+ cur = cur.left;
1776
+ continue;
1777
+ } else {
1778
+ // Remove thread
1779
+ predecessor.right = null;
1780
+ }
1781
+ }
1782
+ ans.push(callback(cur));
1783
+ cur = cur.right;
1784
+ }
1785
+ break;
1786
+ case 'PRE':
1787
+ while (cur) {
1788
+ if (cur.left) {
1789
+ const predecessor = this.getPredecessor(cur);
1790
+ if (!predecessor.right) {
1791
+ // Create thread and visit
1792
+ predecessor.right = cur;
1793
+ ans.push(callback(cur));
1794
+ cur = cur.left;
1795
+ continue;
1796
+ } else {
1797
+ // Remove thread
1798
+ predecessor.right = null;
1799
+ }
1800
+ } else {
1801
+ ans.push(callback(cur));
1802
+ }
1803
+ cur = cur.right;
1804
+ }
1805
+ break;
1806
+ case 'POST':
1807
+ while (cur) {
1808
+ if (cur.left) {
1809
+ const predecessor = this.getPredecessor(cur);
1810
+ if (predecessor.right === null) {
1811
+ // Create thread
1812
+ predecessor.right = cur;
1813
+ cur = cur.left;
1814
+ continue;
1815
+ } else {
1816
+ // Remove thread and print right spine of left child
1817
+ predecessor.right = null;
1818
+ _printEdge(cur.left);
1819
+ }
1820
+ }
1821
+ cur = cur.right;
1822
+ }
1823
+ _printEdge(startNode); // Print the right spine of the root
1824
+ break;
1825
+ }
1826
+ return ans;
1827
+ }
1828
+
1829
+ /**
1830
+ * Clones the tree.
1831
+ * @remarks Time O(N * M), where N is the number of nodes and M is the tree size during insertion (due to `bfs` + `set`, and `set` is O(M)). Space O(N) for the new tree and the BFS queue.
1832
+ *
1833
+ * @returns A new, cloned instance of the tree.
1834
+ */
1835
+ clone(): this {
1836
+ const out = this._createInstance<K, V, R>();
1837
+ this._clone(out);
1838
+ return out;
1839
+ }
1840
+
1841
+ /**
1842
+ * Creates a new tree containing only the entries that satisfy the predicate.
1843
+ * @remarks Time O(N * M), where N is nodes in this tree, and M is size of the new tree during insertion (O(N) iteration + O(M) `set` for each item). Space O(N) for the new tree.
1844
+ *
1845
+ * @param predicate - A function to test each [key, value] pair.
1846
+ * @param [thisArg] - `this` context for the predicate.
1847
+ * @returns A new, filtered tree.
1848
+ */
1849
+ filter(predicate: EntryCallback<K, V | undefined, boolean>, thisArg?: unknown): this {
1850
+ const out = this._createInstance<K, V, R>();
1851
+ let i = 0;
1852
+ for (const [k, v] of this) if (predicate.call(thisArg, v, k, i++, this)) out.set([k, v]);
1853
+ return out;
1854
+ }
1855
+
1856
+ /**
1857
+ * Creates a new tree by mapping each [key, value] pair to a new entry.
1858
+ * @remarks Time O(N * M), where N is nodes in this tree, and M is size of the new tree during insertion. Space O(N) for the new tree.
1859
+ *
1860
+ * @template MK - New key type.
1861
+ * @template MV - New value type.
1862
+ * @template MR - New raw type.
1863
+ * @param cb - A function to map each [key, value] pair.
1864
+ * @param [options] - Options for the new tree.
1865
+ * @param [thisArg] - `this` context for the callback.
1866
+ * @returns A new, mapped tree.
1867
+ */
1868
+ map<MK = K, MV = V, MR = any>(
1869
+ cb: EntryCallback<K, V | undefined, [MK, MV]>,
1870
+ options?: Partial<BinaryTreeOptions<MK, MV, MR>>,
1871
+ thisArg?: unknown
1872
+ ): BinaryTree<MK, MV, MR> {
1873
+ const out = this._createLike<MK, MV, MR>([], options);
1874
+ let i = 0;
1875
+ for (const [k, v] of this) out.set(cb.call(thisArg, v, k, i++, this));
1876
+ return out;
1877
+ }
1878
+
1879
+ /**
1880
+ * Generates a string representation of the tree for visualization.
1881
+ * @remarks Time O(N), visits every node. Space O(N*H) or O(N^2) in the worst case, as the string width can grow significantly.
1882
+ *
1883
+ * @param [startNode=this._root] - The node to start printing from.
1884
+ * @param [options] - Options to control the output (e.g., show nulls).
1885
+ * @returns The string representation of the tree.
1886
+ */
1887
+ override toVisual(
1888
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1889
+ options?: BinaryTreePrintOptions
1890
+ ): string {
1891
+ const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options };
1892
+ startNode = this.ensureNode(startNode);
1893
+ let output = '';
1894
+ if (!startNode) return output;
1895
+
1896
+ if (opts.isShowUndefined) output += `U for undefined\n`;
1897
+ if (opts.isShowNull) output += `N for null\n`;
1898
+ if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)\n`;
1899
+
1900
+ const display = (root: BinaryTreeNode<K, V> | null | undefined): void => {
1901
+ const [lines] = this._displayAux(root, opts);
1902
+ let paragraph = '';
1903
+ for (const line of lines) {
1904
+ paragraph += line + '\n';
1905
+ }
1906
+ output += paragraph;
1907
+ };
1908
+
1909
+ display(startNode);
1910
+ return output;
1911
+ }
1912
+
1913
+ /**
1914
+ * Prints a visual representation of the tree to the console.
1915
+ * @remarks Time O(N) (via `toVisual`). Space O(N*H) or O(N^2) (via `toVisual`).
1916
+ *
1917
+ * @param [options] - Options to control the output.
1918
+ * @param [startNode=this._root] - The node to start printing from.
1919
+ */
1920
+ override print(
1921
+ options?: BinaryTreePrintOptions,
1922
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root
1923
+ ) {
1924
+ console.log(this.toVisual(startNode, options));
1925
+ }
1926
+
1927
+ protected _dfs<C extends NodeCallback<BinaryTreeNode<K, V>>>(
1928
+ callback: C,
1929
+ pattern?: DFSOrderPattern,
1930
+ onlyOne?: boolean,
1931
+ startNode?: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
1932
+ iterationType?: IterationType,
1933
+ includeNull?: boolean,
1934
+ shouldVisitLeft?: (node: BinaryTreeNode<K, V> | null | undefined) => boolean,
1935
+ shouldVisitRight?: (node: BinaryTreeNode<K, V> | null | undefined) => boolean,
1936
+ shouldVisitRoot?: (node: BinaryTreeNode<K, V> | null | undefined) => boolean,
1937
+ shouldProcessRoot?: (node: BinaryTreeNode<K, V> | null | undefined) => boolean
1938
+ ): ReturnType<C>[];
1939
+
1940
+ /**
1941
+ * (Protected) Core DFS implementation.
1942
+ * @remarks Time O(N), visits every node satisfying predicates. Space O(H) for call/explicit stack. O(N) worst-case.
1943
+ *
1944
+ * @template C - Callback type.
1945
+ * @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on nodes.
1946
+ * @param [pattern='IN'] - Traversal order.
1947
+ * @param [onlyOne=false] - Stop after first match.
1948
+ * @param [startNode=this._root] - Starting node.
1949
+ * @param [iterationType=this.iterationType] - Traversal method.
1950
+ * @param [includeNull=false] - Include nulls.
1951
+ * @param [shouldVisitLeft] - Predicate to traverse left.
1952
+ * @param [shouldVisitRight] - Predicate to traverse right.
1953
+ * @param [shouldVisitRoot] - Predicate to visit root.
1954
+ * @param [shouldProcessRoot] - Predicate to process root.
1955
+ * @returns Array of callback results.
1956
+ */
1957
+ protected _dfs<C extends NodeCallback<BinaryTreeNode<K, V> | null>>(
1958
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
1959
+ pattern: DFSOrderPattern = 'IN',
1960
+ onlyOne: boolean = false,
1961
+ startNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
1962
+ iterationType: IterationType = this.iterationType,
1963
+ includeNull = false,
1964
+ shouldVisitLeft: (node: BinaryTreeNode<K, V> | null | undefined) => boolean = node => !!node,
1965
+ shouldVisitRight: (node: BinaryTreeNode<K, V> | null | undefined) => boolean = node => !!node,
1966
+ shouldVisitRoot: (node: BinaryTreeNode<K, V> | null | undefined) => boolean = node => {
1967
+ if (includeNull) return this.isRealNodeOrNull(node);
1968
+ return this.isRealNode(node);
1969
+ },
1970
+ shouldProcessRoot: (node: BinaryTreeNode<K, V> | null | undefined) => boolean = node => this.isRealNodeOrNull(node)
1971
+ ): ReturnType<C>[] {
1972
+ startNode = this.ensureNode(startNode);
1973
+ if (!startNode) return [];
1974
+ const ans: ReturnType<C>[] = [];
1975
+
1976
+ if (iterationType === 'RECURSIVE') {
1977
+ const dfs = (node: BinaryTreeNode<K, V> | null) => {
1978
+ if (!shouldVisitRoot(node)) return;
1979
+
1980
+ const visitLeft = () => {
1981
+ if (shouldVisitLeft(node) && node?.left !== undefined) dfs(node?.left);
1982
+ };
1983
+ const visitRight = () => {
1984
+ if (shouldVisitRight(node) && node?.right !== undefined) dfs(node?.right);
1985
+ };
1986
+
1987
+ switch (pattern) {
1988
+ case 'IN':
1989
+ visitLeft();
1990
+ if (shouldProcessRoot(node)) {
1991
+ ans.push(callback(node));
1992
+ if (onlyOne) return;
1993
+ }
1994
+ visitRight();
1995
+ break;
1996
+ case 'PRE':
1997
+ if (shouldProcessRoot(node)) {
1998
+ ans.push(callback(node));
1999
+ if (onlyOne) return;
2000
+ }
2001
+ visitLeft();
2002
+ visitRight();
2003
+ break;
2004
+ case 'POST':
2005
+ visitLeft();
2006
+ visitRight();
2007
+ if (shouldProcessRoot(node)) {
2008
+ ans.push(callback(node));
2009
+ if (onlyOne) return;
2010
+ }
2011
+ break;
2012
+ }
2013
+ };
2014
+
2015
+ dfs(startNode);
2016
+ } else {
2017
+ // Iterative
2018
+ const stack: DFSStackItem<BinaryTreeNode<K, V>>[] = [{ opt: DFSOperation.VISIT, node: startNode }];
2019
+
2020
+ const pushLeft = (cur: DFSStackItem<BinaryTreeNode<K, V>>) => {
2021
+ if (shouldVisitLeft(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.left });
2022
+ };
2023
+ const pushRight = (cur: DFSStackItem<BinaryTreeNode<K, V>>) => {
2024
+ if (shouldVisitRight(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.right });
2025
+ };
2026
+ const pushRoot = (cur: DFSStackItem<BinaryTreeNode<K, V>>) => {
2027
+ if (shouldVisitRoot(cur.node)) stack.push({ opt: DFSOperation.PROCESS, node: cur.node });
2028
+ };
2029
+
2030
+ while (stack.length > 0) {
2031
+ const cur = stack.pop();
2032
+ if (cur === undefined) continue;
2033
+ if (!shouldVisitRoot(cur.node)) continue;
2034
+ if (cur.opt === DFSOperation.PROCESS) {
2035
+ if (shouldProcessRoot(cur.node) && cur.node !== undefined) {
2036
+ ans.push(callback(cur.node));
2037
+ if (onlyOne) return ans;
2038
+ }
2039
+ } else {
2040
+ // VISIT
2041
+ switch (pattern) {
2042
+ case 'IN':
2043
+ pushRight(cur);
2044
+ pushRoot(cur);
2045
+ pushLeft(cur);
2046
+ break;
2047
+ case 'PRE':
2048
+ pushRight(cur);
2049
+ pushLeft(cur);
2050
+ pushRoot(cur);
2051
+ break;
2052
+ case 'POST':
2053
+ pushRoot(cur);
2054
+ pushRight(cur);
2055
+ pushLeft(cur);
2056
+ break;
2057
+ }
2058
+ }
2059
+ }
2060
+ }
2061
+
2062
+ return ans;
2063
+ }
2064
+
2065
+ /**
2066
+ * (Protected) Gets the iterator for the tree (default in-order).
2067
+ * @remarks Time O(N) for full iteration. O(H) to get the first element. Space O(H) for the iterative stack. O(H) for recursive stack.
2068
+ *
2069
+ * @param [node=this._root] - The node to start iteration from.
2070
+ * @returns An iterator for [key, value] pairs.
2071
+ */
2072
+ protected *_getIterator(node = this._root): IterableIterator<[K, V | undefined]> {
2073
+ if (!node) return;
2074
+
2075
+ if (this.iterationType === 'ITERATIVE') {
2076
+ const stack: (BinaryTreeNode<K, V> | null | undefined)[] = [];
2077
+ let current: BinaryTreeNode<K, V> | null | undefined = node;
2078
+
2079
+ while (current || stack.length > 0) {
2080
+ // Go to the leftmost node
2081
+ while (this.isRealNode(current)) {
2082
+ stack.push(current);
2083
+ current = current.left;
2084
+ }
2085
+
2086
+ // Visit the node
2087
+ current = stack.pop();
2088
+
2089
+ if (this.isRealNode(current)) {
2090
+ if (this._isMapMode) yield [current.key, this._store.get(current.key)];
2091
+ else yield [current.key, current.value];
2092
+ // Move to the right subtree
2093
+ current = current.right;
2094
+ }
2095
+ }
2096
+ } else {
2097
+ // Recursive in-order traversal
2098
+ if (node.left && this.isRealNode(node)) {
2099
+ yield* this[Symbol.iterator](node.left);
2100
+ }
2101
+
2102
+ if (this._isMapMode) yield [node.key, this._store.get(node.key)];
2103
+ else yield [node.key, node.value];
2104
+
2105
+ if (node.right && this.isRealNode(node)) {
2106
+ yield* this[Symbol.iterator](node.right);
2107
+ }
2108
+ }
2109
+ }
2110
+
2111
+ /**
2112
+ * (Protected) Default callback function, returns the node's key.
2113
+ * @remarks Time O(1)
2114
+ *
2115
+ * @param node - The node.
2116
+ * @returns The node's key or undefined.
2117
+ */
2118
+ protected _DEFAULT_NODE_CALLBACK = (node: BinaryTreeNode<K, V> | null | undefined) => (node ? node.key : undefined);
2119
+
2120
+ /**
2121
+ * (Protected) Snapshots the current tree's configuration options.
2122
+ * @remarks Time O(1)
2123
+ *
2124
+ * @template TK, TV, TR - Generic types for the options.
2125
+ * @returns The options object.
2126
+ */
2127
+ protected _snapshotOptions<TK = K, TV = V, TR = R>(): BinaryTreeOptions<TK, TV, TR> {
2128
+ return {
2129
+ iterationType: this.iterationType,
2130
+ toEntryFn: this.toEntryFn as unknown as BinaryTreeOptions<TK, TV, TR>['toEntryFn'],
2131
+ isMapMode: this.isMapMode,
2132
+ isDuplicate: this.isDuplicate
2133
+ };
2134
+ }
2135
+
2136
+ /**
2137
+ * (Protected) Creates a new, empty instance of the same tree constructor.
2138
+ * @remarks Time O(1)
2139
+ *
2140
+ * @template TK, TV, TR - Generic types for the new instance.
2141
+ * @param [options] - Options for the new tree.
2142
+ * @returns A new, empty tree.
2143
+ */
2144
+ protected _createInstance<TK = K, TV = V, TR = R>(options?: Partial<BinaryTreeOptions<TK, TV, TR>>): this {
2145
+ const Ctor = this.constructor as unknown as new (
2146
+ iter?: Iterable<TK | BinaryTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
2147
+ opts?: BinaryTreeOptions<TK, TV, TR>
2148
+ ) => BinaryTree<TK, TV, TR>;
2149
+ return new Ctor([], { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) }) as unknown as this;
2150
+ }
2151
+
2152
+ /**
2153
+ * (Protected) Creates a new instance of the same tree constructor, potentially with different generic types.
2154
+ * @remarks Time O(N) (or as per constructor) due to processing the iterable.
2155
+ *
2156
+ * @template TK, TV, TR - Generic types for the new instance.
2157
+ * @param [iter=[]] - An iterable to populate the new tree.
2158
+ * @param [options] - Options for the new tree.
2159
+ * @returns A new tree.
2160
+ */
2161
+ protected _createLike<TK = K, TV = V, TR = R>(
2162
+ iter: Iterable<TK | BinaryTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR> = [],
2163
+ options?: Partial<BinaryTreeOptions<TK, TV, TR>>
2164
+ ): BinaryTree<TK, TV, TR> {
2165
+ const Ctor = this.constructor as unknown as new (
2166
+ iter?: Iterable<TK | BinaryTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
2167
+ opts?: BinaryTreeOptions<TK, TV, TR>
2168
+ ) => BinaryTree<TK, TV, TR>;
2169
+ return new Ctor(iter, { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) }) as unknown as BinaryTree<
2170
+ TK,
2171
+ TV,
2172
+ TR
2173
+ >;
2174
+ }
2175
+
2176
+ /**
2177
+ * (Protected) Converts a key, node, or entry into a standardized [node, value] tuple.
2178
+ * @remarks Time O(1)
2179
+ *
2180
+ * @param keyNodeOrEntry - The input item.
2181
+ * @param [value] - An optional value (used if input is just a key).
2182
+ * @returns A tuple of [node, value].
2183
+ */
2184
+ protected _keyValueNodeOrEntryToNodeAndValue(
2185
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
2186
+ value?: V
2187
+ ): [BinaryTreeNode<K, V> | null | undefined, V | undefined] {
2188
+ if (keyNodeOrEntry === undefined) return [undefined, undefined];
2189
+ if (keyNodeOrEntry === null) return [null, undefined];
2190
+
2191
+ if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value];
2192
+
2193
+ if (this.isEntry(keyNodeOrEntry)) {
2194
+ const [key, entryValue] = keyNodeOrEntry;
2195
+ if (key === undefined) return [undefined, undefined];
2196
+ else if (key === null) return [null, undefined];
2197
+ const finalValue = value ?? entryValue;
2198
+ return [this.createNode(key, finalValue), finalValue];
2199
+ }
2200
+
2201
+ return [this.createNode(keyNodeOrEntry, value), value];
2202
+ }
2203
+
2204
+ /**
2205
+ * (Protected) Helper for cloning. Performs a BFS and sets all nodes to the new tree.
2206
+ * @remarks Time O(N * M) (O(N) BFS + O(M) `set` for each node).
2207
+ *
2208
+ * @param cloned - The new, empty tree instance to populate.
2209
+ */
2210
+ protected _clone(cloned: BinaryTree<K, V, R>) {
2211
+ // Use BFS with nulls to preserve the tree structure
2212
+ this.bfs(
2213
+ node => {
2214
+ if (node === null) cloned.set(null);
2215
+ else {
2216
+ if (this._isMapMode) cloned.set([node.key, this._store.get(node.key)]);
2217
+ else cloned.set([node.key, node.value]);
2218
+ }
2219
+ },
2220
+ this._root,
2221
+ this.iterationType,
2222
+ true // Include nulls
2223
+ );
2224
+ if (this._isMapMode) cloned._store = this._store;
2225
+ }
2226
+
2227
+ /**
2228
+ * (Protected) Recursive helper for `toVisual`.
2229
+ * @remarks Time O(N), Space O(N*H) or O(N^2)
2230
+ *
2231
+ * @param node - The current node.
2232
+ * @param options - Print options.
2233
+ * @returns Layout information for this subtree.
2234
+ */
2235
+ protected _displayAux(
2236
+ node: BinaryTreeNode<K, V> | null | undefined,
2237
+ options: BinaryTreePrintOptions
2238
+ ): NodeDisplayLayout {
2239
+ const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
2240
+ const emptyDisplayLayout = <NodeDisplayLayout>[['─'], 1, 0, 0]; // Represents an empty spot
2241
+
2242
+ if (node === null && !isShowNull) {
2243
+ return emptyDisplayLayout;
2244
+ } else if (node === undefined && !isShowUndefined) {
2245
+ return emptyDisplayLayout;
2246
+ } else if (this.isNIL(node) && !isShowRedBlackNIL) {
2247
+ return emptyDisplayLayout;
2248
+ } else if (node !== null && node !== undefined) {
2249
+ // Real node
2250
+ const key = node.key,
2251
+ line = this.isNIL(node) ? 'S' : String(key),
2252
+ width = line.length;
2253
+
2254
+ return _buildNodeDisplay(
2255
+ line,
2256
+ width,
2257
+ this._displayAux(node.left, options),
2258
+ this._displayAux(node.right, options)
2259
+ );
2260
+ } else {
2261
+ // Null or Undefined
2262
+ const line = node === undefined ? 'U' : 'N',
2263
+ width = line.length;
2264
+
2265
+ // Treat as a leaf
2266
+ return _buildNodeDisplay(line, width, [[''], 1, 0, 0], [[''], 1, 0, 0]);
2267
+ }
2268
+
2269
+ /**
2270
+ * (Inner) Builds the display lines for a node.
2271
+ * @remarks Time/Space: Proportional to the width and height of the subtrees.
2272
+ */
2273
+ function _buildNodeDisplay(line: string, width: number, left: NodeDisplayLayout, right: NodeDisplayLayout) {
2274
+ const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
2275
+ const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
2276
+ const firstLine =
2277
+ ' '.repeat(Math.max(0, leftMiddle + 1)) +
2278
+ '_'.repeat(Math.max(0, leftWidth - leftMiddle - 1)) +
2279
+ line +
2280
+ '_'.repeat(Math.max(0, rightMiddle)) +
2281
+ ' '.repeat(Math.max(0, rightWidth - rightMiddle));
2282
+
2283
+ const secondLine =
2284
+ (leftHeight > 0
2285
+ ? ' '.repeat(leftMiddle) + '/' + ' '.repeat(leftWidth - leftMiddle - 1)
2286
+ : ' '.repeat(leftWidth)) +
2287
+ ' '.repeat(width) +
2288
+ (rightHeight > 0
2289
+ ? ' '.repeat(rightMiddle) + '\\' + ' '.repeat(rightWidth - rightMiddle - 1)
2290
+ : ' '.repeat(rightWidth));
2291
+
2292
+ const mergedLines = [firstLine, secondLine];
2293
+
2294
+ for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
2295
+ const leftLine = i < leftHeight ? leftLines[i] : ' '.repeat(leftWidth);
2296
+ const rightLine = i < rightHeight ? rightLines[i] : ' '.repeat(rightWidth);
2297
+ mergedLines.push(leftLine + ' '.repeat(width) + rightLine);
2298
+ }
2299
+
2300
+ return <NodeDisplayLayout>[
2301
+ mergedLines,
2302
+ leftWidth + width + rightWidth,
2303
+ Math.max(leftHeight, rightHeight) + 2,
2304
+ leftWidth + Math.floor(width / 2)
2305
+ ];
2306
+ }
2307
+ }
2308
+
2309
+ /**
2310
+ * (Protected) Swaps the key/value properties of two nodes.
2311
+ * @remarks Time O(1)
2312
+ *
2313
+ * @param srcNode - The source node.
2314
+ * @param destNode - The destination node.
2315
+ * @returns The `destNode` (now holding `srcNode`'s properties).
2316
+ */
2317
+ protected _swapProperties(
2318
+ srcNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
2319
+ destNode: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
2320
+ ): BinaryTreeNode<K, V> | undefined {
2321
+ srcNode = this.ensureNode(srcNode);
2322
+ destNode = this.ensureNode(destNode);
2323
+
2324
+ if (srcNode && destNode) {
2325
+ const { key, value } = destNode;
2326
+ const tempNode = this.createNode(key, value); // Use a temp node to hold dest properties
2327
+
2328
+ if (tempNode) {
2329
+ // Copy src to dest
2330
+ destNode.key = srcNode.key;
2331
+ if (!this._isMapMode) destNode.value = srcNode.value;
2332
+
2333
+ // Copy temp (original dest) to src
2334
+ srcNode.key = tempNode.key;
2335
+ if (!this._isMapMode) srcNode.value = tempNode.value;
2336
+ }
2337
+
2338
+ return destNode;
2339
+ }
2340
+ return undefined;
2341
+ }
2342
+
2343
+ /**
2344
+ * (Protected) Replaces a node in the tree with a new node, maintaining children and parent links.
2345
+ * @remarks Time O(1)
2346
+ *
2347
+ * @param oldNode - The node to be replaced.
2348
+ * @param newNode - The node to insert.
2349
+ * @returns The `newNode`.
2350
+ */
2351
+ protected _replaceNode(oldNode: BinaryTreeNode<K, V>, newNode: BinaryTreeNode<K, V>): BinaryTreeNode<K, V> {
2352
+ if (oldNode.parent) {
2353
+ if (oldNode.parent.left === oldNode) {
2354
+ oldNode.parent.left = newNode;
2355
+ } else if (oldNode.parent.right === oldNode) {
2356
+ oldNode.parent.right = newNode;
2357
+ }
2358
+ }
2359
+ newNode.left = oldNode.left;
2360
+ newNode.right = oldNode.right;
2361
+ newNode.parent = oldNode.parent;
2362
+ if (this._root === oldNode) {
2363
+ this._setRoot(newNode);
2364
+ }
2365
+
2366
+ return newNode;
2367
+ }
2368
+
2369
+ /**
2370
+ * (Protected) Sets the root node and clears its parent reference.
2371
+ * @remarks Time O(1)
2372
+ *
2373
+ * @param v - The node to set as root.
2374
+ */
2375
+ protected _setRoot(v: BinaryTreeNode<K, V> | null | undefined) {
2376
+ if (v) {
2377
+ v.parent = undefined;
2378
+ }
2379
+ this._root = v;
2380
+ }
2381
+
2382
+ /**
2383
+ * (Protected) Converts a key, node, entry, or predicate into a standardized predicate function.
2384
+ * @remarks Time O(1)
2385
+ *
2386
+ * @param keyNodeEntryOrPredicate - The item to convert.
2387
+ * @returns A predicate function.
2388
+ */
2389
+ protected _ensurePredicate(
2390
+ keyNodeEntryOrPredicate:
2391
+ | K
2392
+ | BinaryTreeNode<K, V>
2393
+ | [K | null | undefined, V | undefined]
2394
+ | null
2395
+ | undefined
2396
+ | NodePredicate<BinaryTreeNode<K, V>>
2397
+ ): NodePredicate<BinaryTreeNode<K, V>>;
2398
+
2399
+ protected _ensurePredicate(
2400
+ keyNodeEntryOrPredicate:
2401
+ | K
2402
+ | BinaryTreeNode<K, V>
2403
+ | [K | null | undefined, V | undefined]
2404
+ | null
2405
+ | undefined
2406
+ | NodePredicate<BinaryTreeNode<K, V> | null>
2407
+ ): NodePredicate<BinaryTreeNode<K, V> | null> {
2408
+ if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined)
2409
+ return (node: BinaryTreeNode<K, V> | null | undefined) => (node ? false : false);
2410
+
2411
+ if (this._isPredicate(keyNodeEntryOrPredicate)) return keyNodeEntryOrPredicate;
2412
+
2413
+ if (this.isRealNode(keyNodeEntryOrPredicate))
2414
+ return (node: BinaryTreeNode<K, V> | null) => node === keyNodeEntryOrPredicate;
2415
+
2416
+ if (this.isEntry(keyNodeEntryOrPredicate)) {
2417
+ const [key] = keyNodeEntryOrPredicate;
2418
+ return (node: BinaryTreeNode<K, V> | null) => {
2419
+ if (!node) return false;
2420
+ return node.key === key;
2421
+ };
2422
+ }
2423
+
2424
+ // Assume it's a key
2425
+ return (node: BinaryTreeNode<K, V> | null) => {
2426
+ if (!node) return false;
2427
+ return node.key === keyNodeEntryOrPredicate;
2428
+ };
2429
+ }
2430
+
2431
+ /**
2432
+ * (Protected) Checks if an item is a predicate function.
2433
+ * @remarks Time O(1)
2434
+ *
2435
+ * @param p - The item to check.
2436
+ * @returns True if it's a function.
2437
+ */
2438
+ protected _isPredicate(p: any): p is NodePredicate<BinaryTreeNode<K, V>> {
2439
+ return typeof p === 'function';
2440
+ }
2441
+
2442
+ /**
2443
+ * (Protected) Extracts the key from a key, node, or entry.
2444
+ * @remarks Time O(1)
2445
+ *
2446
+ * @param keyNodeOrEntry - The item.
2447
+ * @returns The extracted key.
2448
+ */
2449
+ protected _extractKey(
2450
+ keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
2451
+ ): K | null | undefined {
2452
+ if (keyNodeOrEntry === null) return null;
2453
+ if (keyNodeOrEntry === undefined) return;
2454
+ if (keyNodeOrEntry === this._NIL) return;
2455
+ if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry.key;
2456
+
2457
+ if (this.isEntry(keyNodeOrEntry)) return keyNodeOrEntry[0];
2458
+
2459
+ return keyNodeOrEntry;
2460
+ }
2461
+
2462
+ /**
2463
+ * (Protected) Sets a value in the external store (Map mode).
2464
+ * @remarks Time O(1) (average for Map.set).
2465
+ *
2466
+ * @param key - The key.
2467
+ * @param value - The value.
2468
+ * @returns True if successful.
2469
+ */
2470
+ protected _setValue(key: K | null | undefined, value: V | undefined) {
2471
+ if (key === null || key === undefined) return false;
2472
+ if (value === undefined) return false; // Or allow setting undefined?
2473
+ return this._store.set(key, value);
2474
+ }
2475
+
2476
+ /**
2477
+ * (Protected) Clears all nodes from the tree.
2478
+ * @remarks Time O(1)
2479
+ */
2480
+ protected _clearNodes() {
2481
+ this._setRoot(undefined);
2482
+ this._size = 0;
2483
+ }
2484
+
2485
+ /**
2486
+ * (Protected) Clears all values from the external store.
2487
+ * @remarks Time O(N)
2488
+ */
2489
+ protected _clearValues() {
2490
+ this._store.clear();
2491
+ }
2492
+ }