Golang 的原生实现容器似乎不如 Java 的原生种类那么多,所以有些常见的容器可以直接使用 GoDS,目前原生实现的有堆、列表、环,下面逐一介绍。
也可以直接参考 容器数据类型:heap、list 和 ring
heap
标准库 heap 包中没有可以导出的已经实现好的堆,需要实现 heap.Interface 以得到我们自己的堆,以文件 container/heap/example_intheap_test.go
为例,看看最小堆是如何实现的
// An IntHeap is a min-heap of ints.
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
前三个方法是 sort.Interface
的接口,后两个是堆独特的。不论最大堆还是最小堆,都是用数组保存的,同时需要比较与排序的功能。同时堆的接口定义的方法并不是由调用者直接调用的,使用入堆和出堆需要使用 heap.Push(h Interface, x interface{})
和 heap.Pop(h Interface) interface{}
// Note that Push and Pop in this interface are for package heap's
// implementation to call. To add and remove things from the heap,
// use heap.Push and heap.Pop.
那么实际上两个方法的实现是为了维护数组,我们查看上述最小堆的实现代码可以知道:压入加在数组最后,之后会调用 up(h Interface, j int)
上浮到堆的合适位置,弹出会把堆根的数与数组最后一个交换,然后数组长度减一,堆根的数调用 down(h Interface, i0, n int)
下沉到堆的合适位置。
list
注释直白地介绍了,list 包实现了一个双向链表,需要遍历链表就如下使用,也是非常的直观清晰。
// Package list implements a doubly linked list.
//
// To create a new list
l := list.New()
// To iterate over a list (where l is a *List):
for e := l.Front(); e != nil; e = e.Next() {
// do something with e.Value
}
看一下这个双向链表是如何构造的,分为 Element 和 List,也就是构成链表的基本元素和链表本身(如果自己实现一个,也应该分为这两部分)
- Element
- 存放了前一个和后一个元素的指针,因为这是一个双向链表
- 当前元素所属的具体链表(可能考虑到不同链表会使用含有同样值的元素)
- 具体的值,使用
interface{}
接口表明可以存储任何值
List
- 链表的根节点
除去根节点的链表总长度 ```go // Element is an element of a linked list. type Element struct { // Next and previous pointers in the doubly-linked list of elements. // To simplify the implementation, internally a list l is implemented // as a ring, such that &l.root is both the next element of the last // list element (l.Back()) and the previous element of the first list // element (l.Front()). next, prev *Element
// The list to which this element belongs. list *List
// The value stored with this element. Value interface{} }
// List represents a doubly linked list. // The zero value for List is an empty list ready to use. type List struct { root Element // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element }
对外暴露可用方法,功能就不一一列出了,看名称与注释可得。
| 方法名 | 返回值 |
| --- | --- |
| Init() | *List |
| Len() | int |
| Front() | *Element |
| Back() | *Element |
| Remove(e *Element) | interface{} |
| PushFront(v interface{}) | *Element |
| PushBack(v interface{}) | *Element |
| InsertBefore(v interface{}, mark *Element) | *Element |
| InsertAfter(v interface{}, mark *Element) | *Element |
| MoveToFront(e *Element) | / |
| MoveToBack(e *Element) | / |
| MoveBefore(e, mark *Element) | / |
| MoveAfter(e, mark *Element) | / |
| PushBackList(other *List) | / |
| PushFrontList(other *List) | / |
<a name="7TSEz"></a>
## ring
与双向链表的区别为环的每个元素都代表环自身,没有头和尾之分,所以只需要维护一个结构即可。
```go
// A Ring is an element of a circular list, or ring.
// Rings do not have a beginning or end; a pointer to any ring element
// serves as reference to the entire ring. Empty rings are represented
// as nil Ring pointers. The zero value for a Ring is a one-element
// ring with a nil Value.
//
type Ring struct {
next, prev *Ring
Value interface{} // for use by client; untouched by this library
}