首页 用go实现一个优先队列
文章
取消

用go实现一个优先队列

上篇文章中提到了一个优先队列,正好拿来说一下。

优先队列有别于普通的队列,普通的队列是先进先出,而优先队列是优先级高的先出,优先级低的后出。

优先队列与普通队列的区别在于“谁出”,优先队列可以使用双链表来实现,只需要找到优先级最高的节点然后删除返回即可,时间复杂度为 \(O(n)\)。那还能不能优化呢?当然可以,可以使用二叉堆这种数据结构,其构建时间复杂度为 \(O(nlogn)\)。

具体来说,我们需要最大二叉堆这个结构,这样才能高效地删除最大优先级的元素。

先定义一个元素:

1
2
3
4
type element struct {
	value    interface{}
	priority int
}

定义队列,要实现大小比较方法和 heap 的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type queue []element

// Len、Less、Swap 是排序方法的定义

// 队列长度
func (q queue) Len() int {
	return len(q)
}
// 我们设定最大堆
func (q queue) Less(i, j int) bool {
	return q[i].priority > q[j].priority
}
func (q queue) Swap(i, j int) {
	q[i], q[j] = q[j], q[i]
}

// 实现二叉堆的接口

// 添加元素
func (q *queue) Push(v interface{}) {
	*q = append(*q, v.(element))
}

// 弹出元素
func (q *queue) Pop() interface{} {
	l := len(*q)
	ele := (*q)[l-1]
	*q = (*q)[:l-1]
	return ele
}

我们对上面的 queue 封装下,仅暴露出必要的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 对 queue 封装一层,只保留必要的对外api
type PriorityQueue struct {
	q *queue
}

// 初始化
func (pq *PriorityQueue) Init() {
	tmpQ := make(queue, 0)
	pq.q = &tmpQ
}

// 添加数据
func (pq *PriorityQueue) Push(v interface{}, priority int) {
	heap.Push(pq.q, element{ // 使用 heap.Push 二叉堆会排序
		value:    v,
		priority: priority,
	})
}

// 弹出最大优先级数据
func (pq *PriorityQueue) Pop() interface{} {
	return heap.Pop(pq.q).(element).value
}

// 长度
func (pq *PriorityQueue) Len() int {
	return pq.q.Len()
}

// 是否为空
func (pq *PriorityQueue) IsEmpty() bool {
	return pq.q.Len() == 0
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
	var pq PriorityQueue
	pq.Init()
	pq.Push("a", 4)
	pq.Push("b", 5)
	pq.Push("c", 2)
	pq.Push("d", 9)
	pq.Push("e", 1)
	fmt.Printf("length:%v \n", pq.Len())

	fmt.Printf("pop element:%v, and result length:%v \n", pq.Pop(), pq.Len())
	fmt.Printf("pop element:%v, and result length:%v \n", pq.Pop(), pq.Len())
}

结果:

1
2
3
length:5
pop element:d, and result length:4
pop element:b, and result length:3
本文由作者按照 CC BY 4.0 进行授权

用go实现一个LFU缓存

CROSSSLOT Keys in request don't hash to the same slot