Go基础:ring的介绍与使用
介绍
Go语言提供的内置容器ring
是一个双向循环链表。其源码位于containe
文件夹中的ring
包里。
与list
的区别:虽然二者底层都是采用双向循环链表实现,但ring
没有表头与表尾的概念,ring
的表头与表尾相连,构成一个环。
结构体
ring
的核心结构体为Ring
。环的特点如下:
- 一个
Ring
对象是循环链表或环的一个元素。 - 由
Ring
组成的环没有表头与表尾。 - 指向任何环元素的指针用作整个环的引用。
- 空环用nil环指针标识。
- 环的零值是具有nil值的单元素环。
type Ring struct {
next, prev *Ring
Value interface{}
}
复制代码
方法一览
将ring
的方法主要分为四类:
- 初始化方法
- 基础方法
- 环的合并与拆分
- 环的遍历
初始化
init()
:未导出方法。用于初始化一个环。在Next()
、Prev()
和Move()
方法中被调用。New()
:新建一个具有n
个元素的环。
func (r *Ring) init() *Ring {
r.next = r
r.prev = r
return r
}
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
}
复制代码
基础方法
Next()
:返回下一个元素,当前元素不能为空。Prev()
:返回上一个元素,当前元素不能为空。Move()
:n
小于零时,返回后面第n个元素;n
大于零时,返回前面第n个元素。当前元素不能为空。Len()
:返回环中的元素个数。因为需要遍历环,时间复杂度为O(n)。
func (r *Ring) Next() *Ring {
if r.next == nil {
return r.init()
}
return r.next
}
func (r *Ring) Prev() *Ring {
if r.next == nil {
return r.init()
}
return r.prev
}
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) Len() int {
n := 0
if r != nil {
n = 1
for p := r.Next(); p != r; p = p.next {
n++
}
}
return n
}
复制代码
环的合并与拆分
Link()
:将环s
合并到环r
中,使得r.Next()
指向s
,返回原来的r.Next()
。要求r
不能为空。Unlink()
:从r.Next()
元素开始,从环中移除n
个元素。实际上调用了Link()
方法。返回被移除的子环。要求r
不能为空。
合并时,如果r
与s
指向同一个环的不同元素,合并它俩将会移除r
和s
之间的元素。被移除的元素将会形成一个新的子环,由于返回的元素是原来的r.Next()
,所以返回的元素将会引用新的子环。如果没有元素被移除,那么r.Next()
的值不会发生改变,也就是说,当s
刚好是r
的后续节点时,合并操作不会使环发生任何变化。
可能看说明不是很好理解合并环自身的时候是如何删除元素,以及如何形成子环的,下面我们通过两张图来展示一下。
首先,环的原始状态是如下图所示这样的,两个元素节点分别是r
与s
,r.Next()
为n
节点,s.Prev()
为p
节点。为了方便理解,我们仅画出了next
指针的线条,prev
与next
刚好相反,就不画了。
执行完Link()
方法合并后,环的状态变成了这个样子:
如上图所示,节点r
与节点s
组成的新环已经移除了两个节点之间的其他节点。而被移除的节点形成了一个新的子环,由于返回的节点是原始的r.Next()
,因此返回的节点指向新的子环。
func (r *Ring) Link(s *Ring) *Ring {
n := r.Next()
if s != nil {
p := s.Prev()
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n
}
func (r *Ring) Unlink(n int) *Ring {
if n <= 0 {
return nil
}
return r.Link(r.Move(n + 1))
}
复制代码
遍历环
Do()
:遍历环,对每一个元素进行方法参数f
的操作。
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)
}
}
}
复制代码
使用
- 环的长度
- 环的前后移动及转动
- 环的遍历环的合并与拆分
环的长度
func ExampleRing_Len() {
r := ring.New(4)
fmt.Println(r.Len())
// 输出:
// 4
}
复制代码
环的Next
func ExampleRing_Next() {
r := ring.New(5)
n := r.Len()
for i := 0; i < n; i++ {
r.Value = i
r = r.Next()
}
for j := 0; j < n; j++ {
fmt.Println(r.Value)
r = r.Next()
}
// 输出:
// 0
// 1
// 2
// 3
// 4
}
复制代码
环的Prev
func ExampleRing_Prev() {
r := ring.New(5)
n := r.Len()
for i := 0; i < n; i++ {
r.Value = i
r = r.Next()
}
for j := 0; j < n; j++ {
r = r.Prev()
fmt.Println(r.Value)
}
// 输出:
// 4
// 3
// 2
// 1
// 0
}
复制代码
环的遍历
func ExampleRing_Do() {
r := ring.New(5)
n := r.Len()
for i := 0; i < n; i++ {
r.Value = i
r = r.Next()
}
r.Do(func(p interface{}) {
fmt.Println(p.(int))
})
// Output:
// 0
// 1
// 2
// 3
// 4
}
复制代码
环的转动
func ExampleRing_Move() {
r := ring.New(5)
n := r.Len()
for i := 0; i < n; i++ {
r.Value = i
r = r.Next()
}
r = r.Move(3)
r.Do(func(p interface{}) {
fmt.Println(p.(int))
})
// Output:
// 3
// 4
// 0
// 1
// 2
}
复制代码
环的合并
func ExampleRing_Link() {
r := ring.New(2)
s := ring.New(2)
lr := r.Len()
ls := s.Len()
for i := 0; i < lr; i++ {
r.Value = 0
r = r.Next()
}
for j := 0; j < ls; j++ {
s.Value = 1
s = s.Next()
}
rs := r.Link(s)
rs.Do(func(p interface{}) {
fmt.Println(p.(int))
})
// Output:
// 0
// 0
// 1
// 1
}
复制代码
环的拆分
func ExampleRing_Unlink() {
r := ring.New(6)
n := r.Len()
for i := 0; i < n; i++ {
r.Value = i
r = r.Next()
}
r.Unlink(3)
r.Do(func(p interface{}) {
fmt.Println(p.(int))
})
// Output:
// 0
// 4
// 5
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END