【问题标题】:In a tree, how do I find paths to tree nodes that have children with leaves?在树中,如何找到具有带叶子的子节点的树节点的路径?
【发布时间】:2019-05-13 18:23:28
【问题描述】:

基本上,我正在尝试实现这个算法,但也许有更好的方法来实现它。

  • 从根开始
  • 检查当前节点的每个子节点是否有带叶子的子节点(子节点)
  • 如果当前节点的任何子节点有叶子,记录到当前节点(不是子节点)的路径并且不要继续沿着该路径继续下去。
  • 否则继续 DFS

非功能性伪代码:

def find_paths(node):
    for child in node.children:
       if child.children.len() == 0
          child_with_leaf = true
    if child_with_leaf
       record path to node
    else
       for child in node.children
           find_paths(child)

例如:

:root
  |- :a
  |   +- :x
  |       |- :y
  |       |   +- :t
  |       |       +- :l2
  |       +- :z
  |          +- :l3
  +- :b
      +- :c
          |- :d
          |   +- :l4
          +- :e
              +- :l5

结果是:

[[:root :a]
 [:root :b :c]]

这是我对 clojure 的破解:

(defn atleast-one?
  [pred coll]
  (not (nil? (some pred coll))))

; updated with erdos's answer
(defn children-have-leaves?
  [loc]
  (some->> loc
           (iterate z/children)
           (take-while z/branch?)
           (atleast-one? (comp not empty? z/children))))

(defn find-paths
  [tree]
  (loop [loc (z/vector-zip tree)
         ans nil]
    (if (z/end? loc)
      ans
      (recur (z/next loc)
             (cond->> ans
                      (children-have-leaves? loc)
                      (cons (->> loc z/down z/path (map z/node)))))))
  )

(def test-data2
  [:root [:a [:x [:y [:t [:l2]]] [:z [:l3]]]] [:b [:c [:d [:l4]] [:e [:l5]]]]]
  )

更新:使用下面的 erdos 答案修复了崩溃,但我认为我的代码仍然存在问题,因为它会打印每条路径而不是所需的路径。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    我假设您已经引用了我之前与拉链相关的answer。但请注意,我之前的答案按原样使用vector-zip,因此您必须像vector-zip 一样导航它——您可能需要了解这两个光标的工作方式。为了简化导航,我建议您为您的树形结构创建自己的拉链。即

    (defn my-zipper [root]
      (z/zipper ;; branch?
                (fn [x]
                  (when (vector? x)
                    (let [[n & xs] x] (and n (-> xs count zero? not)))))
                ;; children
                (fn [[n & xs]] xs)
                ;; make-node
                (fn [[n & _] xs] [n xs])
                root))
    

    那么解决方案将类似于我的其他答案:

    (def test-data2
      [:root 
       [:a 
        [:x 
         [:y 
          [:t [:l2]]] 
         [:z [:l3]]]] 
       [:b 
        [:c 
         [:d [:l4]] 
         [:e [:l5]]]]])
    
    (->> test-data2
         my-zipper
         (iterate z/next)
         (take-while (complement z/end?))
         (filter (comp children-with-leaves? z/node))
         (map #(->> % z/path (map z/node)))
         set)
    ;; => #{(:root :a :x) (:root :a :x :y) (:root :b :c)}
    

    主要逻辑简化为:

    (defn children-with-leaves? [[_ & children]]
      (some (fn [[c & xs]] (nil? xs)) children))
    

    【讨论】:

    • 是的,您的其他回答启发了这一点。我在这里尝试了您的解决方案,我收到了#{} 我无法看到它失败的地方。
    • 根应该是这样的分支:(def test-data2 [:root [:a [:x [:y [:t [:l2]]] [:z [:l3]]]] [:b [:c [:d [:l4]] [:e [:l5]]]]])
    • 因为每个节点都是这样的结构:[node-name & children]
    • 但有一件事 - 一旦找到匹配项,你如何让它停止下降树?我的问题中的非功能代码将停在(:root :a :x),而不是返回(:root :a :x :y)。看起来您的代码搜索整个树而不是停止某个分支。
    • DFS 会将您带到(:root :a :x :y, :t) 作为第一个满足条件的路径。 (:root :a :x, :z) 是下一个,因此您将无法撤回之前的答案。看起来你在要求 BFS。
    【解决方案2】:

    异常来自您的children-have-leaves? 函数。

    (not (empty? z/children)) 表达式失败,因为 z/children 是一个函数,但是,empty? 必须在集合上调用。

    如果节点有子节点,您需要一个返回 true 的谓词,例如:(fn [x] (not (empty? (z/children x)))) 或更短:(comp not empty? z/children)

    正确的实现方式:

    (defn children-have-leaves?
      [loc]
      (some->> loc
               (iterate z/children)
               (take-while z/branch?)
               (atleast-one? (comp not empty? z/children))))
    

    【讨论】:

    • 感谢您的回答。这解决了我的崩溃。但我想我的代码有问题,因为它现在打印每条路径,而不仅仅是所需的路径。
    【解决方案3】:

    如果你想处理树状数据结构,我强烈推荐the tupelo.forest library

    不过,我不明白你的目标。在您的示例中,节点 :a:c 与最近的叶子的距离并不相等。

    实际上,我刚刚注意到您示例中的树与您的代码尝试中的树不同。您能否更新问题以使它们保持一致?


    下面是一个例子:

    (dotest ; find the grandparent of each leaf
      (hid-count-reset)
      (with-forest (new-forest)
        (let [data              [:root
                                 [:a
                                  [:x
                                   [:y [:t [:l2]]]
                                   [:z [:l3]]]]
                                 [:b [:c
                                      [:d [:l4]]
                                      [:e [:l5]]]]]
              root-hid          (add-tree-hiccup data)
              leaf-paths        (find-paths-with root-hid [:** :*] leaf-path?)
              grandparent-paths (mapv #(drop-last 2 %) leaf-paths)
              grandparent-tags  (set
                                  (forv [path grandparent-paths]
                                    (let [path-tags (it-> path
                                                      (mapv #(hid->node %) it)
                                                      (mapv #(grab :tag %) it))]
                                      path-tags)))]
          (is= (format-paths leaf-paths)
            [[{:tag :root} [{:tag :a} [{:tag :x} [{:tag :y} [{:tag :t} [{:tag :l2}]]]]]]
             [{:tag :root} [{:tag :a} [{:tag :x} [{:tag :z} [{:tag :l3}]]]]]
             [{:tag :root} [{:tag :b} [{:tag :c} [{:tag :d} [{:tag :l4}]]]]]
             [{:tag :root} [{:tag :b} [{:tag :c} [{:tag :e} [{:tag :l5}]]]]]])
          (is= grandparent-tags
              #{[:root :a :x] 
                [:root :a :x :y] 
                [:root :b :c]} ))))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多