[英]Build List from binary tree inorder traversal using function “fold”
I'm learning Scala. 我正在学习Scala。 Now I have this code snippet:
现在,我有以下代码片段:
sealed abstract class BSTree {
def fold[A](init: A)(f: (A, Int) => A): A = this match {
case Empty => init
case Node(left, data, right) =>
val curInorder:A = f(left.fold(init)(f), data)
right.fold(curInorder)(f)
}
}
case object Empty extends BSTree
case class Node(left: BSTree, data: Int, right: BSTree) extends BSTree
My aim is to add another method toList in class BSTree
, which is on top of method fold
and build a List
from the binary tree's inorder traversal. 我的目标是在方法
fold
顶部向class BSTree
的List
添加另一个方法,并根据二叉树的有序遍历构建List
。
My current implementation is: 我当前的实现是:
sealed abstract class BSTree {
def fold[A](init: A)(f: (A, Int) => A): = .....//code snippet skipped
def toList: List[Int] =
fold(Nil: List[Int])((xs: List[Int], hd)=> hd::xs).reverse
}
But I feel that building a List
and then reversing it is ugly. 但是我觉得建立一个
List
然后反转它是很丑陋的。 Is there a more elegant approach? 有没有更优雅的方法? Any hints are appreciated.
任何提示表示赞赏。
First of all, your fold is not tail recursive which for large input might result in StackOverflowException
. 首先,您的折叠不是尾部递归,对于大量输入而言,这可能会导致
StackOverflowException
。 I'd encourage you to try out and implement it on your own using Stack
. 我鼓励您尝试并使用
Stack
自己实现它。 For reference I'll place a sample implementation at the bottom of my post. 作为参考,我将在示例的底部放置一个示例实现。
Secondly, as it was already mentioned in comments - you might want to use ListBuffer
so that building your list is more efficient in reversed order (thus, there is no need to reverse it back). 其次,正如注释中已经提到的那样-您可能要使用
ListBuffer
以便以反向顺序构建列表更加有效(因此,无需反向反向)。
Here's a one-liner: 这里是单线:
def toList: List[Int] = fold(ListBuffer.empty[Int])(_ += _).toList
And the the reference for implementing tail-recursive fold
: 以及实现尾递归
fold
的参考:
def fold[B](init: B)(op: (B, A) => B): B = {
def go(stack: List[(A, Tree[A])], current: Tree[A], acc: B): B = (current, stack) match {
case (Empty, Nil) => acc
case (Empty, (d, r) :: xs) => go(xs, r, op(acc, d))
case (Node(l, d, r), _) => go((d, r) +: stack, l, acc)
}
go(Nil, this, init)
}
I find that simply using xs :+ hd
instead of hd::xs
puts the values in the correct order (depth-first, left-to-right). 我发现仅使用
xs :+ hd
而不是hd::xs
将值按正确的顺序排列(深度优先,从左至右)。
val testTree: BSTree =
Node(Node(Empty, 0, Empty), 1, Node(Empty, 2, Node(Node(Empty, 3, Empty), 4, Empty)))
def toList(bSTree: BSTree): List[Int] =
bSTree.fold(List[Int]())((acc, next) => acc :+ next)
toList(testTree) // List(0,1,2,3,4)
My implementation above is O(n²) . 我在上面的实现是O(n²) 。 We can improve it to O(n) by using a
ListBuffer
, as per @dkim's comment, or we can use Vector
and then convert to List
when we're done. 我们可以用它来改善O(n)的
ListBuffer
,按@ DKIM的评论,或者我们可以使用Vector
然后转换为List
时,我们就大功告成了。
Apart from simply fixing the toList
method, we might ask why the result of using fold
to implement toList
didn't agree with our intuition (giving us a backwards list instead of a forwards list). 除了简单地修复
toList
方法之外,我们可能会问为什么使用fold
来实现toList
的结果与我们的直觉toList
(为我们提供了向后列表而不是向前列表)。 One might point out that the fold signature for list matches the structure of the List
class hierarchy. 可能会指出,列表的折叠签名与
List
类层次结构的结构匹配。
abstract class List[+A] {
def fold[B](init: B)(step: (A, B) => B): B
}
case object Empty extends List[Nothing] {
def fold[B](init: B)(step: (A, B) => B): B = init
}
case class Cons[+A](head: A, tail: List[A]) extends List[A] {
def fold[B](init: B)(step: (A, B) => B): B =
step(head, tail.fold(init)(step))
}
Notice how the method signature of fold
matches the class hierarchy, even down to the values that each implementing class holds. 注意
fold
的方法签名如何与类层次结构匹配,甚至降至每个实现类所拥有的值。 (Aside: For purposes of brevity, I am using a very naive implementation of fold
that is neither efficient nor stack safe. Production implementations should be tail recursive or use a loop and a mutable buffer, but the point is that the method signature would be the same.) (另外:为简洁起见,我使用的
fold
非常幼稚,既不高效也不安全。生产实现应为尾递归或使用循环和可变缓冲区,但要点是方法签名应为相同。)
We can do the same for your BSTree
class, the fold
signature would be: 我们可以对您的
BSTree
类执行相同的BSTree
, fold
签名为:
abstract class BSTree {
def fold[A](withEmpty: A)(withNode: (A, Int, A) => A): A
}
Then toList
would be tree.fold(List[Int]())((l, n, r) => l ++ List(n) ++ r)
. 那么
toList
将是tree.fold(List[Int]())((l, n, r) => l ++ List(n) ++ r)
。 But again, use a buffer or Vector
to get decent performance if you anticipate tree
being even about 50 entries or so. 但是同样,如果您预期
tree
大约有50个左右的条目,请使用缓冲区或Vector
获得不错的性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.