clojure不可变二叉搜索树插入
我目前正试图在clojure中实现一个不可变的BST。 这是我的生成树功能:clojure不可变二叉搜索树插入,clojure,binary-search-tree,Clojure,Binary Search Tree,我目前正试图在clojure中实现一个不可变的BST。 这是我的生成树功能: (定义生成树[v]{:v:l nil:r nil}) 和插入: (defn insert [tree v] (if (nil? tree) (make-tree v) (case (compare v (tree :v)) -1 (assoc tree :l (insert (:l tree) v)) 0 tree 1 (assoc tree :r (insert
(定义生成树[v]{:v:l nil:r nil})
和插入:
(defn insert [tree v]
(if (nil? tree)
(make-tree v)
(case (compare v (tree :v))
-1 (assoc tree :l (insert (:l tree) v))
0 tree
1 (assoc tree :r (insert (:r tree) v)))))
问题是,这个insert函数将溢出如下内容
(减少插入(生成树1)(范围10000))
我知道我可以平衡这棵树,这样我就不需要超过1000个深度。我仍然很好奇是否有任何方法来定义函数,这样它就不会溢出
由于可变版本只修改节点,因此不需要存储父节点,这似乎很方便
在现实生活中你会选择什么?可变还是不可变?您的实现不是自平衡的,您的示例是按顺序插入元素的最坏情况。堆栈溢出是由于深度递归造成的。为了避免这种情况,您需要以延续传递样式重写算法,以便可以使用尾部递归或创建显式展开堆栈,而不是隐式使用调用堆栈 在现实生活中,Clojure已经有了一个不可变的自平衡树,
排序集
和排序映射
,我在大多数情况下都会使用它。Java有可变的TreeMap
,如果需要,您可以通过Clojure的Java互操作轻松地利用它
(进入(排序集)(范围10000))
要添加到A.韦伯的答案中:
虽然纯功能性实现需要在CPS中编写,但您可以将树实现为功能包装器+可变节点,其中修改将按如下方式进行:
我还将添加两条相切的评论: 首先,您的实现假设
compare
将返回-1、0、1中的一个,实际上,它可以自由返回任何负数表示“小于”,任何正数表示“大于”((compare“foo”“bar”)
在我的REPL中计算为4
,很可能是任何JVM REPL,尽管确切的值同样未指定)
其次,如果您对Clojure中实现的自平衡树的实际示例感兴趣,您可能想看看,这是Clojure中的PersistentTreeMap
和PersistentTreeSet
的实现。它基于ClojureScript实现,而ClojureScript又是Clojure Java实现的一个端口。在这一点上,我想说它纯粹是实验性的,但它似乎确实有效(通过了CLJS测试套件,达到了我在REPL上预期的效果),并且可以作为Clojure中简单PDS实现的示例
1基本上,在编写ClojureScript实现之后,我想看看生成相同数据结构的Clojure实现需要多少额外工作。很明显会有一些额外的工作,因为有Java接口要实现,一些数组处理代码需要调整等等,但我希望这不会增加太多。我很高兴地报告,这一基本期望得到了经验的证实。就性能而言,它并没有完全达到Java实现的标准(我似乎记得大多数基准测试都慢了1.5倍,不过我必须重新检查一下);我希望最终能改进这一点,不过目前性能调整的优先级更高。您同时提出了几个问题。Re:现实生活中,总有一个权衡。可变数据结构往往需要更少的分配,因此它们减轻了GC的压力,并导致更少的内存碎片。当数据必须在线程之间共享时,不可变数据结构更安全、更方便,并且可能更高效。