标准库优先级队列推送方法

时间:2013-02-03 16:28:58

标签: algorithm go priority-queue

下面的代码片段是优先级队列的推送方法的库实现。我想知道为什么代码a = a[0 : n+1]的行不会抛出越界错误。

 func (pq *PriorityQueue) Push(x interface{}) {
    // Push and Pop use pointer receivers because they modify the slice's length,
    // not just its contents.
    // To simplify indexing expressions in these methods, we save a copy of the
    // slice object. We could instead write (*pq)[i].
    a := *pq
    n := len(a)
    a = a[0 : n+1]
    item := x.(*Item)
    item.index = n
    a[n] = item
    *pq = a
}

4 个答案:

答案 0 :(得分:3)

切片不是数组;它是现有数组的视图。有问题的切片由比自身大的数组支持。定义现有切片的切片时,实际上是切片基础数组,但引用的索引是相对于源切片的。

那是满口的。让我们通过以下方式证明这一点:我们将创建一个零长度的切片,但我们将强制底层数组更大。使用make创建切片时,第三个参数将设置基础数组的大小。表达式make([]int, 0, 2)将分配一个大小为2的数组,但它的计算结果为大小为零的切片。

package main

import ("fmt")

func main() {
    // create a zero-width slice over an initial array of size 2
    a := make([]int, 0, 2)
    fmt.Println(a)

    // expand the slice.  Since we're not beyond the size of the initial
    // array, this isn't out of bounds.
    a = a[0:len(a)+1]

    a[0] = 1
    fmt.Println(a)
    fmt.Println(a[0:len(a)+1])
}

see here。您可以使用cap关键字来引用支持给定切片的数组的大小。

您询问的有关在调用上下文中循环cap(pq)的特定代码(container / heap / example_test.go第90行)。如果您修改呼叫站点上的代码并尝试将另一个项目推入队列,它将像您期望的那样恐慌。我...可能不会建议像这样编写代码。虽然标准库中的代码会执行,但如果我在代码库中找到它,我会非常讨厌。使用append关键字通常更安全。

答案 1 :(得分:2)

因为它适用于特定的示例程序。以下是原始/完整example source

中的重要部分
const nItem = 10

pq := make(PriorityQueue, 0, nItem)

for i := 0; i < cap(pq); i++ {
        item := &Item{
                value:    values[i],
                priority: priorities[i],
        }
        heap.Push(&pq, item)
}

答案 2 :(得分:1)

这是container/heap的例子吗?如果是,那么它不会引发异常,因为容量足够大(请参阅如何使用Push方法)。如果您将示例更改为Push更多项目然后更改容量,那么它将会抛出。

答案 3 :(得分:0)

一般来说;它不在container/heap示例中。这是我前段时间给你的一般修复。

func (pq *PriorityQueue) Push(x interface{}) {
    a := *pq
    n := len(a)
    item := x.(*Item)
    item.index = n
    a = append(a, item)
    *pq = a
}

Golang solution to Project Euler problem #81