队列:优先队列
# 概念
- **优先队列(Priority Queue)**在普通队列的基础上给每个元素增加了优先级,这样每次出队的元素不再是队首的元素,而是队列中优先级最高的元素,而具体的优先级可以自行定义,典型的就是按元素从大到小或从小到大的顺序定义优先级
- 优先队列可以通过不同的数据结构来实现,常见的实现方式有:
- 最小堆(Min Heap):在最小堆中,根节点的值是最小的,每个节点的值都小于或等于其子节点的值。这种实现方式适合找到最小元素的应用场景。
- 最大堆(Max Heap):在最大堆中,根节点的值是最大的,每个节点的值都大于或等于其子节点的值。这种实现方式适合找到最大元素的应用场景。
- 二叉搜索树(Binary Search Tree):通过保持二叉搜索树的性质,即左子树的节点值小于父节点值,右子树的节点值大于父节点值,可以实现优先队列。
# 优缺点
- 优点
- 快速获取最大或最小元素:优先队列通过堆的性质,能够在 O(1) 时间复杂度内获取优先级最高或最低的元素
- 适用于任务调度:优先队列常用于任务调度场景,按照任务的优先级或执行时间进行排序,确保高优先级任务或即将执行的任务能够及时被调度
- 缺点
- 插入元素的时间复杂度:插入元素的时间复杂度通常为 O(log n),因为需要维护堆的结构
- 动态扩容代价:在基于数组实现的堆中,当队列长度超过数组容量时,可能需要进行动态扩容,带来额外的时间和空间消耗
# 应用场景
- 网络调度:用于管理和调度网络数据包的优先级,确保重要数据优先传输
- 操作系统:在操作系统中,可以使用优先队列来调度进程和线程的执行顺序
- 数据压缩:在哈夫曼编码等数据压缩算法中,通过优先队列来构建最优编码树
- 最短路径算法:在 Dijkstra 算法和 A* 算法中,优先队列用于选择下一个探索的节点
# 代码示例
go官方实现
import ( "container/heap" "fmt" ) // An Item is something we manage in a priority queue. type Item struct { value string // The value of the item; arbitrary. priority int // The priority of the item in the queue. // The index is needed by update and is maintained by the heap.Interface methods. index int // The index of the item in the heap. } // A PriorityQueue implements heap.Interface and holds Items. type PriorityQueue []*Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { // We want Pop to give us the highest, not lowest, priority so we use greater than here. return pq[i].priority > pq[j].priority } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] pq[i].index = i pq[j].index = j } func (pq *PriorityQueue) Push(x any) { n := len(*pq) item := x.(*Item) item.index = n *pq = append(*pq, item) } func (pq *PriorityQueue) Pop() any { old := *pq n := len(old) item := old[n-1] old[n-1] = nil // avoid memory leak item.index = -1 // for safety *pq = old[0 : n-1] return item } // update modifies the priority and value of an Item in the queue. func (pq *PriorityQueue) update(item *Item, value string, priority int) { item.value = value item.priority = priority heap.Fix(pq, item.index) } // This example creates a PriorityQueue with some items, adds and manipulates an item, // and then removes the items in priority order. func Example_priorityQueue() { // Some items and their priorities. items := map[string]int{ "banana": 3, "apple": 2, "pear": 4, } // Create a priority queue, put the items in it, and // establish the priority queue (heap) invariants. pq := make(PriorityQueue, len(items)) i := 0 for value, priority := range items { pq[i] = &Item{ value: value, priority: priority, index: i, } i++ } heap.Init(&pq) // Insert a new item and then modify its priority. item := &Item{ value: "orange", priority: 1, } heap.Push(&pq, item) pq.update(item, item.value, 5) // Take the items out; they arrive in decreasing priority order. for pq.Len() > 0 { item := heap.Pop(&pq).(*Item) fmt.Printf("%.2d:%s ", item.priority, item.value) } // Output: // 05:orange 04:pear 03:banana 02:apple }
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
上次更新: 2023/08/27, 21:33:49