Scheme - 插入二进制堆与二进制搜索树

时间:2018-02-24 17:57:42

标签: scheme racket

以前,我已经参与了一项练习,用于在计划中实现二进制搜索树的作业,现在我正在尝试将此代码转换为堆。我正在努力的是找到将新元素插入堆中的正确位置。在二进制搜索树中,我们简单地使用谓词导航树,比较左侧或右侧的每个节点。

(define (bst-insert bst f x)
  (cond ((bst-is-empty? bst) (bst-create x
                                         (bst-create-empty)
                                         (bst-create-empty)))
        ((f x (bst-root bst)) (bst-create (bst-root bst)
                                          (bst-insert (bst-left bst) f x)
                                          (bst-right bst)))
        (else (bst-create (bst-root bst)
                          (bst-left bst)
                          (bst-insert (bst-right bst) f x)))))

其中f是我们的谓词函数。但是对于堆,我们应该在下一个可用位置插入。为了找到下一个位置,我是否缺少一个技巧?

编辑:

(define heap-create list)

(define (heap-create-empty) '())

(define heap-root car)

(define heap-left cadr)

(define heap-right caddr)

(define heap-is-empty? null?)

(define (heap-insert h f x)
  (if (null? h) (heap-create x (heap-create-empty) (heap-create-empty))
      (let ((h (heap-root h)))
        (if (f x h) (heap-create x (heap-right h) (heap-insert (heap-left h) f h))
            (heap-create h (heap-right h) (heap-insert (heap-left) f x))))))

(define (list->heap xs f)
  (heap-insert (heap-create) xs f))

所以我在上面发布了我当前的代码,我认为我离我更近了,但是我收到的输出有问题'(#<procedure:<> () ())当我调用(list->heap '(3 1 5 9 8 2 7 4 6) <)

1 个答案:

答案 0 :(得分:1)

This question通过交替使用左右子树来构建堆:当插入新元素时,make(min old_root new_value)根,使旧正确子树为新 left 子树,并将(max old_root new_value)插入旧左子树,并使成为新的子树。 (其中min和max假设&lt;是谓词)。

插入总是在右子树中完成,但每次交换子树时,左右子树的高度将保持平衡。由于堆属性不需要保留元素的顺序,因此这很好。

例如,将序列1 2 3 4 5 1和5 4 3 2 1 6插入最小堆中:

        1                     5

        1                     4                   
       / \                   / \
          2                     5

         1                    3
        / \                  / \ 
       2   3                5   4

         1                    2
        / \                  / \
       3   2                4   3
      /\  / \              /\    \
             4                    5

         1                    1
        / \                  / \
       2   3                3   2
      / \   \              / \   \
         4   5                5   4

          1                    1
        /   \                 / \
       3     1               2   3 
      /\    / \             /\   /\
        5  4   2              4 5  6  

将谓词作为参数finsert可写为:

(define (insert heap f val)
  (if (null? heap)
      (make-heap val '() '())
  (let ((h (first heap)))
    (if (f val h)
        (make-heap val (right heap) (insert (left heap) f h ))
        (make-heap h (right heap) (insert (left heap) f val))))))

(define (make-heap val l r)
  (list val l r))

(define (left heap)
  (cadr heap))

(define (right heap)
  (caddr heap))

另一个“技巧”是使用向量来实现堆,就像racket/data/堆中所做的那样。这样更有效,因为不需要存储指针,并且节点的子节点/父节点的位置是向量中节点索引的函数(参见wikipedia article)。

更新:使用非工作代码进行编辑:

列表顶部的#<procedure:<>元素是<函数(此处的repl输出可能有点令人困惑,实际上procdeure:<<>包围),如

所示
> (define foo (list->heap '(3 1 5 9 8 2 7 4 6) <))
> foo
'(#<procedure:<> () ())
> (car foo)
#<procedure:<>
> ((car foo) 1 2)
#t
> ((car foo) 2 1)
#f

原因是参数以错误的顺序传递:谓词是heap-insert函数的第二个参数。因此,heap-insert使用<作为要插入的值。

关于list->heap,应该递归地逐个插入列表中的元素(参见链接问题的答案)。通过这个定义(交换参数的顺序),它给出了

> (list->heap '(3 1 5 9 8 2 7 4 6) <)
'((3 1 5 9 8 2 7 4 6) () ())

因为这只是

> (heap-insert (heap-create) < '(3 1 5 9 8 2 7 4 6))

> (heap-create '(3 1 5 9 8 2 7 4 6) (heap-create-empty) (heap-create-empty))

相当于

> ( list  '(3 1 5 9 8 2 7 4 6) (heap-create-empty) (heap-create-empty))
'((3 1 5 9 8 2 7 4 6) () ())