Go 是否有队列实现?
任何人都可以为简单快速的FIF/队列推荐Go容器,Go有3种不同的容器:Go 是否有队列实现?,go,queue,fifo,Go,Queue,Fifo,任何人都可以为简单快速的FIF/队列推荐Go容器,Go有3种不同的容器:堆、列表和向量。哪一个更适合实现队列?向量或列表都可以,但向量可能是一种方法。我之所以这样说,是因为vector的分配频率可能低于list,而垃圾收集(在当前的Go实现中)相当昂贵。不过,在一个小程序中,这可能无关紧要。要在实现方面进行扩展,请在队列和堆栈的某些结构中提出: // Stack is a basic LIFO stack that resizes as needed. type Stack struct {
堆
、列表
和向量
。哪一个更适合实现队列?向量或列表都可以,但向量可能是一种方法。我之所以这样说,是因为vector的分配频率可能低于list,而垃圾收集(在当前的Go实现中)相当昂贵。不过,在一个小程序中,这可能无关紧要。要在实现方面进行扩展,请在队列和堆栈的某些结构中提出:
// Stack is a basic LIFO stack that resizes as needed.
type Stack struct {
nodes []*Node
count int
}
// Queue is a basic FIFO queue based on a circular list that resizes as needed.
type Queue struct {
nodes []*Node
head int
tail int
count int
}
您可以从中看到它的作用。事实上,如果您想要的是一个基本且易于使用的fifo队列,slice提供了您所需要的一切
queue := make([]int, 0)
// Push to the queue
queue = append(queue, 1)
// Top (just get next element, don't remove it)
x = queue[0]
// Discard top element
queue = queue[1:]
// Is empty ?
if len(queue) == 0 {
fmt.Println("Queue is empty !")
}
当然,我们假设我们可以信任追加和切片的内部实现,从而避免无用的调整大小和重新分配。对于基本用法,这已经足够了。惊讶地发现,对于大小受限的FIFO队列,还没有人建议使用缓冲通道
//Or however many you might need + buffer.
c := make(chan int, 300)
//Push
c <- value
//Pop
x <- c
//或者您可能需要多少+缓冲区。
c:=制造(成龙国际,300)
//推
c在顶部使用切片和适当的(“循环”)索引方案似乎仍然是一条可行之路。我的看法是:那里的基准测试也证实了通道更快,但代价是功能更有限。切片可以用来实现队列
type queue struct {
values []*int
}
func New() *queue {
queue := &queue{}
return queue
}
func (q *queue) enqueue(val *int) {
q.values = append(q.values, val)
}
//deque function
更新:
下面是我的GitHub页面的完整实现我还实现了上面的slice中的队列。但是,它不是线程安全的。所以我决定添加一个锁(互斥锁)来保证线程安全
package queue
import (
"sync"
)
type Queue struct {
lock *sync.Mutex
Values []int
}
func Init() Queue {
return Queue{&sync.Mutex{}, make([]int, 0)}
}
func (q *Queue) Enqueue(x int) {
for {
q.lock.Lock()
q.Values = append(q.Values, x)
q.lock.Unlock()
return
}
}
func (q *Queue) Dequeue() *int {
for {
if (len(q.Values) > 0) {
q.lock.Lock()
x := q.Values[0]
q.Values = q.Values[1:]
q.lock.Unlock()
return &x
}
return nil
}
return nil
}
您可以在github上查看我的解决方案,大多数队列实现有三种:基于切片的、基于链表的和基于循环缓冲区(环形缓冲区)的
- 基于片的队列往往会浪费内存,因为它们不会重用以前被删除项占用的内存。此外,基于片的队列往往是单端的
- 链表队列可以更好地实现内存重用,但由于维护链接的开销,通常会稍微慢一些,并且总体上使用更多内存。它们可以提供在不移动内存的情况下从队列中间添加和删除项目的功能,但是如果您执行了大部分操作,那么队列的数据结构就是错误的李>
- 环形缓冲队列提供了片的所有效率,其优点是不浪费内存。更少的分配意味着更好的性能。它们从两端添加和删除项目同样有效,因此您自然会得到一个双端队列。因此,作为一般建议,我建议使用基于环形缓冲区的队列实现。这就是本文其余部分讨论的内容
基于环形缓冲区的队列通过环绕其存储来重用内存:当队列超出底层片的一端时,它会向片的另一端添加额外的节点。看
此外,还需要一些代码来说明:
// PushBack appends an element to the back of the queue. Implements FIFO when
// elements are removed with PopFront(), and LIFO when elements are removed
// with PopBack().
func (q *Deque) PushBack(elem interface{}) {
q.growIfFull()
q.buf[q.tail] = elem
// Calculate new tail position.
q.tail = q.next(q.tail)
q.count++
}
// next returns the next buffer position wrapping around buffer.
func (q *Deque) next(i int) int {
return (i + 1) & (len(q.buf) - 1) // bitwise modulus
}
这总是使用2的幂的缓冲区大小,因此可以更有效地计算按位模
这意味着只有当片的所有容量都用完时,片才需要增长。由于采用了一种调整大小的策略,避免了在同一边界上增加和缩小存储空间,这使得它的内存效率非常高
下面是调整底层切片缓冲区大小的代码:
// resize resizes the deque to fit exactly twice its current contents. This is
// used to grow the queue when it is full, and also to shrink it when it is
// only a quarter full.
func (q *Deque) resize() {
newBuf := make([]interface{}, q.count<<1)
if q.tail > q.head {
copy(newBuf, q.buf[q.head:q.tail])
} else {
n := copy(newBuf, q.buf[q.head:])
copy(newBuf[n:], q.buf[:q.tail])
}
q.head = 0
q.tail = q.count
q.buf = newBuf
}
//resize调整deque的大小,使其正好适合其当前内容的两倍。这是
//用于在队列已满时增加队列,也用于在队列已满时缩小队列
//只有四分之一满。
func(q*Deque)resize(){
newBuf:=make([]接口{},q.count不幸的是,队列当前不是go标准库的一部分,因此您需要编写自己的/导入其他人的解决方案。遗憾的是,在标准库之外编写的容器无法使用泛型
固定容量队列的一个简单示例是:
type MyQueueElement struct {
blah int // whatever you want
}
const MAX_QUEUE_SIZE = 16
type Queue struct {
content [MAX_QUEUE_SIZE]MyQueueElement
readHead int
writeHead int
len int
}
func (q *Queue) Push(e MyQueueElement) bool {
if q.len >= MAX_QUEUE_SIZE {
return false
}
q.content[q.writeHead] = e
q.writeHead = (q.writeHead + 1) % MAX_QUEUE_SIZE
q.len++
return true
}
func (q *Queue) Pop() (MyQueueElement, bool) {
if q.len <= 0 {
return MyQueueElement{}, false
}
result := q.content[q.readHead]
q.content[q.readHead] = MyQueueElement{}
q.readHead = (q.readHead + 1) % MAX_QUEUE_SIZE
q.len--
return result, true
}
键入MyQueueElement结构{
随便你想要什么
}
常量最大队列大小=16
类型队列结构{
内容[最大队列大小]MyQueueElement
读头int
写头整数
莱恩内特酒店
}
func(q*Queue)Push(e MyQueueElement)bool{
如果q.len>=最大队列大小{
返回错误
}
q、 content[q.writeHead]=e
q、 writeHead=(q.writeHead+1)%MAX\u队列大小
q、 莱恩++
返回真值
}
func(q*Queue)Pop()(MyQueueElement,bool){
如果q.len我实现了一个队列,该队列将自动扩展底层缓冲区:
package types
// Note: this queue does not shrink the underlying buffer.
type queue struct {
buf [][4]int // change to the element data type that you need
head int
tail int
}
func (q *queue) extend(need int) {
if need-(len(q.buf)-q.head) > 0 {
if need-len(q.buf) <= 0 {
copy(q.buf, q.buf[q.head:q.tail])
q.tail = q.tail - q.head
q.head = 0
return
}
newSize := len(q.buf) * 2
if newSize == 0 {
newSize = 100
}
newBuf := make([][4]int, newSize)
copy(newBuf, q.buf[q.head:q.tail])
q.buf = newBuf
q.tail = q.tail - q.head
q.head = 0
}
}
func (q *queue) push(p [4]int) {
q.extend(q.tail + 1)
q.buf[q.tail] = p
q.tail++
}
func (q *queue) pop() [4]int {
r := q.buf[q.head]
q.head++
return r
}
func (q *queue) size() int {
return q.tail - q.head
}
// put the following into queue_test.go
package types
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestQueue(t *testing.T) {
const total = 1000
q := &queue{}
for i := 0; i < total; i++ {
q.push([4]int{i, i, i, i})
assert.Equal(t, i+1, q.size())
}
for i := 0; i < total; i++ {
v := q.pop()
assert.Equal(t, [4]int{i, i, i, i}, v)
assert.Equal(t, total-1-i, q.size())
}
}
包类型
//注意:此队列不会收缩基础缓冲区。
类型队列结构{
buf[][4]int//更改为所需的元素数据类型
头部智力
尾int
}
func(q*队列)扩展(需要int){
如果需要-(len(q.buf)-q.head)>0{
如果需要len(q.buf)双堆栈实现:
O(1)
Enqueue
和Dequeue
并使用切片
(这对于缓存未命中更为有利)
列表对于队列和堆栈来说已经足够了,我们应该做的是l.Remove(l.Front())
用于队列轮询,l.Remove(l.Back())
用于堆栈轮询,PushBack
用于堆栈和队列的添加操作。列表有前后指针,因此时间复杂度为O(1)编辑,更清晰地实现队列:
主程序包
输入“fmt”
类型队列[]接口{}
func(自*队列)推送(x接口{}){
*self=append(*self,x)
}
func(self*Queue)Pop()接口{}{
h:=*自我
变量el接口{}
l:=len(h)
el,*self=h[0],h[1:l]
//或者将其用于堆栈
//el,*self=h[l-1],h[0:l-1]
雷图
type Queue struct{
enqueue, dequeue Stack
}
func (q *Queue) Enqueue(n *Thing){
q.enqueue.Push(n)
}
func (q *Queue) Dequeue()(*Thing, bool){
v, ok := q.dequeue.Pop()
if ok{
return v, true
}
for {
v, ok := d.enqueue.Pop()
if !ok{
break
}
d.dequeue.Push(v)
}
return d.dequeue.Pop()
}
type Stack struct{
v []*Thing
}
func (s *Stack)Push(n *Thing){
s.v=append(s.v, n)
}
func (s *Stack) Pop()(*Thing, bool){
if len(s.v) == 0 {
return nil, false
}
lastIdx := len(s.v)-1
v := s.v[lastIdx]
s.v=s.v[:lastIdx]
return v, true
}
type Queue struct {
slice []int
len int
}
func newq() Queue {
q := Queue{}
q.slice = make([]int, 0)
q.len = 0
return q
}
func (q *Queue) Add(v int) {
q.slice = append(q.slice, v)
q.len++
}
func (q *Queue) PopLeft() int {
a := q.slice[0]
q.slice = q.slice[1:]
q.len--
return a
}
func (q *Queue) Pop() int {
a := q.slice[q.len-1]
q.slice = q.slice[:q.len-1]
q.len--
return a
}