Go基础:ring的介绍与使用

1,684 阅读3分钟

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不能为空。

合并时,如果rs指向同一个环的不同元素,合并它俩将会移除rs之间的元素。被移除的元素将会形成一个新的子环,由于返回的元素是原来的r.Next(),所以返回的元素将会引用新的子环。如果没有元素被移除,那么r.Next()的值不会发生改变,也就是说,当s刚好是r的后续节点时,合并操作不会使环发生任何变化。

可能看说明不是很好理解合并环自身的时候是如何删除元素,以及如何形成子环的,下面我们通过两张图来展示一下。

首先,环的原始状态是如下图所示这样的,两个元素节点分别是rsr.Next()n节点,s.Prev()p节点。为了方便理解,我们仅画出了next指针的线条,prevnext刚好相反,就不画了。

1.png

执行完Link()方法合并后,环的状态变成了这个样子:

2.png

如上图所示,节点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
}