【问题标题】:Scheme - Inserting into Binary Heap vs Binary Search Tree方案 - 插入二叉堆与二叉搜索树
【发布时间】:2018-02-24 17:57:41
【问题描述】:

以前,我在一个家庭作业中完成了一个在方案中实现二叉搜索树的练习,现在我正在尝试将此代码转换为堆。我正在努力寻找将新元素插入堆的正确位置。在二叉搜索树中,我们只需使用谓词在树中导航,比较向左或向右的每个节点。

(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) <) 时出现了问题

【问题讨论】:

    标签: scheme racket


    【解决方案1】:

    This question 通过交替左右子树来构建堆:插入新元素时,将 (min old_root new_value) 设为根,将旧 right 子树设为新 left 子树,并将 (max old_root new_value) 插入 旧左 子树,并使其成为 新右 子树。 (其中 min 和 max 假设

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

    例如,在最小堆中插入序列 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))))))
    

    (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 使用 &lt; 作为要插入的值。

    关于list-&gt;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) () ())
    

    【讨论】:

    • 所以我希望你能澄清一些事情:1)我正在阅读你提供的链接以及上面的代码,我想我对它如何交替使用新的树感到困惑值被插入。除非我读错了,否则在链接中它被插入更像是一个二叉搜索树,基于与根节点的比较而从左到右,或者我误解了代码? 2) Let has 并且在某些情况下我仍然有点困惑,我理解它,在其他情况下我没有完全理解。我不确定 (let ((h (first heap)) 在您提供的代码中到底在做什么。
    • 我添加了一个构建堆的示例。关于let,它只是将一个值绑定到一个本地名称。在这种情况下,您可以在两个make-heap 表达式中删除let 并将h 替换为(first heap)
    • 加了一句解释。这是否更清楚?
    • 是的,这使它更清晰。非常感谢您的详细解释。
    • 不客气。如果它回答了您的问题,请考虑接受答案。
    猜你喜欢
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    • 1970-01-01
    相关资源
    最近更新 更多