From 8401d68477d0cad0e1b0ad75e962a6503672e926 Mon Sep 17 00:00:00 2001 From: hlccd <56643462+hlccd@users.noreply.github.com> Date: Thu, 21 Oct 2021 22:21:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=8F=8C=E5=90=91?= =?UTF-8?q?=E9=98=9F=E5=88=97deque,=E4=BD=BF=E7=94=A8=E9=93=BE=E8=A1=A8?= =?UTF-8?q?=E5=92=8C=E5=9B=BA=E5=AE=9A=E6=95=B0=E7=BB=84=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?,=E4=BD=BF=E7=94=A8=E5=B9=B6=E5=8F=91=E9=94=81=E4=BF=9D?= =?UTF-8?q?=E8=AF=81=E5=9C=A8=E9=AB=98=E5=B9=B6=E5=8F=91=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E6=95=B0=E6=8D=AE=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- goSTL/data_structure/deque/deque.go | 249 ++++++++++++-------------- goSTL/data_structure/deque/node.go | 263 ++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+), 138 deletions(-) create mode 100644 goSTL/data_structure/deque/node.go diff --git a/goSTL/data_structure/deque/deque.go b/goSTL/data_structure/deque/deque.go index df36d00..e24121e 100644 --- a/goSTL/data_structure/deque/deque.go +++ b/goSTL/data_structure/deque/deque.go @@ -2,62 +2,61 @@ package deque //@Title deque //@Description -// deque双向队列容器包 -// 以切片数组的形式实现 -// 该容器可以在首部和尾部实现线性增减元素 -// 通过interface实现泛型 +// deque双队列容器包 +// 区别于queue的动态数组实现方式,deque采取将数组和链表相结合的方案 +// 该容器既可以在首部增删元素,也可以在尾部增删元素 +// deque在扩容和缩容时,都是固定增加2^10的空间,同时这一部分空间形成链表节点,并串成链表去保存 // 可接纳不同类型的元素 -//@author hlccd 2021-07-6 -//@update hlccd 2021-08-01 增加互斥锁实现并发控制 +// 通过并发控制锁保证了在高并发过程中的数据一致性 + import ( "github.com/hlccd/goSTL/utils/iterator" "sync" ) //deque双向队列结构体 -//包含泛型切片和该切片的首尾指针 -//当删除节点时仅仅需要后移首指针一位即可 -//当剩余长度小于实际占用空间长度的一半时会重新规划以释放掉多余占用的空间 -//当添加节点时若未占满全部已分配空间则尾指针后移一位同时进行覆盖存放 -//当添加节点时尾指针大于已分配空间长度,则新增空间 -//首节点指针始终不能超过尾节点指针 +//包含链表的头尾节点指针 +//当删除节点时通过头尾节点指针进入链表进行删除 +//当一个节点全部被删除后则释放该节点,同时首尾节点做相应调整 +//当添加节点时若未占满节点空间时移动下标并做覆盖即可 +//当添加节点时空间已使用完毕时,根据添加位置新建一个新节点补充上去 type deque struct { - data []interface{} //泛型切片 - begin int //首节点指针 - end int //尾节点指针 - mutex sync.Mutex //并发控制锁 + first *node //链表首节点指针 + last *node //链表尾节点指针 + size uint64 //当前存储的元素个数 + mutex sync.Mutex //并发控制锁 } //deque双向队列容器接口 //存放了deque容器可使用的函数 //对应函数介绍见下方 -type dequeer interface { - Iterator() *Iterator.Iterator //返回一个包含deque中所有使用元素的迭代器 - Size() (num int) //返回该双向队列中元素的使用空间大小 - Clear() //清空该双向队列 - Empty() (b bool) //判断该双向队列是否为空 - PushFront(e interface{}) //将元素e添加到该队列首部 - PushBack(e interface{}) //将元素e添加到该队列末尾 - PopFront() (e interface{}) //将该队列首元素弹出并返回 - PopBack() (e interface{}) //将该队列尾元素弹出并返回 - Front() (e interface{}) //获取该队列首元素 - Back() (e interface{}) //获取该队列尾元素 + +type dequer interface { + Iterator() (i *Iterator.Iterator) //返回包含双向队列中所有元素的迭代器 + Size() (size uint64) //返回该双向队列中元素的使用空间大小 + Clear() //清空该双向队列 + Empty() (b bool) //判断该双向队列是否为空 + PushFront(e interface{}) //将元素e添加到该双向队列的首部 + PushBack(e interface{}) //将元素e添加到该双向队列的尾部 + PopFront() //将该双向队列首元素弹出 + PopBack() //将该双向队列首元素弹出 + Front() (e interface{}) //获取该双向队列首部元素 + Back() (e interface{}) //获取该双向队列尾部元素 } //@title New //@description // 新建一个deque双向队列容器并返回 -// 初始deque的切片数组为空 -// 初始deque的首尾指针均置零 -//@author hlccd 2021-07-6 +// 初始deque双向队列的链表首尾节点为nil +// 初始size为0 //@receiver nil //@param nil //@return d *deque 新建的deque指针 func New() *deque { return &deque{ - data: make([]interface{}, 0, 0), - begin: 0, - end: 0, + first: nil, + last: nil, + size: 0, mutex: sync.Mutex{}, } } @@ -65,23 +64,21 @@ func New() *deque { //@title Iterator //@description // 以deque双向队列容器做接收者 -// 将deque队列容器中不使用空间释放掉 -// 返回一个包含容器中所有使用元素的迭代器 -//@auth hlccd 2021-07-6 +// 将deque双向队列容器中所承载的元素放入迭代器中 +// 节点的冗余空间不释放 //@return d *deque 接收者的deque指针 //@param nil //@return i *iterator.Iterator 新建的Iterator迭代器指针 func (d *deque) Iterator() (i *Iterator.Iterator) { if d == nil { - return Iterator.New(make([]interface{}, 0, 0)) + d = New() } - d.mutex.Lock() - d.data = d.data[d.begin:d.end] - d.begin = 0 - d.end = len(d.data) - i = iterator.New(d.data) - d.mutex.Unlock() - return i + tmp := make([]interface{}, 0, d.size) + //遍历链表的所有节点,将其中承载的元素全部复制出来 + for m := d.first; m != nil; m = m.nextNode() { + tmp = append(tmp, m.value()...) + } + return Iterator.New(&tmp) } //@title Size @@ -89,35 +86,34 @@ func (d *deque) Iterator() (i *Iterator.Iterator) { // 以deque双向队列容器做接收者 // 返回该容器当前含有元素的数量 // 该长度并非实际占用空间数量 -// 当容器为nil时返回-1 -//@auth hlccd 2021-07-6 +// 若容器为空则返回0 //@return d *deque 接收者的deque指针 //@param nil -//@return num int 容器中实际使用元素所占空间大小 -func (d *deque) Size() (num int) { +//@return size uint64 容器中实际使用元素所占空间大小 +func (d *deque) Size() (size uint64) { if d == nil { - return -1 + d = New() } - return d.end - d.begin + return d.size } //@title Clear //@description // 以deque双向队列容器做接收者 // 将该容器中所承载的元素清空 -// 将该容器的首尾指针均置0 -//@auth hlccd 2021-07-6 +// 将该容器的首尾指针均置nil,将size重置为0 //@return d *deque 接收者的deque指针 //@param nil //@return nil func (d *deque) Clear() { if d == nil { + d = New() return } d.mutex.Lock() - d.data = d.data[0:0] - d.begin = 0 - d.end = 0 + d.first = nil + d.last = nil + d.size = 0 d.mutex.Unlock() } @@ -128,168 +124,145 @@ func (d *deque) Clear() { // 如果含有元素则不为空,返回false // 如果不含有元素则说明为空,返回true // 如果容器不存在,返回true -// 该判断过程通过首尾指针数值进行判断 -// 当尾指针数值等于首指针时说明不含有元素 -// 当尾指针数值大于首指针时说明含有元素 -//@auth hlccd 2021-07-6 +// 该判断过程通过size进行判断,为0则为true,否则为false //@return d *deque 接收者的deque指针 //@param nil //@return b bool 该容器是空的吗? -func (d *deque) Empty() bool { +func (d *deque) Empty() (b bool) { if d == nil { - return true + d = New() } - return d.Size() <= 0 + return d.Size() == 0 } //@title PushFront //@description -// 以deque双向队列容器做接收者 +// 以deque双向队列向量容器做接收者 // 在容器首部插入元素 -// 若首指针大于0,则对当前指针位置进行覆盖,同时首指针前移一位 -// 若首指针等于0,重新分配空间并将首指针置0尾指针设为总长度 -//@auth hlccd 2021-07-6 +// 通过链表首节点进行添加 //@return d *deque 接收者的deque指针 -//@param e interface{} 待插入首部的元素 +//@param e interface{} 待插入元素 //@return nil func (d *deque) PushFront(e interface{}) { if d == nil { - return + d = New() } d.mutex.Lock() - if d.begin > 0 { - d.begin-- - d.data[d.begin] = e - } else { - d.data = append(append([]interface{}{}, e), d.data[:d.end]...) - d.begin = 0 - d.end = len(d.data) + d.size++ + //通过首节点进行添加 + if d.first == nil { + d.first = createFirst() + d.last = d.first } + d.first = d.first.pushFront(e) d.mutex.Unlock() } //@title PushBack //@description -// 以deque双向队列容器做接收者 +// 以deque双向队列向量容器做接收者 // 在容器尾部插入元素 -// 若尾指针小于切片实际使用长度,则对当前指针位置进行覆盖,同时尾指针后移一位 -// 若尾指针等于切片实际使用长度,则新增切片长度同时使尾指针后移一位 -//@auth hlccd 2021-07-6 +// 通过链表尾节点进行添加 //@return d *deque 接收者的deque指针 -//@param e interface{} 待插入尾部的元素 +//@param e interface{} 待插入元素 //@return nil func (d *deque) PushBack(e interface{}) { if d == nil { - return + d = New() } d.mutex.Lock() - if d.end < len(d.data) { - d.data[d.end] = e - } else { - d.data = append(d.data, e) + d.size++ + //通过尾节点进行添加 + if d.last == nil { + d.last = createLast() + d.first = d.last } - d.end++ + d.last = d.last.pushBack(e) d.mutex.Unlock() } //@title PopFront //@description // 以deque双向队列容器做接收者 -// 弹出容器第一个元素,同时首指针后移一位 -// 当剩余元素数量小于容器切片实际使用空间的一半时,重新分配空间释放未使用部分 -// 若容器为空,则不进行弹出 -//@auth hlccd 2021-07-6 +// 利用首节点进行弹出元素,可能存在首节点全部释放要进行首节点后移的情况 +// 当元素全部删除后,释放全部空间,将首尾节点都设为nil //@return d *deque 接收者的deque指针 //@param nil -//@return e interface{} 队尾元素 -func (d *deque) PopFront() (e interface{}) { +//@return nil +func (d *deque) PopFront() { if d == nil { - return nil + d = New() } - if d.Empty() { - return nil + if d.size == 0 { + return } d.mutex.Lock() - e = d.data[d.begin] - d.begin++ - if d.begin*2 >= d.end { - d.data = d.data[d.begin:d.end] - d.begin = 0 - d.end = len(d.data) + //利用首节点删除首元素 + //返回新的首节点 + d.first = d.first.popFront() + d.size-- + if d.size == 0 { + //全部删除完成,释放空间,并将首尾节点设为nil + d.first = nil + d.last = nil } d.mutex.Unlock() - return e } //@title PopBack //@description // 以deque双向队列容器做接收者 -// 弹出容器最后一个元素,同时尾指针前移一位 -// 当剩余元素数量小于容器切片实际使用空间的一半时,重新分配空间释放未使用部分 -// 若容器为空,则不进行弹出 -//@auth hlccd 2021-07-6 +// 利用尾节点进行弹出元素,可能存在尾节点全部释放要进行尾节点前移的情况 +// 当元素全部删除后,释放全部空间,将首尾节点都设为nil //@return d *deque 接收者的deque指针 //@param nil -//@return e interface{} 队首元素 -func (d *deque) PopBack() (e interface{}) { +//@return nil +func (d *deque) PopBack() { if d == nil { - return nil + d = New() } - if d.Empty() { - return nil + if d.size == 0 { + return } d.mutex.Lock() - d.end-- - e = d.data[d.end] - if d.begin*2 >= d.end { - d.data = d.data[d.begin:d.end] - d.begin = 0 - d.end = len(d.data) + //利用尾节点删除首元素 + //返回新的尾节点 + d.last = d.last.popBack() + d.size-- + if d.size == 0 { + //全部删除完成,释放空间,并将首尾节点设为nil + d.first = nil + d.last = nil } d.mutex.Unlock() - return e } //@title Front //@description // 以deque双向队列容器做接收者 -// 返回该容器的第一个元素 +// 返回该容器的第一个元素,利用首节点进行寻找 // 若该容器当前为空,则返回nil -//@auth hlccd 2021-07-6 //@return d *deque 接收者的deque指针 //@param nil //@return e interface{} 容器的第一个元素 func (d *deque) Front() (e interface{}) { if d == nil { - return nil + d = New() } - if d.Empty() { - return nil - } - d.mutex.Lock() - e = d.data[d.begin] - d.mutex.Unlock() - return e + return d.first.front() } //@title Back //@description // 以deque双向队列容器做接收者 -// 返回该容器的最后一个元素 +// 返回该容器的最后一个元素,利用尾节点进行寻找 // 若该容器当前为空,则返回nil -//@auth hlccd 2021-07-6 //@return d *deque 接收者的deque指针 //@param nil //@return e interface{} 容器的最后一个元素 func (d *deque) Back() (e interface{}) { if d == nil { - return + d = New() } - if d.Empty() { - return nil - } - d.mutex.Lock() - e = d.data[d.end-1] - d.mutex.Unlock() - return e + return d.last.back() } diff --git a/goSTL/data_structure/deque/node.go b/goSTL/data_structure/deque/node.go new file mode 100644 index 0000000..1fe3127 --- /dev/null +++ b/goSTL/data_structure/deque/node.go @@ -0,0 +1,263 @@ +package deque + +//@Title deque +//@Description +// deque双队列容器包 +// 该部分包含了deque双向队列中链表节点 +// 链表的增删都通过节点的增删完成 +// 节点空间全部使用或全部废弃后将进行节点增删 +// 增删之后会返回对应的首尾节点以辅助deque容器仍持有首尾节点 +// 为保证效率问题,设定了一定的冗余量,即每个节点设定2^10的空间以存放元素 + +//deque双向队列中链表的node节点结构体 +//包含一个2^10空间的固定数组用以承载元素 +//使用begin和end两个下标用以表示新增的元素的下标,由于begin可能出现-1所以不选用uint16 +//pre和next是该节点的前后两个节点 +//用以保证链表整体是相连的 +type node struct { + data [1024]interface{} //用于承载元素的股东数组 + begin int16 //该结点在前方添加结点的下标 + end int16 //该结点在后方添加结点的下标 + pre *node //该结点的前一个结点 + next *node //该节点的后一个结点 +} + +//deque双向队列容器接口 +//存放了deque容器可使用的函数 +//对应函数介绍见下方 +type noder interface { + nextNode() (m *node) //返回下一个结点 + preNode() (m *node) //返回上一个结点 + value() (es []interface{}) //返回该结点所承载的所有元素 + pushFront(e interface{}) (first *node) //在该结点头部添加一个元素,并返回新首结点 + pushBack(e interface{}) (last *node) //在该结点尾部添加一个元素,并返回新尾结点 + popFront() (first *node) //弹出首元素并返回首结点 + popBack() (last *node) //弹出尾元素并返回尾结点 + front() (e interface{}) //返回首元素 + back() (e interface{}) //返回尾元素 +} + +//@title createFirst +//@description +// 新建一个冗余在前方的首结点并返回其指针 +// 初始首结点的begin为1023,end为1024 +// 该结点的前后结点均置为nil +//@receiver nil +//@param nil +//@return n *node 新建的node指针 +func createFirst() (n *node) { + return &node{ + data: [1024]interface{}{}, + begin: 1023, + end: 1024, + pre: nil, + next: nil, + } +} + +//@title createLast +//@description +// 新建一个冗余在后方的尾结点并返回其指针 +// 初始首结点的begin为-1,end为0 +// 该结点的前后结点均置为nil +//@receiver nil +//@param nil +//@return n *node 新建的node指针 +func createLast() (n *node) { + return &node{ + data: [1024]interface{}{}, + begin: -1, + end: 0, + pre: nil, + next: nil, + } +} + +//@title nextNode +//@description +// 以node结点做接收者 +// 返回该结点的后一个结点 +// 如果n为nil则返回nil +//@receiver n *node 接收者的node指针 +//@param nil +//@return m *node n结点的下一个结点m的指针 +func (n *node) nextNode() (m *node) { + if n == nil { + return nil + } + return n.next +} + +//@title preNode +//@description +// 以node结点做接收者 +// 返回该结点的前一个结点 +// 如果n为nil则返回nil +//@receiver n *node 接收者的node指针 +//@param nil +//@return m *node n结点的上一个结点m的指针 +func (n *node) preNode() (m *node) { + if n == nil { + return nil + } + return n.pre +} + +//@title value +//@description +// 以node结点做接收者 +// 返回该结点所承载的所有元素 +// 根据其begin和end来获取其元素 +// 当该结点为nil时返回[]而非nil +//@receiver n *node 接收者的node指针 +//@param nil +//@return es []interface{} 该结点所承载的所有元素 +func (n *node) value() (es []interface{}) { + es = make([]interface{}, 0, 0) + if n == nil { + return es + } + if n.begin > n.end { + return es + } + es = n.data[n.begin+1 : n.end] + return es +} + +//@title pushFront +//@description +// 以node结点做接收者 +// 向该节点前方添加元素e +// 当该结点空间已经使用完毕后,新建一个结点并将新结点设为首结点 +// 将插入元素放入新结点并返回新结点作为新的首结点 +// 否则插入当前结点并返回当前结点,首结点不变 +//@receiver n *node 接收者的node指针 +//@param e interface{} 待插入元素 +//@return first *node 首节点指针 +func (n *node) pushFront(e interface{}) (first *node) { + if n == nil { + return n + } + if n.begin >= 0 { + //该结点仍有空间可用于承载元素 + n.data[n.begin] = e + n.begin-- + return n + } + //该结点无空间承载,创建新的首结点用于存放 + m := createFirst() + m.data[m.begin] = e + m.next = n + n.pre = m + m.begin-- + return m +} + +//@title pushBack +//@description +// 以node结点做接收者 +// 向该节点后方添加元素e +// 当该结点空间已经使用完毕后,新建一个结点并将新结点设为尾结点 +// 将插入元素放入新结点并返回新结点作为新的尾结点 +// 否则插入当前结点并返回当前结点,尾结点不变 +//@receiver n *node 接收者的node指针 +//@param e interface{} 待插入元素 +//@return last *node 尾节点指针 +func (n *node) pushBack(e interface{}) (last *node) { + if n == nil { + return n + } + if n.end < int16(len(n.data)) { + //该结点仍有空间可用于承载元素 + n.data[n.end] = e + n.end++ + return n + } + //该结点无空间承载,创建新的尾结点用于存放 + m := createLast() + m.data[m.end] = e + m.pre = n + n.next = m + m.end++ + return m +} + +//@title popFront +//@description +// 以node结点做接收者 +// 利用首节点进行弹出元素,可能存在首节点全部释放要进行首节点后移的情况 +// 当发生首结点后移后将会返回新首结点,否则返回当前结点 +//@receiver n *node 接收者的node指针 +//@param nil +//@return first *node 首节点指针 +func (n *node) popFront() (first *node) { + if n == nil { + return nil + } + if n.begin < int16(len(n.data))-2 { + //该结点仍有承载元素 + n.begin++ + n.data[n.begin] = nil + return n + } + if n.next != nil { + //清除该结点下一节点的前结点指针 + n.next.pre = nil + } + return n.next +} + +//@title popBack +//@description +// 以node结点做接收者 +// 利用尾节点进行弹出元素,可能存在尾节点全部释放要进行尾节点前移的情况 +// 当发生尾结点前移后将会返回新尾结点,否则返回当前结点 +//@receiver n *node 接收者的node指针 +//@param nil +//@return last *node 尾节点指针 +func (n *node) popBack() (last *node) { + if n == nil { + return nil + } + if n.end > 1 { + //该结点仍有承载元素 + n.end-- + n.data[n.end] = nil + return n + } + if n.pre != nil { + //清除该结点上一节点的后结点指针 + n.pre.next = nil + } + return n.pre +} + +//@title front +//@description +// 以node结点做接收者 +// 返回该结点的第一个元素,利用首节点和begin进行查找 +// 若该结点为nil,则返回nil +//@receiver n *node 接收者的node指针 +//@param nil +//@return e interface{} 该结点承载的的第一个元素 +func (n *node) front() (e interface{}) { + if n == nil { + return nil + } + return n.data[n.begin+1] +} + +//@title back +//@description +// 以node结点做接收者 +// 返回该结点的最后一个元素,利用尾节点和end进行查找 +// 若该结点为nil,则返回nil +//@receiver n *node 接收者的node指针 +//@param nil +//@return e interface{} 该结点承载的的最后一个元素 +func (n *node) back() (e interface{}) { + if n == nil { + return nil + } + return n.data[n.end-1] +}