feat: stack-safe traversal + lazy Sequence + navigation/functional extensions

Core additive work for v3.4 (non-breaking):

- Rewrite nodeCount(), height(), clear() and the post-order iterator iteratively
  so deep/degenerate trees no longer throw StackOverflowError (verified to 50k deep).
- Add lazy Sequence traversal: asSequence(order), pre/post/levelOrderSequence().
- Add navigation extensions: isLeaf, degree, root(), ancestors(), siblings(),
  leaves(), descendants().
- Add functional extensions: findNode, filterNodes, anyNode, allNodes, countNodes,
  foldNodes, mapValues, deepCopy, structurallyEquals (all stack-safe).
- Add tests for stack-safety, the new APIs, and previously-uncovered
  height/depth/nodeCount/path (incl. exception paths). 40 tests green on JVM.
This commit is contained in:
2026-06-06 13:35:39 +02:00
parent 6de95f7976
commit c45c5b7afa
10 changed files with 473 additions and 29 deletions

View File

@@ -22,27 +22,22 @@ import com.github.adriankuta.datastructure.tree.TreeNode
*/
class PostOrderTreeIterator<T>(root: TreeNode<T>) : Iterator<TreeNode<T>> {
private val stack = ArrayDeque<TreeNode<T>>()
private val result = ArrayDeque<TreeNode<T>>()
init {
stack.addAll(getChildrenStack(root))
}
override fun hasNext(): Boolean = stack.isNotEmpty()
override fun next(): TreeNode<T> {
return stack.removeFirst()
}
private fun getChildrenStack(node: TreeNode<T>): ArrayDeque<TreeNode<T>> {
// Iterative post-order: pop a node, prepend it to `result`, then push its children
// left-to-right. Reading `result` front-to-back yields post-order — without the deep
// recursion that overflowed the stack on degenerate (linear) trees.
val stack = ArrayDeque<TreeNode<T>>()
if(node.children.isEmpty()) {
return ArrayDeque(listOf(node))
stack.addLast(root)
while (stack.isNotEmpty()) {
val node = stack.removeLast()
result.addFirst(node)
node.children.forEach { stack.addLast(it) }
}
node.children.forEach {
stack.addAll(getChildrenStack(it))
}
stack.addLast(node)
return stack
}
override fun hasNext(): Boolean = result.isNotEmpty()
override fun next(): TreeNode<T> = result.removeFirst()
}