简体   繁体   English

如何在 Go 中实现队列?

[英]How to implement a queue in Go?

The current Go library doesn't provide the queue container.当前的 Go 库不提供队列容器。 To implement a simple queue, I use circle array as the underlying data structure.为了实现一个简单的队列,我使用圆数组作为底层数据结构。 It follows algorithms mentioned in TAOCP:它遵循 TAOCP 中提到的算法:

Insert Y into queue X: X[R]<-Y; R<-(R+1)%M; if R=F then OVERFLOW.
Delete Y from queue X: if F=R then UNDERFLOW; Y<-X[F]; F<-(F+1) % M.
F: Front, R: Rear, M: Array length.

Following is the code:以下是代码:

package main

import (
    "fmt"
)

type Queue struct {
    len        int 
    head, tail int 
    q          []int
}

func New(n int) *Queue {
    return &Queue{n, 0, 0, make([]int, n)} 
}

func (p *Queue) Enqueue(x int) bool {
    p.q[p.tail] = x 
    p.tail = (p.tail + 1) % p.len
    return p.head != p.tail
}

func (p *Queue) Dequeue() (int, bool) {
    if p.head == p.tail {
        return 0, false
    }   
    x := p.q[p.head]
    p.head = (p.head + 1) % p.len
    return x, true
}

func main() {
    q := New(10)
    for i := 1; i < 13; i++ {
        fmt.Println(i, q.Enqueue(i))
    }   
    fmt.Println()
    for i := 1; i < 13; i++ {
        fmt.Println(q.Dequeue())
    }   
}

But the output is obviously wrong:但输出显然是错误的:

1 true 2 true 3 true 4 true 5 true 6 true 7 true 8 true 9 true 10 false 11 true 12 true 1真2真3真4真5真6真7真8真9真10假11真12真

11 true 12 true 0 false 0 false 0 false 0 false 0 false 0 false 0 false 0 false 0 false 0 false 11 真 12 真 0 假 0 假 0 假 0 假 0 假 0 假 0 假 0 假 0 假 0 假

I think I need one more field to make the code work properly.我想我还需要一个字段来使代码正常工作。 What do you suggest?你有什么建议?

The improved code has a small shortcoming: an array of size n can contain only n-1 elements.改进后的代码有一个小缺点:大小为 n 的数组只能包含 n-1 个元素。

package main

import (
    "fmt"
)

type Queue struct {
    len        int 
    head, tail int 
    q          []int
}

func New(n int) *Queue {
    return &Queue{n, 0, 0, make([]int, n)} 
}

func (p *Queue) Enqueue(x int) bool {
    p.q[p.tail] = x 
    ntail := (p.tail + 1) % p.len
    ok := false
    if ntail != p.head {
        p.tail = ntail
        ok = true
    }   
    return ok
}

func (p *Queue) Dequeue() (int, bool) {
    if p.head == p.tail {
        return 0, false
    }   
    x := p.q[p.head]
    p.head = (p.head + 1) % p.len
    return x, true
}

func main() {
    q := New(10)
    for i := 1; i < 13; i++ {
        fmt.Println(i, q.Enqueue(i))
    }   
    fmt.Println()
    for i := 1; i < 13; i++ {
        fmt.Println(q.Dequeue())
    }   
}

You do not need all this hustle in any reasonable go version (after 1.x). 在任何合理的版本中都不需要所有这些喧嚣(在1.x之后)。 Everything can be achieved with slices . 切片可以实现一切。

queue := []int{}

Add to a queue: 添加到队列:

queue = append(queue, 6)

Pop from a queue: 从队列中弹出:

el := queue[0]
queue = queue[1:]

Here is implementation that shows that pop does not take a lot of time (in fact here it is shorter than push because in my opinion of reallocation of memory when the queue is growing). 这里的实现表明pop不需要花费很多时间(事实上这里它比push更短,因为我认为在队列增长时重新分配内存)。

package main

import (
    "fmt"
    "time"
)

func main() {
    n := 10000000
    queue := []int{1, 2, 3}

    start := time.Now()
    for i := 0; i < n; i++ {
        queue = append(queue, i)
    }
    elapsed := time.Since(start)
    fmt.Println(elapsed)

    start = time.Now()
    for i := 0; i < n; i++ {
        _ = queue[0]
        queue = queue[1:]
    }
    elapsed = time.Since(start)
    fmt.Println(elapsed)
    fmt.Println(queue)
}

On my machine the numbers are: 在我的机器上,数字是:

216.611664ms
13.441106ms

From @DaveC 's comment: 来自@DaveC的评论:

This is simple and works very well for everything except critical code where allocations (pressure on the garbage collector) is undesirable.Two things to note, first it does keep re-allocating the underlying array on push (although efficiently and not on every call) and pop doesn't free any space until that happens. 这很简单,除了关键代码之外的所有事情都非常有效,其中分配(垃圾收集器上的压力)是不合需要的。需要注意的是,首先它确实在推送时重新分配底层数组(虽然有效而不是每次调用)直到发生这种情况,pop才会释放任何空间。 This leads to the second thing, if (as is common) the queue contains a pointer to something, then it's good to do queue[0] = nil; 这导致了第二件事,如果(通常)队列包含指向某事物的指针,那么做队列[0] = nil是好的; queue = queue[1:] to have the queue stop referencing the pointer right away. queue = queue [1:]让队列立即停止引用指针。

