基于深度的Clojure树节点

时间:2016-09-06 02:20:57

标签: clojure tree

我一直在努力解决递归问题,而且我的想法已经不多了。基本上,我有一个树形表示,如下所示:

{1 {:root nil} 2 {:root 1} 3 {:root 1} 4 {:root 2} 5 {:root 1} 6 {:root 4}}

我必须在最后一个树中建立一个新树,它表示父/子关系级别。有效输出将是:

{ 1 [3 1] 2 [1] 3 [] 4 [1] 5 [] 6 [] }

每个节点都有一个按关系级别计算的项目数组。因此节点1有3个直接子节点(2 3 5)和一个孙子节点(4)。节点2只有一个子节点(4),节点4有一个直接子节点(6),所有其他子节点都是空的(没有子节点)。

我发现了一些问题like this one实际上有所帮助,但并不完全符合我的要求。我也是功能编程的新手,任何帮助都会受到赞赏。

3 个答案:

答案 0 :(得分:0)

我将假设上面的示例输出中存在错误,它应该是:

{ 1 [3 1 1] 2 [1 1] 3 [] 4 [1] 5 [] 6 [] }

你的样本输出没有说明6是1的曾孙和2的孙子。

我将在这里详述一个解决方案。我们首先编写一个函数,给定树中的树和顶点,计算从该顶点到树顶部的路径:

(defn path-to-top [tree v]
    (if (nil? v)
        '()
        (cons v (path-to-top tree (:root (get tree v))))))

接下来,让我们编写一个函数,它从树的顶点到顶部采用这样的路径,并将每个顶点关联,该顶点距起始顶点的距离为:

(defn steps-indexed-path
    ([upward-path steps]
        (if (= upward-path '())
            '()
            (cons [(first upward-path) steps] (steps-indexed-path (rest upward-path) (+ steps 1)))))
    ([upward-path]
        (steps-indexed-path upward-path 0)))

在第一个函数返回顶点列表的情况下,此函数返回一个向量列表,其中第一个条目是顶点,第二个条目是从路径上第一个顶点到给定顶点的步数。 / p>

好吧,当我们将这个函数应用于树中的每个顶点时,我们将为每个顶点vw的每个后代v(以某种嵌套形式)数据[v <# steps from v to w>]。对于这些数据中的每一个,我们应该在最终解决方案中将<# steps from v to w>组件添加到与v关联的向量的(defn count-descendants [tree] (let [markers (reduce concat '() (map steps-indexed-path (map (partial path-to-top tree) (keys tree))))] (reduce (fn [counter [vertex generation]] (assoc counter vertex (assoc (get counter vertex {}) generation (+ (get (get counter vertex {}) generation 0) 1)))) {} markers))) 组件中。在我们进入向量阶段之前,让我们将关联级别与计数相关联:

hash-map

这会生成一个v,其键是v的顶点,并且每个顶点hash-map对应的值是另一个(defn sanitize-descendant-counts [association] (let [max-depth (apply max (keys association))] (map (fn [i] (get association i 0)) (range 1 (+ max-depth 1))))) (defn solve-problem [tree] (let [descendant-counts (count-descendants tree)] (apply merge (map (fn [v] (hash-map v (vec (sanitize-descendant-counts (get descendant-counts v))))) (keys descendant-counts))))) ,其中键是不同的树中该顶点的后代的可能代,并且值是每一代的后代数。

我们现在要做的就是将上一个函数的输出转换为您指定的格式:

{1 [3 1 1], 4 [1], 6 [], 3 [], 2 [1 1], 5 []}

这是我在您的示例中运行此代码时获得的输出:

application.config

您可以access all the code here,包括您在示例中运行所需的内容。希望有所帮助!

答案 1 :(得分:0)

我将尝试概述一种可能的方法,强调递归核心,并对较小的细节进行掩饰。这些较小的细节有很多,其中一些并不是微不足道的,但它们与递归本身无关,而且会使答案变得杂乱无章。

让我们从树表示的细节中抽象出来。将树视为节点集合,其中每个节点可以是叶子(没有子节点),也可以是分支。假设我们有两个函数branch?children。接收一个参数 - 一个节点。 branch?是具有明显含义的谓词,children返回节点的子序列。 (它与tree-seq核心功能所预期的合同相同。)我留给您编写branch?children代码。 (您可能希望更改树形表示,以便更容易编写这些函数。)

让我们尝试创建一个函数levels,给定一个节点将按级别返回一系列后代 - 儿童,孙子等等。所以我们期待你的树

(levels 1)
;; => (3 1 1)
(levels 2)
;; => (1 1)

(顺便说一下,你有一个错字。节点1有一个曾孙子 - 它是6个)

这是核心 - levels

(defn levels
  [node]
  (if (branch? node)
    (cons (count (children node)) (sum-up-levels (map levels (children node))))
    []))

这是递归的核心。 基本情况是叶子 - 当branch?返回false时,我们知道没有子节点,因此级别为空 - []。否则,我们会计算子项和cons下面总结级别的数字(即添加到列表中)。总结意味着按级别对数字进行求和 - 儿童总数,然后是孙子孙女总数等。在这里我们有递归 - 我们下降到孩子们,使用levels递归地为每个孩子调用map

sum-up-levels函数对代码有点烦人。我离开这么多,你要填写我可能只是在这里给我的代码(当然不是最短的,但我没有更多的时间来完善它。)

(defn reduce-through
   [f & colls]
   (when-let [colls (seq (filter seq colls))]
     (cons (reduce f (map first colls))
           (apply reduce-through f (map rest colls)))))

(defn sum-up-levels
   [levels]
   (apply reduce-through + levels))

定义levels后,很容易以您需要的形式获得结果。试一试(提示 - 使用tree-seq。)

答案 2 :(得分:0)

(defn children-depths [parents]
  (let [children ; let's first build an inverted index
        (reduce-kv (fn [children node {parent :root}]
                     (update children parent conj node))
          {} parents)
        depths (fn depths [node]
                 (if-some [nodes (children node)]
                   (into [(count nodes)]
                     ; pads and sums
                     (reduce #(map + (concat %1 (repeat 0)) (depths %2))
                       nil nodes))
                   []))] ; no descendants -> empty 
    (into {} (for [node (keys parents)] [node (depths node)]))))

=> (children-depths {1 {:root nil} 2 {:root 1} 3 {:root 1} 4 {:root 2} 5 {:root 1} 6 {:root 4}})
{1 [3 1 1], 2 [1 1], 3 [], 4 [1], 5 [], 6 []}

一个明显的改进是避免重新计算儿童的深度。