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

Scheme 方案-插入二进制堆与二进制搜索树,scheme,racket,Scheme,Racket,之前,我做了一个作业练习,在scheme中实现了一个二进制搜索树,现在我正试图将这段代码转换成一个堆。我所要做的是找到将新元素插入堆的正确位置。在二叉搜索树中,我们只需使用一个谓词在树中导航,并与向左或向右的每个节点进行比较 (define (bst-insert bst f x) (cond ((bst-is-empty? bst) (bst-create x (bst-create-empty)

之前,我做了一个作业练习,在scheme中实现了一个二进制搜索树,现在我正试图将这段代码转换成一个堆。我所要做的是找到将新元素插入堆的正确位置。在二叉搜索树中,我们只需使用一个谓词在树中导航,并与向左或向右的每个节点进行比较

(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))
因此,我在上面发布了我当前的代码,我认为我更接近了,但有点不对劲,因为我收到的输出是
”(#heap'(3 1 5 9 8 2 7 4 6)通过交替左、右子树来构建堆:插入新元素时,将(min old_root new_value)设为根,将旧的右子树设为新的左子树,然后插入(max old_root new_value)到旧的左子树中,并使其成为新的右子树(其中min和max假设<是谓词)

插入始终在右子树中进行,但由于每次交换子树,左子树和右子树的高度将保持平衡。由于heap属性不要求保留元素的顺序,因此这很好

例如,在最小堆中插入序列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  
使用谓词作为参数
f
insert
可以写成:

(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))))))

另一个“技巧”是使用向量来实现堆,就像在堆中一样。这更有效,因为不需要存储指针,节点的子节点/父节点的位置是向量中节点索引的函数(请参阅)

更新:响应使用非工作代码的编辑:


#所以我希望你能澄清一些事情:1)我正在阅读您提供的链接以及上面的代码,我想我对新值插入到哪个树中感到困惑。除非我读错了,否则在链接中插入的内容更像是基于与根节点的比较而从左到右的二元搜索树,或者我误解了代码?2)Let对我来说有点困惑,在某些情况下我理解它,在另一些情况下我不完全理解。我不确定(let((h(第一堆))在您提供的代码中到底在做什么。我添加了一个构建堆的示例。关于
let
,它只是将一个值绑定到本地名称。在这种情况下,您可以删除
let
,并将
h
替换为
(第一堆)在两个<代码>中制作堆表达式。增加了一个解释的句子。这能使它更清楚吗?是的,这使得它更清楚。非常感谢你的详细解释。不客气。如果回答了你的问题,请考虑接受答案。
(define (make-heap val l r)
  (list val l r))
(define (left heap)
  (cadr heap))

(define (right heap)
  (caddr heap))
> (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
> (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) () ())