Container/ring-golang

2019.05.25

环状双向链表 (无哨兵)

相较于 List , ring 因为不需要哨兵快速找到头尾, 所以只有一个 struct ring (环)

type Ring struct {
    // 前后节点
	next, prev *Ring
	Value      interface{}
}

创建一个环, 使用 New 函数, 创建一个有 n 个容量的环

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
}

同时提供 Len() 函数, 获取当前 ring 的容量

func (r *Ring) Len() int {
	n := 0
	if r != nil {
		n = 1
        // 即时的去遍历一次 ring 获取当前长度
		for p := r.Next(); p != r; p = p.next {
			n++
		}
	}
	return n
}

为在 ring 中移动当前操作的指针, 提供了 move 方法, 支持正向移动和反方向移动 (向左或向右移动)

func (r *Ring) Move(n int) *Ring {
	if r.next == nil {
	    /*
	    避免 a:=&ring.Ring{}
	    然后直接拿来用而没有初始化从而报错的情况
	     */
		return r.init()
	}
	switch {
	case n < 0:
        // 负数加到 0
		for ; n < 0; n++ {
			r = r.prev
		}
	case n > 0:
        // 正数减到 0
		for ; n > 0; n-- {
			r = r.next
		}
	}
	return r
}

也可以给加入环节点或者删除环节点 (类似于)

func (r *Ring) Link(s *Ring) *Ring {
    /**
    ┌-----------------------┐
    ∨                       ∨
    a <---> r <---> b <---> c
            ^
                add s
    ___
    OR
    __
    ┌-----------------------┐
    ∨                       ∨
    a <---> r <---> b <---> c
            ^

    ┌-----------------------┐
    ∨                       ∨
    d <---> f <---> s <---> g
                    ^

    s will add after r
    */

    /*
    ┌-----------------------┐
    ∨              n|       ∨
    a <---> r <---> b <---> c

    ┌-----------------------┐
    ∨                       ∨
    d <---> f <---> s <---> g
     */
	n := r.Next()

	if s != nil {
        /*
        ┌-----------------------┐
        ∨              n|       ∨
        a <---> r <---> b <---> c

        ┌-----------------------┐
        ∨      p|               ∨
        d <---> f <---> s <---> g
        */
		p := s.Prev()

        // Note: Cannot use multiple assignment because
		// evaluation order of LHS is not specified.

		/*
        ┌-----------------------┐
        ∨              n|       ∨
        a <---> r <---- b <---> c
                └----┐
		             |
               p|    └--↓
        d <---> f <---> s <---> g
		^                       ^
        └-----------------------┘
        */
		r.next = s
		/*
        ┌-----------------------┐
        ∨              n|       ∨
        a <---> r <---- b <---> c
                ↑----┐
		             |
               p|    └--↓
        d <---> f ----> s <---> g
		^                       ^
        └-----------------------┘
        */
		s.prev = r
		/*
        ┌-----------------------┐
        ∨              n|       ∨
        a <---> r       b <---> c
                ↑----┐  |
		        ┌----┼--┘
               p|    └--↓
        d <---> f ----> s <---> g
		^                       ^
        └-----------------------┘
        */
		n.prev = p
		/*
        ┌-----------------------┐
        ∨              n|       ∨
        a <---> r       b <---> c
                ↑----┐  ↑
		        ┌----┼--┘
               p↓    └--↓
        d <---> f       s <---> g
		^                       ^
        └-----------------------┘
        */
		p.next = n
		/*
		    ┌-------------------------------------------------------┐
            ∨                                      p|      n|       ∨
            a <---> r <---> s <---> g <---> d <---> f <---> b <---> c
		 */
	}
	return n
}

所以最终是将 r 节点和 s 节点进行链接, 将 r 的后驱节点和 s 的前驱节点进行链接, 组成一个新的双向链表实现的环

而 Unlink() 方法则是 调用的 link 方法并做了一些变化

func (r *Ring) Unlink(n int) *Ring {
	if n <= 0 {
		return nil
	}
	return r.Link(
	    // 获取 r 的 n 个节点之后的那个 b 节点
	    r.Move(n + 1)
	    // 然后将 当前的 r 节点和 b 节点连接起来
	    // 则中间的这些节点相当于从环中被排除掉
    )
}

最后, Ring 还提供一个 Do() 方法, 允许使用闭包函数遍历 整个 Ring

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)
		}
	}
}
Last modified 2019.05.25