Skip to content

Commit e59c04c

Browse files
committed
完成优先级队列
1 parent c4d3197 commit e59c04c

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# 优先级队列
2+
你可能比较奇怪,队列不是早就讲了嘛。这里之所以放到这里讲优先级队列,是因为虽然名字有队列,
3+
其实使用的是堆来实现的。上一章讲完了堆,这一章我们就来实现一个优先级队列。
4+
5+
6+
# 实现优先级队列
7+
优先级队列(Priority Queue) 顾名思义,就是入队的时候可以给一个优先级,通常是个数字或者时间戳等,
8+
当出队的时候我们希望按照给定的优先级出队,我们按照 TDD 的方式先来写测试代码:
9+
10+
```py
11+
def test_priority_queue():
12+
size = 5
13+
pq = PriorityQueue(size)
14+
pq.push(5, 'purple')
15+
pq.push(0, 'white')
16+
pq.push(3, 'orange')
17+
pq.push(1, 'black')
18+
19+
res = []
20+
while not pq.is_empty():
21+
res.append(pq.pop())
22+
assert res == ['purple', 'orange', 'black', 'white']
23+
```
24+
25+
上边就是期望的行为,然后编写优先级队列,按照出队的时候最大优先级先出对的顺序:
26+
27+
28+
```py
29+
class PriorityQueue(object):
30+
def __init__(self, maxsize):
31+
self.maxsize = maxsize
32+
self._maxheap = MaxHeap(maxsize)
33+
34+
def push(self, priority, value):
35+
# 注意这里把这个 tuple push进去,python 比较 tuple 从第一个开始比较
36+
# 这样就很巧妙地实现了按照优先级排序
37+
entry = (priority, value)
38+
self._maxheap.add(entry)
39+
40+
def pop(self, with_priority=False):
41+
entry = self._maxheap.extract()
42+
if with_priority:
43+
return entry
44+
else:
45+
return entry[1]
46+
47+
def is_empty(self):
48+
return len(self._maxheap) == 0
49+
```
50+
51+
52+
# 练习题
53+
- 请你实现按照小优先级先出队的顺序的优先级队列
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# -*- coding:utf-8 -*-
2+
3+
# 第二章拷贝的 Array 代码
4+
5+
6+
class Array(object):
7+
8+
def __init__(self, size=32):
9+
self._size = size
10+
self._items = [None] * size
11+
12+
def __getitem__(self, index):
13+
return self._items[index]
14+
15+
def __setitem__(self, index, value):
16+
self._items[index] = value
17+
18+
def __len__(self):
19+
return self._size
20+
21+
def clear(self, value=None):
22+
for i in range(self._items):
23+
self._items[i] = value
24+
25+
def __iter__(self):
26+
for item in self._items:
27+
yield item
28+
29+
#####################################################
30+
# heap 实现
31+
#####################################################
32+
33+
34+
class MaxHeap(object):
35+
"""
36+
Heaps:
37+
完全二叉树,最大堆的非叶子节点的值都比孩子大,最小堆的非叶子结点的值都比孩子小
38+
Heap包含两个属性,order property 和 shape property(a complete binary tree),在插入
39+
一个新节点的时候,始终要保持这两个属性
40+
插入操作:保持堆属性和完全二叉树属性, sift-up 操作维持堆属性
41+
extract操作:只获取根节点数据,并把树最底层最右节点copy到根节点后,sift-down操作维持堆属性
42+
43+
用数组实现heap,从根节点开始,从上往下从左到右给每个节点编号,则根据完全二叉树的
44+
性质,给定一个节点i, 其父亲和孩子节点的编号分别是:
45+
parent = (i-1) // 2
46+
left = 2 * i + 1
47+
rgiht = 2 * i + 2
48+
使用数组实现堆一方面效率更高,节省树节点的内存占用,一方面还可以避免复杂的指针操作,减少
49+
调试难度。
50+
51+
"""
52+
53+
def __init__(self, maxsize=None):
54+
self.maxsize = maxsize
55+
self._elements = Array(maxsize)
56+
self._count = 0
57+
58+
def __len__(self):
59+
return self._count
60+
61+
def add(self, value):
62+
if self._count >= self.maxsize:
63+
raise Exception('full')
64+
self._elements[self._count] = value
65+
self._count += 1
66+
self._siftup(self._count-1) # 维持堆的特性
67+
68+
def _siftup(self, ndx):
69+
if ndx > 0:
70+
parent = int((ndx-1)/2)
71+
if self._elements[ndx] > self._elements[parent]: # 如果插入的值大于 parent,一直交换
72+
self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx]
73+
self._siftup(parent) # 递归
74+
75+
def extract(self):
76+
if self._count <= 0:
77+
raise Exception('empty')
78+
value = self._elements[0] # 保存 root 值
79+
self._count -= 1
80+
self._elements[0] = self._elements[self._count] # 最右下的节点放到root后siftDown
81+
self._siftdown(0) # 维持堆特性
82+
return value
83+
84+
def _siftdown(self, ndx):
85+
left = 2 * ndx + 1
86+
right = 2 * ndx + 2
87+
# determine which node contains the larger value
88+
largest = ndx
89+
if (left < self._count and # 有左孩子
90+
self._elements[left] >= self._elements[largest] and
91+
self._elements[left] >= self._elements[right]): # 原书这个地方没写实际上找的未必是largest
92+
largest = left
93+
elif right < self._count and self._elements[right] >= self._elements[largest]:
94+
largest = right
95+
if largest != ndx:
96+
self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx]
97+
self._siftdown(largest)
98+
99+
100+
class PriorityQueue(object):
101+
def __init__(self, maxsize):
102+
self.maxsize = maxsize
103+
self._maxheap = MaxHeap(maxsize)
104+
105+
def push(self, priority, value):
106+
entry = (priority, value) # 注意这里把这个 tuple push进去,python 比较 tuple 从第一个开始比较
107+
self._maxheap.add(entry)
108+
109+
def pop(self, with_priority=False):
110+
entry = self._maxheap.extract()
111+
if with_priority:
112+
return entry
113+
else:
114+
return entry[1]
115+
116+
def is_empty(self):
117+
return len(self._maxheap) == 0
118+
119+
120+
def test_priority_queue():
121+
size = 5
122+
pq = PriorityQueue(size)
123+
pq.push(5, 'purple')
124+
pq.push(0, 'white')
125+
pq.push(3, 'orange')
126+
pq.push(1, 'black')
127+
128+
res = []
129+
while not pq.is_empty():
130+
res.append(pq.pop())
131+
assert res == ['purple', 'orange', 'black', 'white']

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ pages:
2828
- 快速排序: '13_高级排序算法/quick_sort.md'
2929
- 树与二叉树: '14_树与二叉树/tree.md'
3030
- 堆和堆排序: '15_堆与堆排序/heap_and_heapsort.md'
31+
- 优先级队列: '16_优先级队列/priority_queue.md'

priority_queue.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env python
2+
# -*- coding:utf-8 -*-
3+
4+
5+
if __name__ == '__main__':
6+
pass

0 commit comments

Comments
 (0)