二叉堆提供了o(lgn) 时间的插入, 删除最小,降级等操作,o(n) 时间的合并操作; 斐波那契堆提供了更优操作时间界限:o(1) 插入, o(lgn) 删除最小, o(lgn) 删除, o(1)合并。 根据算法导论上说,斐波那契堆在删除最小或删除操作被执行次数相比其他操作少得多时,尤为适用。一些图的算法中(计算最小生成树,单源最短路径)作为基本构建块(作为优先队用)。
考虑到斐波那契堆实现的复杂,可能二叉堆在实践上更具可用性。就像rob pike 在 <notes on programming in c>里说:
3. 花哨的算法在 n 很小时通常很慢,而 n 通常很小。花哨算法的常数复杂度很大。除非你确定 n 总是很大,否则不要用花哨算法。
4. 花哨的算法比简单算法更容易出bug,更难实现。尽量使用简单的算法配合简单的数据结构。
以下实现参考:www.cs.princeton.edu/~wayne/teaching/fibonacci-heap.pdf, 算法导论第19章节。
fiboheap.go
package main import ( "container/ring" "log" "unsafe" ) type FiboNode struct { mark bool //记录节点是否刚刚失去第二个孩子,降级操作使用此属性 degree int //记录下面挂接几个孩子 child, parent *FiboNode //child指向孩子链表,parent指向自己的父节点 ring.Ring //借用标准库已有的ring 实现 } // 客户提供节点比较函数实现,为了执行删除节点操作,比较函数实现,假定nil 值 小于 非nil 值。 type Less func(a, b interface{}) bool //类似c语言中常用的hack手段,匿名字段类型的方法提升到外部类型 func container_of(in *ring.Ring, off uintptr) *FiboNode { f := (*FiboNode)(unsafe.Pointer(uintptr(unsafe.Pointer(in)) - off)) return f } func (r *FiboNode) link(s *FiboNode) *FiboNode { off := unsafe.Offsetof(r.Ring) return container_of(r.Link(&s.Ring), off) } func (r *FiboNode) unlink(n int) *FiboNode { off := unsafe.Offsetof(r.Ring) return container_of(r.Unlink(n), off) } func (r *FiboNode) prev() *FiboNode { off := unsafe.Offsetof(r.Ring) return container_of(r.Prev(), off) } func (r *FiboNode) next() *FiboNode { off := unsafe.Offsetof(r.Ring) return container_of(r.Next(), off) } func (r *FiboNode) move(n int) *FiboNode { off := unsafe.Offsetof(r.Ring) return container_of(r.Move(n), off) } //n 个节点fibonacci heap 度数上界是 math.floor(math.log(n, 1.618)) //所以预分配了64 大小数组,最多容纳节点数为 math.pow(1.618, 64) //省去每次进入consolidate 操作重新分配 type FiboHeap struct { min *FiboNode //记录当前最小节点 n int //heap 中节点的个数 degtab [64]*FiboNode //记录根表中节点的度数 less Less //客户提供比较函数实现 } func New(less Less) *FiboHeap { if less == nil { return nil } return &FiboHeap{less: less} } func (fh *FiboHeap) Insert(val interface{}) *FiboNode { fn := &FiboNode{Ring: ring.Ring{Value: val}} if fh.min == nil { fh.min = fn } else { fh.min.link(fn) if fh.less(val, fh.min.Value) { fh.min = fn } } fh.n++ return fn } func Join(fh, oh *FiboHeap, less Less) *FiboHeap { if fh == nil && oh == nil { return nil } else if fh == nil && oh != nil { return oh } else if fh != nil && oh == nil { return fh } else { nh := New(less) if fh.min != nil && oh.min == nil { nh.min = fh.min nh.n = fh.n } else if fh.min == nil && oh.min != nil { nh.min = oh.min nh.n = oh.n } else if fh.min != nil && oh.min != nil { fh.min.link(oh.min) if less(oh.min.Value, fh.min.Value) { nh.min = oh.min } else { nh.min = fh.min } nh.n = fh.n + oh.n } return nh } } func (fh *FiboHeap) DelMin() *FiboNode { z := fh.min if z != nil { first := z.child //log.Println(first, " ", z.degree, " ", off) for i := z.degree; i > 0; i-- { c := first.unlink(1) c.parent = nil z.link(c) } z.child = nil z.degree = 0 //fh.min.Do(func( v interface{}){ log.Println(v) }) th := z.prev() //log.Printf("th = %v, z = %v\n", th, z) if th == z { fh.min = nil //log.Println("heap will become empty") } else { th.unlink(1) fh.min = th //log.Printf("fh.min = %v, z = %v\n", fh.min, z) consolidate(fh) } fh.n-- } return z } func heapLink(y, x *FiboNode) { y.Prev().Unlink(1) y.parent = x if x.child == nil { x.child = y } else { x.child.link(y) } x.degree++ y.mark = false } func consolidate(fh *FiboHeap) { n := len(fh.degtab) for i := 0; i < n; i++ { fh.degtab[i] = nil } rootList := make([]*FiboNode, 0) rootList = append(rootList, fh.min) for x := fh.min.next(); x != fh.min; x = x.next() { rootList = append(rootList, x) } for _, x := range rootList { /*log.Printf("in range x=%v\n", x.Value) if x.child != nil { x.child.Do(func(v interface{}){log.Println(v)}) }*/ d := x.degree tab := fh.degtab[:] for tab[d] != nil { y := tab[d] if fh.less(y.Value, x.Value) { x, y = y, x } heapLink(y, x) //log.Printf("y=%v, x=%v\n", y, x) tab[d] = nil d++ } tab[d] = x } //log.Printf("after, fh.degtab=%v\n", fh.degtab) for _, n := range fh.degtab { if n != nil { fh.min = n break } } min := fh.min for n := fh.min.next(); n != fh.min; n = n.next() { if fh.less(n.Value, min.Value) { min = n } } fh.min = min } func (fh *FiboHeap) DecreaseKey(n *FiboNode, val interface{}) { if fh.less(val, n.Value) { n.Value = val p := n.parent if p != nil && fh.less(n.Value, p.Value) { fh.cut(n, p) fh.cascadingCut(p) } if fh.less(n.Value, fh.min.Value) { fh.min = n } } } func (fh *FiboHeap) cut(n, p *FiboNode) { if n.prev() == n { p.child = nil } else { n.Prev().Unlink(1) } p.degree-- fh.min.link(n) n.parent = nil n.mark = false } func (fh *FiboHeap) cascadingCut(x *FiboNode) { y := x.parent if y != nil { if x.mark == false { x.mark = true } else { fh.cut(x, y) fh.cascadingCut(y) } } } //删除操作,通过把当前节点值设为nil, 比较函数让 nil 小于 非 nil,正常节点不应包含nil值, //降级后, 删除最小 func (fh *FiboHeap) DelNode(n *FiboNode) *FiboNode { fh.DecreaseKey(n, nil) return fh.DelMin() } func (fh *FiboHeap) Walk() { if fh.min == nil { return } heapVisit(fh.min) for c := fh.min.next(); c != fh.min; c = c.next() { heapVisit(c) } } func heapVisit(c *FiboNode) { if c == nil { return } log.Printf("value:%v degree:%v mark:%v\n", c.Value, c.degree, c.mark) if c.child != nil { heapVisit(c.child) for n := c.child.next(); n != c.child; n = n.next() { heapVisit(n) } } }
简单测试:(可能用testing package 最好) fibotest.go
package main import ( "fmt" "math/rand" "time" ) func main() { less := func(a, b interface{}) bool { if a == nil && b != nil { return true } else if a != nil && b == nil { return false } else if a == nil && b == nil { return false } return a.(int) < b.(int) } less1 := func(a, b interface{}) bool { if a != nil && b == nil { return false } else if a == nil && b != nil { return true } else if a == nil && b == nil { return false } return a.(float64) < b.(float64) } _ = less1 rand.Seed(time.Now().UnixNano()) fh := New(less) //arr := []int{661,704,997,400,917,278,508,817,561,660} for i := 0; i < 1000; i++ { //n := arr[i] n := rand.Intn(100000) fh.Insert(n) } //fmt.Println("heap size ", fh.n) /* fh.Insert(9.32) a := fh.Insert(10.439) fh.DecreaseKey(a, 7.890) fh.Insert(8.43439) fh.Insert(100.1) b := fh.Insert(1000.88) fh.Insert(5.324) fh.DelNode(b) fh.Insert(3.6789)*/ for i := 0; i < 1000; i++ { fmt.Println(fh.DelMin().Value) //fh.Walk() } }
go build -o fibotest fibotest.go fiboheap.go