检查Common Lisp中是否平衡了n-ary树

时间:2015-12-11 21:25:07

标签: tree lisp common-lisp balance

我正在尝试编写代码来检查一个n-ary树是否在clisp中是平衡的。 树是这样给出的:

(A (B (E (I))(F))(C (G))(D)) 

看起来像:

      A
   /  |  \
  B   C   D
 /\   |   
E  F  G   
|
I

哪个会不平衡。

我正在考虑用以下方法解决它:

  • 一封信的所有叶子的最高等级 - 所有叶子的最低等级不应大于1.

  • 我首先考虑将此规则应用于A,B,C。如果差值不大于1,则检查E,F,G,直到我检查所有可能的字母并且树是平衡的或者我得到大于1的差异,这意味着它是不平衡的。

这是最小/最高级别的代码:

(defun nrlvlmax (tail) 
(cond 
    ( (null tail) 0)
    ( (listp (car tail)) (max ( + 1 (nrlvl (car tail))) (nrlvl (cdr tail))))
    ( t (nrlvl (cdr tail)))
)

我不知道如何解析列表并应用我的规则。我不应该使用map / lamba函数,只是像基础知识一样。如何解析给定的列表?

2 个答案:

答案 0 :(得分:1)

这是一种不使用高阶函数的可能解决方案。

这个想法是计算树的最大水平及其最小水平,并检查它们的差异。

为了计算树的最大(或最小)级别,我们使用两个相互递归的函数:第一个计算树的最大(最小)级别,第二个计算所有子级的最大值(最小值)。

当然,如果一棵树有孩子,那么它的等级是1加上其子女的最大(最小)等级。

子项的函数有两个参数,第一个是要考虑的其余子项,第二个是当前值的最大值(最小值)。

(defun maxlevel(tree)
  (cond ((null tree) 0)
        ((null (cdr tree)) 1)
        (t (1+ (max-for-children (cddr tree) (maxlevel (cadr tree)))))))

(defun max-for-children(children current-max)
  (if (null children)
      current-max
      (max-for-children (cdr children) (max current-max (maxlevel (car children))))))

(defun minlevel(tree)
  (cond ((null tree) 0)
        ((null (cdr tree)) 1)
        (t (1+ (min-for-children (cddr tree) (minlevel (cadr tree)))))))

(defun min-for-children(children current-min)
  (if (null children)
      current-min
      (min-for-children (cdr children) (min current-min (minlevel (car children))))))

(defun balanced(tree)
  (= (maxlevel tree) (minlevel tree)))

这是完美平衡的树木。如果允许树的级别之间至少有一个树,则用最后一个函数替换:

(defun balanced(tree)
  (<= (abs (- (maxlevel tree) (minlevel tree))) 1))

答案 1 :(得分:0)

我的previous solution效率不高有两个原因:

  1. 树被访问了两次,
  2. 一旦找到从根到叶子的路径违反平衡条件,算法就不会停止。
  3. 因此,这是一个更有效的解决方案,只访问树一次,并在找到路径太短或太长时立即停止。

    基本思路是:

    1. 所有工作都由内部函数min-max执行,它返回几个值:最小深度和以函数当前参数为根的最大子树深度,这样只允许访问树一次

    2. 在每次递归调用时,函数接收当前级别,当前最小级别和当前最大级别,以便它可以尽快检查树是否不平衡并且必须立即停止访问。对于节点的第一个子节点,当前最大值和最小值设置为nil(因此我定义了两个辅助函数,即使第二个参数为nil,也计算最小值或最大值)。

    3. 请注意,如果树不平衡,则main函数返回nil,或树的最小深度。

      (defun mymin(x y)
        (if y (min x y) x))
      
      (defun mymax(x y)
        (if y (max x y) x))
      
      (defun balanced(tree)
        (labels ((min-max(tree current-level current-min current-max)
                   (when (and current-min (> (1- current-level) current-min))
                      (return-from balanced nil))                  ; this path is too long
                   (if (null (cdr tree))                           ; if it is a leaf node
                       (if (and current-max (< (1+ current-level) current-max))
                           (return-from balanced nil)              ; this path is too short
                           (values current-level current-level))   ; return normally
                       (loop for child in (cdr tree)               ; find min-max for each child
                          do (multiple-value-bind (min1 max1)
                                 (min-max child (1+ current-level) current-min current-max)
                               (setf current-min (mymin min1 current-min)
                                     current-max (mymax max1 current-max)))
                          finally (return (values current-min current-max))))))
          (values (min-max tree 0 nil nil))))
      

      最后,请注意该函数使用循环。可以生成递归版本,但这只会使解决方案复杂化并使其不自然。