3.3 container — 容器数据类型:heap、list 和 ring

    这里的堆使用的数据结构是最小二叉树,即根节点比左边子树和右边子树的所有值都小。 go 的堆包只是实现了一个接口,我们看下它的定义:

    可以看出,这个堆结构继承自 sort.Interface, 回顾下 sort.Interface,它需要实现三个方法

    • Len() int
    • Less(i, j int) bool
    • Swap(i, j int)

    加上堆接口定义的两个方法

    • Push(x interface{})
    • Pop() interface{}

    就是说你定义了一个堆,就要实现五个方法,直接拿 package doc 中的 example 做例子:

    1. func (h IntHeap) Len() int { return len(h) }
    2. func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
    3. func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
    4. func (h *IntHeap) Push(x interface{}) {
    5. *h = append(*h, x.(int))
    6. }
    7. func (h *IntHeap) Pop() interface{} {
    8. old := *h
    9. n := len(old)
    10. x := old[n-1]
    11. *h = old[0 : n-1]
    12. return x
    1. h := &IntHeap{2, 1, 5}
    2. heap.Init(h)
    3. heap.Push(h, 3)
    4. heap.Pop(h)

    具体说下内部实现,是使用最小堆,索引排序从根节点开始,然后左子树,右子树的顺序方式。索引布局如下:

    堆内部实现了 down 和 up 函数 : down 函数用于将索引 i 处存储的值 ( 设 i=1, 即小明 ) 与它的左子树 ( 小黑 ) 和右子树 ( 大黄 ) 相比 , 将三者最小的值大黄与小明的位置交换,交换后小明继续与交换后的子树 (heap[9]和 heap[10]) 相比,重复以上步骤,直到小明位置不变。

    假设 heap[11]== 阿花 当从堆中 Pop 一个元素的时候,先把元素和最后一个节点的值 ( 阿花 ) 交换,然后弹出,然后对阿花调用 down,向下保证最小堆。

    链表就是一个有 prev 和 next 指针的数组了。它维护两个 type,( 注意,这里不是 interface)

    1. type Element struct {
    2. next, prev *Element // 上一个元素和下一个元素
    3. list *List // 元素所在链表
    4. Value interface{} // 元素
    5. }
    6. type List struct {
    7. root Element // 链表的根元素
    8. len int // 链表的长度
    9. }

    基本使用是先创建 list,然后往 list 中插入值,list 就内部创建一个 Element,并内部设置好 Element 的 next,prev 等。具体可以看下例子:

    list 对应的方法有:

    1. type Element
    2. func (e *Element) Next() *Element
    3. func (e *Element) Prev() *Element
    4. type List
    5. func New() *List
    6. func (l *List) Back() *Element // 最后一个元素
    7. func (l *List) Front() *Element // 第一个元素
    8. func (l *List) Init() *List // 链表初始化
    9. func (l *List) InsertAfter(v interface{}, mark *Element) *Element // 在某个元素后插入
    10. func (l *List) InsertBefore(v interface{}, mark *Element) *Element // 在某个元素前插入
    11. func (l *List) MoveAfter(e, mark *Element) // 把 e 元素移动到 mark 之后
    12. func (l *List) MoveBefore(e, mark *Element) // 把 e 元素移动到 mark 之前
    13. func (l *List) MoveToBack(e *Element) // 把 e 元素移动到队列最后
    14. func (l *List) MoveToFront(e *Element) // 把 e 元素移动到队列最头部
    15. func (l *List) PushBack(v interface{}) *Element // 在队列最后插入元素
    16. func (l *List) PushBackList(other *List) // 在队列最后插入接上新队列
    17. func (l *List) PushFront(v interface{}) *Element // 在队列头部插入元素
    18. func (l *List) PushFrontList(other *List) // 在队列头部插入接上新队列
    19. func (l *List) Remove(e *Element) interface{} // 删除某个元素

    环的结构有点特殊,环的尾部就是头部,所以每个元素实际上就可以代表自身的这个环。 它不需要像 list 一样保持 list 和 element 两个结构,只需要保持一个结构就行。

    1. type Ring struct {
    2. next, prev *Ring
    3. Value interface{}
    4. }

    ring 提供的方法有

    1. type Ring
    2. func New(n int) *Ring // 初始化环
    3. func (r *Ring) Do(f func(interface{})) // 循环环进行操作
    4. func (r *Ring) Len() int // 环长度
    5. func (r *Ring) Link(s *Ring) *Ring // 连接两个环
    6. func (r *Ring) Move(n int) *Ring // 指针从当前元素开始向后移动或者向前(n 可以为负数)
    7. func (r *Ring) Next() *Ring // 当前元素的下个元素
    8. func (r *Ring) Unlink(n int) *Ring // 从当前元素开始,删除 n 个元素

    导航