12

鉴于以下...

(def inTree
 '((1 2)
   (1 2 3)
   (1 2 4 5 9)
   (1 2 4 10 15)
   (1 2 4 20 25)))

你会如何把它变成这个 trie?

(def outTrie
 '(1
    (2 ()
       (3 ())
       (4 (5
            (9 ()))
          (10
            (15 ()))
          (20
            (25 ()))))))
4

4 回答 4

16

这是一个清理后的解决方案。这修复了 Brian 的 add-to-trie 方法的错误,因为它目前依赖于您以长度递增的顺序插入 seq。它还允许通过前缀查询 trie,这是一个常见的用例。

请注意,这里的内存使用率更高,因为它将值存储在 trie 的叶节点中,因此您可以执行搜索。

(defn add-to-trie [trie x]
  (assoc-in trie x (merge (get-in trie x) {:val x :terminal true})))

(defn in-trie? [trie x]
  "Returns true if the value x exists in the specified trie."
  (:terminal (get-in trie x) false))

(defn prefix-matches [trie prefix]
  "Returns a list of matches with the prefix specified in the trie specified."
  (keep :val (tree-seq map? vals (get-in trie prefix))))

(defn build-trie [coll]
  "Builds a trie over the values in the specified seq coll."
  (reduce add-to-trie {} coll))
于 2010-03-24T20:24:06.860 回答
10

列表在这里非常笨拙,更不用说效率低下了。在 Clojure 中,在适当的时候使用向量、散列映射和集合更为惯用。使用哈希映射:

(def in-tree
 '((1 2)
   (1 2 3)
   (1 2 4 5 9)
   (1 2 4 10 15)
   (1 2 4 20 25)))

(defn add-to-trie [trie x]
  (assoc-in trie `(~@x :terminal) true))

(defn in-trie? [trie x]
  (get-in trie `(~@x :terminal)))

如果您希望它打印 sorted 您可以使用sorted-maps 代替,但您必须编写您自己的assoc-in使用 sorted maps 的版本。任何状况之下:

user> (def trie (reduce add-to-trie {} in-tree))
#'user/trie
user> trie
{1 {2 {4 {20 {25 {:terminal true}}, 10 {15 {:terminal true}}, 5 {9 {:terminal true}}}, 3 {:terminal true}, :terminal true}}}
user> (in-trie? trie '(1 2))
true
user> (in-trie? trie '(1 2 4))
nil
user> (in-trie? trie '(1 2 4 20 25))
true
于 2009-09-21T08:49:26.627 回答
1

作为一般方法,这就是我要做的:

  • 编写一些函数来创建一个 trie 并将新元素插入到 trie 中。
  • 创建一个新的树。
  • 遍历输入列表并将每个元素插入到 trie 中。

这个问题非常适合递归实现。如果可能的话,我会以此为目标。

于 2009-09-21T03:28:34.293 回答
1

我敢肯定有一种更漂亮的方式(有!见布赖恩的回答更好):

(defn find-in-trie
  "Finds a sub trie that matches an item, eg:
  user=> (find-in-trie '(1 (2) (3 (2))) 3)
  (3 (2))"
  [tr item]
  (first (for [ll (rest tr) :when (= (first ll) item)] ll)))


(defn add-to-trie
  "Returns a new trie, the result of adding se to tr, eg:
  user=> (add-to-trie nil '(1 2))
  (1 (2))"
  [tr se]
  (cond
    (empty? se) tr
    (empty? tr) (add-to-trie (list (first se)) (rest se))
    :else (if-let [st (find-in-trie tr (first se))]
            (cons (first tr)
                  (cons (add-to-trie st (rest se))
                        (filter (partial not= st) (rest tr))))
            (cons (first tr)
                  (cons (add-to-trie (list (first se)) (rest se))
                        (rest tr))))))

(def in '((1 2)
          (1 2 3)
          (1 2 4 5 9)
          (1 2 4 10 15)
          (1 2 4 20 25)))

(reduce add-to-trie '(nil) in)

-> (无 (1 (2 (4 (20 (25)) (10 (15)) (5 (9))) (3))))

请注意,我选择使用 nil 作为根节点,并且没有费心保留空列表来表示没有子节点。实际上这样做是不正确的,因为它不保留子字符串标识。

于 2009-09-21T05:57:16.753 回答