【发布时间】:2012-04-23 23:20:56
【问题描述】:
给定一个简单的 node.id,node.parentId 关联,我有一个函数可以计算一些 treeNodes 集合的左右节点值。它非常简单并且效果很好......但是,我想知道是否有更惯用的方法。具体来说,有一种方法可以在不使用一些外部跟踪值的情况下跟踪左/右值,但仍然保持美味的递归。
/*
* A tree node
*/
case class TreeNode(val id:String, val parentId: String){
var left: Int = 0
var right: Int = 0
}
/*
* a method to compute the left/right node values
*/
def walktree(node: TreeNode) = {
/*
* increment state for the inner function
*/
var c = 0
/*
* A method to set the increment state
*/
def increment = { c+=1; c } // poo
/*
* the tasty inner method
* treeNodes is a List[TreeNode]
*/
def walk(node: TreeNode): Unit = {
node.left = increment
/*
* recurse on all direct descendants
*/
treeNodes filter( _.parentId == node.id) foreach (walk(_))
node.right = increment
}
walk(node)
}
walktree(someRootNode)
编辑 - 节点列表取自数据库。将节点拉入适当的树会花费太多时间。我正在将一个平面列表拉入内存,我所拥有的只是通过节点 ID 与父母和孩子相关的关联。
添加左/右节点值允许我使用单个 SQL 查询获取所有子节点(和子节点的子节点)的快照。
如果父子关联发生变化(它们经常发生变化),计算需要非常快速地运行以保持数据完整性。
除了使用了不起的 Scala 集合之外,我还通过对树节点上的一些前置/后置过滤使用并行处理来提高速度。我想找到一种更惯用的方法来跟踪左/右节点值。在查看@dhg 的答案后,它变得更好了。使用 groupBy 而不是过滤器将算法(主要是?)变成线性的而不是二次的!
val treeNodeMap = treeNodes.groupBy(_.parentId).withDefaultValue(Nil)
def walktree(node: TreeNode) = {
def walk(node: TreeNode, counter: Int): Int = {
node.left = counter
node.right =
treeNodeMap(node.id)
.foldLeft(counter+1) {
(result, curnode) => walk(curnode, result) + 1
}
node.right
}
walk(node,1)
}
【问题讨论】:
-
treeNodes在哪里定义?您没有递归定义 TreeNode 是否有某些原因?walktree有什么意义?要重新编号left和right值?为什么left和right值不与TreeNodes 关联? -
这不是codereview,但你需要方式个更少的cmets,你至少需要一个有用的评论。恰好零个 cmets 说出了代码尚未告诉您的任何相关内容。把它们都扔掉,然后添加两行来描述目标。
-
@Rex,我也有同样的想法 :-)
-
treeNodes 只是一个 TreeNode 的列表。它不是递归定义的,因为列表是从数据库中提取的,这需要时间。