2012-12-08 111 views
2

我剛纔看到在通用類型的滿足接口的任何 類型可以放入隊列中的優先級隊列的實現。這是去與否的方式,或者這是否引入任何問題?Go中的優先隊列實現

// Copyright 2012 Stefan Nilsson 
// 
// Licensed under the Apache License, Version 2.0 (the "License"); 
// you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at 
// 
//  http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
// See the License for the specific language governing permissions and 
// limitations under the License. 

// Package prio provides a priority queue. 
// The queue can hold elements that implement the two methods of prio.Interface. 
package prio 

/* 
A type that implements prio.Interface can be inserted into a priority queue. 

The simplest use case looks like this: 

     type myInt int 

     func (x myInt) Less(y prio.Interface) bool { return x < y.(myInt) } 
     func (x myInt) Index(i int)    {} 

To use the Remove method you need to keep track of the index of elements 
in the heap, e.g. like this: 

     type myType struct { 
       value int 
       index int // index in heap 
     } 

     func (x *myType) Less(y prio.Interface) bool { return x.value < y.(*myType).value } 
     func (x *myType) Index(i int)    { x.index = i } 
*/ 
type Interface interface { 
     // Less returns whether this element should sort before element x. 
     Less(x Interface) bool 
     // Index is called by the priority queue when this element is moved to index i. 
     Index(i int) 
} 

// Queue represents a priority queue. 
// The zero value for Queue is an empty queue ready to use. 
type Queue struct { 
     h []Interface 
} 

// New returns an initialized priority queue with the given elements. 
// A call of the form New(x...) uses the underlying array of x to implement 
// the queue and hence might change the elements of x. 
// The complexity is O(n), where n = len(x). 
func New(x ...Interface) Queue { 
     q := Queue{x} 
     heapify(q.h) 
     return q 
} 

// Push pushes the element x onto the queue. 
// The complexity is O(log(n)) where n = q.Len(). 
func (q *Queue) Push(x Interface) { 
     n := len(q.h) 
     q.h = append(q.h, x) 
     up(q.h, n) // x.Index(n) is done by up. 
} 

// Pop removes a minimum element (according to Less) from the queue and returns it. 
// The complexity is O(log(n)), where n = q.Len(). 
func (q *Queue) Pop() Interface { 
     h := q.h 
     n := len(h) - 1 
     x := h[0] 
     h[0], h[n] = h[n], nil 
     h = h[:n] 
     if n > 0 { 
       down(h, 0) // h[0].Index(0) is done by down. 
     } 
     q.h = h 
     x.Index(-1) // for safety 
     return x 
} 

// Peek returns, but does not remove, a minimum element (according to Less) of the queue. 
func (q *Queue) Peek() Interface { 
     return q.h[0] 
} 

// Remove removes the element at index i from the queue and returns it. 
// The complexity is O(log(n)), where n = q.Len(). 
func (q *Queue) Remove(i int) Interface { 
     h := q.h 
     n := len(h) - 1 
     x := h[i] 
     h[i], h[n] = h[n], nil 
     h = h[:n] 
     if i < n { 
       down(h, i) // h[i].Index(i) is done by down. 
       up(h, i) 
     } 
     q.h = h 
     x.Index(-1) // for safety 
     return x 
} 

// Len returns the number of elements in the queue. 
func (q *Queue) Len() int { 
     return len(q.h) 
} 

// Establishes the heap invariant in O(n) time. 
func heapify(h []Interface) { 
     n := len(h) 
     for i := n - 1; i >= n/2; i-- { 
       h[i].Index(i) 
     } 
     for i := n/2 - 1; i >= 0; i-- { // h[i].Index(i) is done by down. 
       down(h, i) 
     } 
} 

// Moves element at position i towards top of heap to restore invariant. 
func up(h []Interface, i int) { 
     for { 
       parent := (i - 1)/2 
       if i == 0 || h[parent].Less(h[i]) { 
         h[i].Index(i) 
         break 
       } 
       h[parent], h[i] = h[i], h[parent] 
       h[i].Index(i) 
       i = parent 
     } 
} 

// Moves element at position i towards bottom of heap to restore invariant. 
func down(h []Interface, i int) { 
     for { 
       n := len(h) 
       left := 2*i + 1 
       if left >= n { 
         h[i].Index(i) 
         break 
       } 
       j := left 
       if right := left + 1; right < n && h[right].Less(h[left]) { 
         j = right 
       } 
       if h[i].Less(h[j]) { 
         h[i].Index(i) 
         break 
       } 
       h[i], h[j] = h[j], h[i] 
       h[i].Index(i) 
       i = j 
     } 
} 
+0

這不是'通用'。您的(節點)類型需要支持「接口」,並且第三方包類型需要以支持「接口」的類型進行包裝。 – alphazero

+3

專業提示:避免在Go語境中使用generic這個詞。它 - 攪動人們。 – Sonia

回答

2

這個軟件包不是一般的使用方式,但是如果你喜歡它並且它滿足你的需求,那就使用它。我沒有看到任何重大問題。

該包與容器/堆相比的概念是將接口放在節點上而不是容器上。容器/堆使用您的容器可以實現更大的靈活性。 (你可能已經有一個容器中的節點了,這個容器甚至可能不是一個切片,它只是可以索引的。)另一方面,這可能是一個常見的情況,你不關心容器,而是很高興讓這個軟件包爲你管理。這個包的索引管理是容器/堆的一個很好的功能,儘管它增加了方法調用的開銷,即使不需要索引管理也是如此。

總是存在折衷。容器/堆是非常一般的。這個包通過一個較小的方法集(2而不是5)獲得,並且在頂部添加了索引管理,但是僅僅在某些情況下犧牲了一些普遍性和性能。 (如果你真的關心,你會想要進行基準測試,還有其他的差異可能會導致索引調用的開銷。)