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