type Ring struct {
next, prev *Ring
Value interface{} // for use by client; untouched by this library
}
func (r *Ring) init() *Ring {
r.next = r
r.prev = r
return r
}
// Next returns the next ring element. r must not be empty.
func (r *Ring) Next() *Ring {
if r.next == nil {
return r.init()
}
return r.next
}
// Prev returns the previous ring element. r must not be empty.
func (r *Ring) Prev() *Ring {
if r.next == nil {
return r.init()
}
return r.prev
}
和官方 SDK 定义的双向链表相比,其实非常相似,Ring 这里就可以看成是双向链表里面的 Element,也就是链表中的节点,但是这个 Ring 节点并没有指向一个独立的链表结构体,也就是说,循环链表中并没有定义像双向链表中的 List 的结构体。
循环链表中的的创建:
// New creates a ring of n elements.
func New(n int) *Ring {
if n <= 0 {
return nil
}
r := new(Ring)
p := r
for i := 1; i < n; i++ {
p.next = &Ring{prev: p}
p = p.next
}
p.next = r
r.prev = p
return r
}
创建和双向链表相比也显得十分的简洁,其中参数 n 代表链表元素的个数,最后返回的是创建好的链表的链表首元素指针(这里也是比较罕见的用 new 去创建一个结构体,赶紧拿笔记下来)。
获取链表长度
// Len computes the number of elements in ring r.
// It executes in time proportional to the number of elements.
//
func (r *Ring) Len() int {
n := 0
if r != nil {
n = 1
for p := r.Next(); p != r; p = p.next {
n++
}
}
return n
}
这里其实就是对链表的遍历啦,通过创建一个 n 作为计数器,遍历节点最终计算出链表的长度。那么可以看出来,这个计算链表的时间复杂度是 O(n),如果链表中元素较多的时候,这个操作还是比较耗时的,建议在使用的时候最好在外部把链表的长度先存起来,做修改的时候直接操作外部的值,以免造成额外的开销。
链表中节点的获取(移动)
// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
// in the ring and returns that ring element. r must not be empty.
//
func (r *Ring) Move(n int) *Ring {
if r.next == nil {
return r.init()
}
switch {
case n < 0:
for ; n < 0; n++ {
r = r.prev
}
case n > 0:
for ; n > 0; n-- {
r = r.next
}
}
return r
}
func (r *Ring) Link(s *Ring) *Ring {
n := r.Next()
if s != nil {
p := s.Prev()
// Note: Cannot use multiple assignment because
// evaluation order of LHS is not specified.
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n
}
// Unlink removes n % r.Len() elements from the ring r, starting
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
// The result is the removed subring. r must not be empty.
//
func (r *Ring) Unlink(n int) *Ring {
if n <= 0 {
return nil
}
return r.Link(r.Move(n + 1))
}
这个 Link 方法其实就是把 s 链表接到 r 链表的后面。
在解连接的时候,传入的参数是 n,其实是删除 n % r.Len() 个元素,从 r 的下一个点开始,到 r 往后的第 n 个节点,这里非常巧妙通过 move 指向 r 往后的第 n + 1 个节点,然后将 r 节点和后面的第 n + 1 个节点连接,完成整个删除的操作。
不过,需要注意的是,r 本身不能为空,并且传入参数的 n 如果和链表本身长度相同,那么这个解连接会失效(相当于什么都没做,因为这个时候 n+1 就是自己的下一个节点)。
Do 方法(遍历)
func (r *Ring) Do(f func(interface{})) {
if r != nil {
f(r.Value)
for p := r.Next(); p != r; p = p.next {
f(p.Value)
}
}
}
其实就是实现对循环链表遍历的操作,你可以通过传入 f 函数实现一些定制化的功能。
总结
通过看 ring 的源码,与双向链表相比,我们还是可以发现 ring 更加精简,实现的复杂性明显降低的很多,实现特别简单,但是在计算链表长度时可能会比较耗时,建议在使用循环链表的时候最好在外部把链表的长度先存起来,做修改的时候直接操作外部的值,以免造成额外的开销。