简体   繁体   English

自上而下的树搜索和替换

[英]Top-down tree search & replace

I'm having trouble coding a tree search & replace algorithm. 我在编写树搜索和替换算法时遇到麻烦。 The input tree contains arbitrarily nested data items--eg, tree = (1 (2 3 (4 (5)) 6)), where 1 is the root, and each level down is embedded in parentheses. 输入树包含任意嵌套的数据项,例如tree =(1(2 3(4(5))6)),其中1是根,而每一级向下均嵌入括号中。 So 1 is at level#1; 所以1在1级 2, 3, 4, 6 are at level#2 (under 1), and 5 is at level#3 (under 4). 2、3、4、6位于2级(低于1),而5位于3级(低于4)。 The entire tree is structured such that the car of any list is always a data item, which can be followed by other data items or subtrees. 整个树的结构使得任何列表的汽车始终是一个数据项,之后可以跟随其他数据项或子树。 The problem is to find a data item in the tree matching (#'equal in my specific case) an input item, and replace the existing old item with a given new subtree--eg, (exchange subtree olditem tree ...). 问题是在树中找到与输入项匹配的数据项(在我的特定情况下为#'等于),然后用给定的新子树替换现有的旧项,例如,(交换子树olditem树...)。 The tree therefore grows with each replacement. 因此,树随着每次替换而增长。 However, the search must proceed top-down in the tree, exchanging only the first such olditem found, and then exit. 但是,搜索必须在树中自上而下进行,仅交换找到的第一个此类旧项目,然后退出。

Some observations?: 1) For binary trees, the search order (top-down visitation) is normally called level-order, the other possible search orders being preorder, inorder, and postorder, but my trees are not necessarily binary. 一些观察结果?:1)对于二叉树,搜索顺序(自上而下的访问)通常称为级别顺序,其他可能的搜索顺序为前顺序,后顺序和后顺序,但是我的树不一定是二叉树。 2) Something like a breadth-first-search algorithm might work, but the nodes are selected by tree traversal, rather than being generated. 2)诸如广度优先搜索算法之类的方法可能起作用,但是节点是通过树遍历选择的,而不是生成的。 3) The standard "substitute" function works only for sequences, not trees. 3)标准的“替换”功能仅适用于序列,不适用于树。 4) The "subst" function works for trees, but seems to traverse in a depth-first manner replacing all matching items, and has no :count keyword (like "substitute" does) to stop after the first replacement. 4)“替换”功能适用于树木,但似乎以深度优先的方式遍历,替换了所有匹配项,并且没有:count关键字(如“替换”一样)在第一次替换后停止。

Any help coding or even framing a good approach would be appreciated. 任何帮助编码,甚至构筑一个好的方法的帮助,将不胜感激。 (Also curious why common-lisp does not have more "tree" functions for both lists and vectors.) (也很好奇为什么common-lisp对列表和向量都没有更多的“树”功能。)

Maybe I shouldn't be doing this, cause you are supposed to do your homework yourself, but it would take me longer to explain what to do, than to show it. 也许我不应该这样做,因为您应该自己做家庭作业,但是我要花更多的时间来解释做什么而不是展示它。 Here is a breadth-first search and replace version: 这是广度优先的搜索和替换版本:

(defun search-replace (item new-item lst)
  (when (listp lst)
    (let ((found-item (member item lst)))
      (if found-item
          (rplaca found-item new-item)
          (some #'(lambda (sublst) (search-replace item new-item sublst)) lst) ))))

This function is destructive, ie, it will modify the original list, because it uses rplaca , and it won't return the resulting list (you can add it at the end). 此函数是破坏性的,即它将修改原始列表,因为它使用rplaca ,并且不会返回结果列表(您可以在末尾添加它)。 You can also add other nice features, such as a test function ( equal or whichever you need). 您还可以添加其他不错的功能,例如测试功能( equal或所需的任何功能)。 It will work also with lists whose car is a sublist (in your example it's always an atom). 它也可以用于其car是子列表的列表(在您的示例中,它始终是一个原子)。 I hope it helps you get started. 希望它能帮助您入门。

@Leo. @Leo。 Like your concise solution--will have to study it for understanding. 就像您的简洁解决方案一样,必须对其进行研究才能理解。 In the meantime here is another preliminary breadth-first search attempt: 同时,这是另一个初步的广度优先搜索尝试:

(defun add-tree (newsubtree tree)
  (let ((queue (make-array 0 :adjustable t :fill-pointer t))
        (data (first newsubtree))
        (index 0))
    (vector-push-extend tree queue)
    (loop until (= index (fill-pointer queue))
        do (let ((current-node (elt queue index)))
             (incf index)
             (loop for child in (second current-node)
                 for i from 0
                 if (and (numberp child) (= child data))
                    do (setf (elt (second current-node) i) newsubtree)
                       (return-from add-tree tree)
                    else do (vector-push-extend child queue))))))

(add-tree '(2 (5 6)) '(0 ((1 (3 2 4)) 2)))
(0 ((1 (3 2 4)) (2 (5 6))))

Thanks for confirming my intuition that breadth-first was the way to approach this. 感谢您证实我的直觉,即广度优先是解决这一问题的方法。 (ps: this is not homework) (ps:这不是作业)

Here's a real breadth first search that actually does replace the shallowest leftmost occurrence. 这是真正的广度优先搜索,实际上确实替代了最浅的最左边的出现。 (Unfortunately @Leo's code, albeit slick, doesn't do that.) (不幸的是,@ Leo的代码虽然没有实现)。

For fun used a circular list as a queue: 为了娱乐起见,将循环列表用作队列:

(setf *print-circle* t)

(defun one-element-queue (item)
  (let ((link (list item)))
    (setf (cdr link) link)))

(defun enqueue (item &optional queue)
  (cond ((null queue) (one-element-queue item))
        (t (let ((new-link (cons item (cdr queue))))
             (setf (cdr queue) new-link)))))

(defun enqueue-all (items &optional queue)
  (dolist (item items queue) (setq queue (enqueue item queue))))

(defun dequeue (queue)
  (cond ((eq queue (cdr queue)) (values (car queue) nil))
        (t (let ((item (cadr queue)))
             (setf (cdr queue) (cddr queue))
             (values item queue)))))

(defun node-replace (new-item old-item node)
  (let ((position (position old-item node :test #'equal)))
    (when position (setf (nth position node) new-item))
    position))

(defun tree-replace (new-item old-item tree)
  (loop with queue = (enqueue tree) and node
        while queue
        do (multiple-value-setq (node queue) (dequeue queue))
        until (node-replace new-item old-item node)
        do (setq queue (enqueue-all (remove-if-not #'listp node) queue)))
  tree)

(setq tree '(1 ((5 ((41))) 3 (4 (5)) 5)))

(print (tree-replace 42 5 tree))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM