goSTL/blog/数据结构STL——golang实现环ring.md

462 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

github仓库存储地址https://github.com/hlccd/goSTL
### 概述
ring是一种**离散的环状线性结构**,即**首尾连接的线性结构**,它是又多个分布在不同物理空间的结点,通过指针链接建立逻辑连接而形成的线性结构。
但不同的地方在于环本身没有首尾结点之分,甚至说,它没有首尾结点,它可以看作是**将链表的首尾结点连接起来**,即任何一个结点都可以看作是首节点也可以看作是尾结点,可以通过对环中任意一个结点向前或向后遍历得到全部结点。
同链表一样,它的所有结点之间都是相互分离的,基本不存在在物理上临接的可能(不绝对),所以它要增加或删除结点的过程也是十分简单的,在添加时建立待添加结点和前后节点的连接即可,删除时建立前后节点的连接即可。适用于频繁删减的情况,或者循环遍历的情况。
### 原理
对于一个环状线性结构来说,主要是要建立其它的连接,相比较于用数组的形式实现来说,数组实现需要分配一段连续的空间用以存储数据,但当数组分配的空间不足时,则需要再重新分配空间并将原有的元素复制过去,考虑到复制数据的时间开销也是不容忽略的,所以需要找到另一个结构去尽量减少复制元素的时间开销。
同时,对于使用数组去实现环状结构来说,它需要通过逻辑上去建立首尾结点的连接,而不是将这两个点连起来,直观上并不是十分友好,特别是在扩容缩容的时候需要做的事情太多了,时间开销太大了,所以本次**实现并不采用数组形式实现**,但**数组形式仍然是可行的**。
#### 添加策略
对于环来说,由于它的结点之间的物理间隔并不是连续的,而是根据需要随机的分布在整个内存中的,同时,对于一个环来说,**环并不存在一个核心结点**,它是任意一个结点都可以当作核心结点去使用。
对于此种特性ring在添加结点时只会出现两种情况
1. 环不存在:创造一个自环的结点并持有即可,自环指自己前后节点均指向自己
2. 环存在:在当前持有的结点后面插入一个结点即可,同时建立新结点和前后节点之间的关系,此时持有结点不变
#### 删除策略
同添加策略类似,考虑到环本身无核心的特性,只需要判断销毁该结点后是否仍有结点即可,有则更换持有结点,否则销毁环即可。
ring删除主要会有以下三种情况
1. 环不存在:结束
2. 环仅有一个结点:销毁环
3. 环有多个结点:销毁当前持有结点,并将持有结点切换为原持有节点的**下一个结点**,同时通过在新持有结点前插入原持有结点的前结点实现连接。
### 实现
ring环结构体包含环的头尾节点指针当增删结点时只需要移动到对应位置进行操作即可当一个节点进行增删时需要同步修改其临接结点的前后指针结构体中记录该环中当前所持有的结点的指针即可同时记录该环中存在多少元素即size使用并发控制锁以保证数据一致性。
```go
type ring struct {
now *node //环当前持有的结点指针
size uint64 //当前存储的元素个数
mutex sync.Mutex //并发控制锁
}
```
环的node节点结构体pre和next是该节点的前后两个节点的指针用以保证环整体是相连的。
```go
type node struct {
data interface{} //结点所承载的元素
pre *node //前结点指针
next *node //后结点指针
}
```
#### 接口
```go
type ringer interface {
Iterator() (i *Iterator.Iterator) //创建一个包含环中所有元素的迭代器并返回其指针
Size() (size uint64) //返回环所承载的元素个数
Clear() //清空该环
Empty() (b bool) //判断该环是否位空
Insert(e interface{}) //向环当前位置后方插入元素e
Erase() //删除当前结点并持有下一结点
Value() (e interface{}) //返回当前持有结点的元素
Set(e interface{}) //在当前结点设置其承载的元素为e
Next() //持有下一节点
Pre() //持有上一结点
}
```
```go
type noder interface {
preNode() (m *node) //返回前结点指针
nextNode() (m *node) //返回后结点指针
insertPre(pre *node) //在该结点前插入结点并建立连接
insertNext(next *node) //在该结点后插入结点并建立连接
erase() //删除该结点,并使该结点前后两结点建立连接
value() (e interface{}) //返回该结点所承载的元素
setValue(e interface{}) //修改该结点承载元素为e
}
```
#### New
新建一个ring环容器并返回初始持有的结点不存在,即为nil初始size为0。
```go
func New() (r *ring) {
return &ring{
now: nil,
size: 0,
mutex: sync.Mutex{},
}
}
```
新建一个自环结点并返回其指针,初始首结点的前后结点指针都为自身。
```go
func newNode(e interface{}) (n *node) {
n = &node{
data: e,
pre: nil,
next: nil,
}
n.pre = n
n.next = n
return n
}
```
#### Iterator
以ring环容器做接收者将ring环容器中所承载的元素放入迭代器中从该结点开始向后遍历获取全部承载的元素。
```go
func (r *ring) Iterator() (i *Iterator.Iterator) {
if r == nil {
r = New()
}
r.mutex.Lock()
//将所有元素复制出来放入迭代器中
tmp := make([]interface{}, r.size, r.size)
//从当前结点开始向后遍历
for n, idx := r.now, uint64(0); n != nil && idx < r.size; n, idx = n.nextNode(), idx+1 {
tmp[idx] = n.value()
}
i = Iterator.New(&tmp)
r.mutex.Unlock()
return i
}
```
以node结点做接收者返回该结点的前结点。
```go
func (n *node) preNode() (pre *node) {
if n == nil {
return
}
return n.pre
}
```
以node结点做接收者返回该结点的后结点。
```go
func (n *node) nextNode() (next *node) {
if n == nil {
return
}
return n.next
}
```
#### Size
以ring环容器做接收者返回该容器当前含有元素的数量。
```go
func (r *ring) Size() (size uint64) {
if r == nil {
r = New()
}
return r.size
}
```
#### Clear
以ring环容器做接收者将该容器中所承载的元素清空将该容器的当前持有的结点置为nil,长度初始为0。
```go
func (r *ring) Clear() {
if r == nil {
r = New()
}
r.mutex.Lock()
//销毁环
r.now = nil
r.size = 0
r.mutex.Unlock()
}
```
#### Empty
以ring环容器做接收者判断该ring环容器是否含有元素该判断过程通过size进行判断,size为0则为true,否则为false。
```go
func (r *ring) Empty() (b bool) {
if r == nil {
r = New()
}
return r.size == 0
}
```
#### Insert
以ring环容器做接收者通过环中当前持有的结点进行添加如果环为建立,则新建一个自环结点设为环,存在持有的结点,则在其后方添加即可。
```go
func (r *ring) Insert(e interface{}) {
if r == nil {
r = New()
}
r.mutex.Lock()
//新建自环结点
n := newNode(e)
if r.size == 0 {
//原本无环,设为新环
r.now = n
} else {
//持有结点,在后方插入
r.now.insertNext(n)
}
r.size++
r.mutex.Unlock()
}
```
以node结点做接收者对该结点插入前结点并建立前结点和该结点之间的连接。
```go
func (n *node) insertPre(pre *node) {
if n == nil || pre == nil {
return
}
pre.next = n
pre.pre = n.pre
if n.pre != nil {
n.pre.next = pre
}
n.pre = pre
}
```
以node结点做接收者对该结点插入后结点并建立后结点和该结点之间的连接。
```go
func (n *node) insertNext(next *node) {
if n == nil || next == nil {
return
}
next.pre = n
next.next = n.next
if n.next != nil {
n.next.pre = next
}
n.next = next
}
```
#### Erase
以ring环容器做接收者先判断是否仅持有一个结点若仅有一个结点,则直接销毁环,否则将当前持有结点设为下一节点,并前插原持有结点的前结点即可。
```go
func (r *ring) Erase() {
if r == nil {
r = New()
}
if r.size == 0 {
return
}
r.mutex.Lock()
//删除开始
if r.size == 1 {
//环内仅有一个结点,销毁环即可
r.now = nil
} else {
//环内还有其他结点,将持有结点后移一位
//后移后将当前结点前插原持有结点的前结点
r.now = r.now.nextNode()
r.now.insertPre(r.now.preNode().preNode())
}
r.size--
r.mutex.Unlock()
}
```
以node结点做接收者销毁该结点同时建立该节点前后节点之间的连接。
```go
func (n *node) erase() {
if n == nil {
return
}
if n.pre == nil && n.next == nil {
return
} else if n.pre == nil {
n.next.pre = nil
} else if n.next == nil {
n.pre.next = nil
} else {
n.pre.next = n.next
n.next.pre = n.pre
}
n = nil
}
```
#### Value
以ring环容器做接收者获取环中当前持有节点所承载的元素若环中持有的结点不存在,直接返回nil。
```go
func (r *ring) Value() (e interface{}) {
if r == nil {
r = New()
}
if r.now == nil {
//无持有结点,直接返回nil
return nil
}
return r.now.value()
}
```
以node结点做接收者返回该结点所要承载的元素。
```go
func (n *node) value() (e interface{}) {
if n == nil {
return nil
}
return n.data
}
```
#### Set
以ring环容器做接收者修改当前持有结点所承载的元素若未持有结点,直接结束即可。
```go
func (r *ring) Set(e interface{}) {
if r == nil {
r = New()
}
if r.now == nil {
return
}
r.mutex.Lock()
r.now.setValue(e)
r.mutex.Unlock()
}
```
以node结点做接收者对该结点设置其承载的元素。
```go
func (n *node) setValue(e interface{}) {
if n == nil {
return
}
n.data = e
}
```
#### Next
以ring环容器做接收者将当前持有的结点后移一位若当前无持有结点,则直接结束。
```go
func (r *ring) Next() {
if r == nil {
r = New()
}
if r.now == nil {
return
}
r.mutex.Lock()
r.now = r.now.nextNode()
r.mutex.Unlock()
}
```
#### Pre
以ring环容器做接收者将当前持有的结点前移一位若当前无持有结点,则直接结束。
```go
func (r *ring) Pre() {
if r == nil {
r = New()
}
if r.size == 0 {
return
}
r.mutex.Lock()
r.now = r.now.preNode()
r.mutex.Unlock()
}
```
### 使用示例
```go
package main
import (
"fmt"
"github.com/hlccd/goSTL/data_structure/ring"
"sync"
)
func main() {
r:=ring.New()
wg:=sync.WaitGroup{}
for i:=0;i<10;i++{
wg.Add(1)
go func(num int) {
r.Insert(num)
wg.Done()
}(i)
}
wg.Wait()
for i:=uint64(0);i<r.Size();i++{
fmt.Println(r.Value())
r.Pre()
}
fmt.Println("ring当前持有结点的元素:",r.Value())
r.Set(-1)
fmt.Println("ring修改有持有的结点元素:",r.Value())
fmt.Println("删除后")
r.Erase()
for i:=r.Iterator();i.HasNext();i.Next(){
fmt.Println(i.Value())
}
}
```
注:由于过程中的增删过程是并发执行的,所以其结果和下方示例并不完全相同
> 2
> 0
> 1
> 5
> 3
> 4
> 7
> 6
> 8
> 9
> ring当前持有结点的元素: 2
> ring修改有持有的结点元素: -1
> 删除后
> 9
> 8
> 6
> 7
> 4
> 3
> 5
> 1
> 0