container/heap
#Interface
关于堆的介绍参看Heap。
在Go中实现的是一个最小堆,即每个节点的值总是以该节点为根节点的子树的最小值。同时应当注意到,对于任意一个节点,假设其下表为i
,则其父节点下标为(i - 1) / 2
,其左子节点下标为2 * i + 1
,其右子节点下标为2 * i + 2
。
在heap包中,定义了一个heap.Interface
接口,只要实现该接口的数据结构,都可以作为一个堆来使用。
1 | type Interface interface { |
#down 和 up
heap操作中最重要的两个方法是down
和up
。
down
让一个节点下沉:
1 | func down(h Interface, i0, n int) bool { |
up
让一个节点上升:
1 | func up(h Interface, j int) { |
#Init
1 | func Init(h Interface) { |
#Push
1 | func Push(h Interface, x interface{}) { |
Push
的时候,先把新元素放置在结尾,然后让其上升。
#Pop
1 | func Pop(h Interface) interface{} { |
Pop
的时候,先把根节点和最后一个节点交换位置,然后让新的根节点下降。注意这里down
的第三个参数是n
的值为h.Len() - 1
,因此新的根节点下降的过程中,原先的根节点位于最后的位置,不受影响。
#Remove
1 | func Remove(h Interface, i int) interface{} { |
Remove
操作与Pop
非常类似,先将需要移除的节点和最后一个节点交换位置,然后同时进行down
和up
操作。
#Fix
1 | func Fix(h Interface, i int) { |
当某个节点的值改变后,通过Fix
操作来调整堆的结构。该操作实际上也是同时进行了down
和up
操作。
与先移除旧节点再添加新节点相比,Fix
操作的复杂度要低一些。
container/list
list定义了一个双向链表。
#Element
list的元素定义如下:
1 | type Element struct { |
Element定义了Next()
和Prev()
方法,定义如下:
1 | // Next returns the next list element or nil. |
总的来说,这两个方法的执行逻辑是:
- 如果该元素在某一个链表上,且其前一个(后一个)元素不是链表的根元素,则返回其前一个(后一个)元素
- 其它情况下,即如果该元素不属于任何链表,或者其前一个(后一个)元素为链表的根元素,则返回
nil
(即:链表的根元素只起到一个占位作用,而并不会存储数据值)
#List
通过New()
方法可以创建一个新的链表:
1 | // List represents a doubly linked list. |
可以看到,New()
实际上调用了Init()
方法,主要作用就是初始化一个链表的根元素。
事实上,List是一个首尾相连的环状结构,只不过由于根元素只是起到了占位作用,对外是不可见的,因此对于用户操作来说,看到的是一个链状结构。
其相关操作以及源码都比较简单,不做赘述。下面是一个例子:
1 | package main |
container/ring
ring是一个首尾相连的list。其中每一个元素的定义如下:
1 | type Ring struct { |
通过New
方法可以创建一个特定大小的ring,例如:
1 | r := ring.New(5) |
#Next 和 Prev
Next
和Prev
操作比较简单,不做赘述。
#Len
通过遍历整个ring来统计元素的个数。
#Move
Move
操作用来获取某个元素之前或者之后的某个元素。
1 | func (r *Ring) Move(n int) *Ring { |
#Link 和 Unlink
Link
操作可以将两个ring连接起来,比较简单,不做赘述。
Unlink
可以移除某个元素后面的若干个元素:
1 | func (r *Ring) Unlink(n int) *Ring { |
Unlink
操作实际上是将起始点与移除后的下一个点进行了Link
操作,这样中间需要被移除的点就不再出现在该ring中了。