It's true that there's no package called queue, but either vector or list would make fine queues. 确实没有名为queue的包,但是vectorlist都可以排队。 Also see this question . 另见这个问题

A buffered channel makes a fine queue, though it has a fixed maximum queue length, chosen at creation time. 缓冲通道构成一个精细队列,尽管它具有固定的最大队列长度,在创建时选择。 A channel has the useful property that dequeues are threadsafe (your code isn't). 一个通道具有有用的属性,该属性的出列是线程安全的(您的代码不是)。

When Enqueue fails, you're still incrementing p.tail , so next time it will appear not to fail -- that explains the single false in your first loop (and messes everything up for the second one). Enqueue失败时,你仍然在增加p.tail ,所以下次它看起来不会失败 - 这解释了你的第一个循环中的单个false (并且为第二个循环弄乱了所有内容)。 The original algorithm says OVERFLOW meaning "give everything up", not "just keep going as if nothing untowards happened";-). 最初的算法说OVERFLOW意味着“放弃一切”,而不是“只是继续前进就像没有发生任何不幸事件”;-)。

All you need to do is decrement p.tail if you've checked that failure's occurring -- or put the incremented value in a local temporary and move it to p.tail only if failure is not occurring, that may be more elegant. 你需要做的就是减去p.tail如果你已经检查过发生了故障 - 或者将增加的值放在一个本地临时值中,只有在没有发生故障时才将其移到p.tail ,这可能更优雅。 This way, the failing Enqueue does not enqueue the new value, but the queue itself (without that overflowing value) is still semantically intact and correct for future operations. 这样一来,失败的Enqueue没有入队的新价值,但本身队列(即无溢值)仍是语义完整和正确的未来运营。

I modify the original implementation to make a dynamic queue. 我修改原始实现以创建动态队列。 Ie when the queue fills up it will allocate a larger queue and move all the items over. 即,当队列填满时,它将分配更大的队列并移动所有项目。

package main

import (
    "fmt"
)

type Queue struct {
    len        uint
    head, tail uint
    q          []int
}

func NextPowerOfTwo(v uint) uint {
    if v == 0 {
        return 1
    }
    v--
    v |= v >> 1
    v |= v >> 2
    v |= v >> 4
    v |= v >> 8
    v |= v >> 16
    v++
    return v
}

func NewQueue(n uint) *Queue {
    n = NextPowerOfTwo(n)
    if n < 4 {
        n = 4
    }
    println("create queue of", n)
    return &Queue{n, 0, 0, make([]int, n)}
}

func (p *Queue) Resize() {
    if p.head == (p.tail + 1) % p.len {
        new_len := p.len * 2;
        new_q := make([]int, new_len)
        // currently there are (len - 1) items in the queue
        var i uint
        for i = 0; i < p.len - 1; i++ {
            n, _ := p.Dequeue()
            new_q[i] = n
        }
        p.q = new_q
        p.head, p.tail = 0, p.len - 1
        p.len = new_len
        println("queue resized to ", p.len)
    }
}

func (p *Queue) Enqueue(x int) {
    p.Resize();
    p.q[p.tail] = x
    p.tail = (p.tail + 1) % p.len
}

func (p *Queue) Dequeue() (int, bool) {
    if p.head == p.tail {
        return -1, false
    }
    x := p.q[p.head]
    p.head = (p.head + 1) % p.len
    return x, true
}

func main() {
    q := NewQueue(1)
    for i := 1; i < 13; i++ {
        q.Enqueue(2 * i + 1)
        println("enqueued item ", i)
    }
    println("** queue content **")
    for i := 1; i < 13 + 1; i++ {
        fmt.Println(q.Dequeue())
    }
}

First you need to create a struct for Queue for holding queue properties.首先,您需要为Queue创建一个结构体来保存队列属性。 Then create a initQueue function to initialize default values, which will also take memory size from the user.然后创建一个initQueue函数来初始化默认值,这也将从用户那里获取内存大小。 Create a function to enqueue values, create function to dequeue values.创建一个函数来入值,创建函数来出队值。 Create a display function to display queues values.创建一个显示函数来显示队列值。

type Queue struct {
    front  int
    rear   int
    size   int
    QArray []int
}

func (q *Queue) initQueue(size int) {
    q.size = size
    q.QArray = make([]int, q.size)
    q.front = -1
    q.rear = -1
}

func (q *Queue) enqueue(value int) {
    if q.rear == q.size-1 {
        fmt.Println("Queue is Full")
        return
    } else {
        q.rear++
        q.QArray[q.rear] = value
    }
}

func (q *Queue) dequeue() int {
    var x int = -1
    if q.front == q.rear {
        fmt.Println("Queue is Empty!")
    } else {
        q.front++
        x = q.QArray[q.front]
    }
    return x
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM