1.数组实现堆操作
例1:实现大堆操作
'''
0629 实现大堆的操作
堆用数组表示,一颗完全二叉树
i的父节点是par=(i-1)/2
i的左子节点left=2*i+1
i的右子节点right=2*i+2
数组下标从0开始
大堆的特点是父节点是最大的
下沉操作:新元素比子节点都要小,下沉帮助找到正确位置(贪心原则,子节点大就要上移)
上浮操作:新元素比父节点大,其他都满足大堆
'''
class Heap(object):
a = []
n = 0
def _sink(self, i):
# 把新元素拿出来
t = self.a[i]
# 有左子节点
while i + i + 1 < self.n:
# j指向左子节点
j = i + i + 1
# 判断是否有右子节点,如果有且大,那么j指向右子节点
if j < self.n - 1 and self.a[j] < self.a[j + 1]:
# 左边小于右边,j设为右子节点下标
j += 1
# 如果子节点比t大,t的位置还要下移
if self.a[j] > t:
# 大于当前,节点上移
self.a[i] = self.a[j]
i = j # j成为新的空位,再查看新空位的左右子节点
else:
# 找到了t的位置,t大于所有子节点
break
# 把新元素放入正确的位置
self.a[i] = t
def _swim(self, i):
t = self.a[i]
# 父节点
while i > 0:
par = (i - 1) / 2
# 如果父节点比新元素小,父节点下沉
if self.a[par] < t:
self.a[i] = self.a[par]
i = par
else:
break
self.a[i] = t
def push(self, val):
# 在堆的尾巴上添加新元素
self.a.append(val)
# 新元素进行上浮
self._swim(self.n)
self.n += 1
def pop(self):
# 取出a[0]作为返回值
ret = self.a[0]
# 将a[n-1]放到a[0]中
self.a[0] = self.a[self.n - 1]
self.a.pop()
self.n -= 1
# a[0]进行下沉
self._sink(0)
return ret
def size(self):
return self.n
2.优先队列实现前k个例题
例2:数组最小的k个元素
'''
0620 数组最小的k个元素
用自己实现的堆
o(nlogk)
o(k)
'''
class Solution:
a = []
n = 0
# 下沉操作
def _sink(self, i):
# 把新元素拿出来
t = self.a[i]
# 有左子节点
while i + i + 1 < self.n:
# j指向左子节点
j = i + i + 1
# 判断是否有右子节点,如果有且大,那么j指向右子节点
if j < self.n - 1 and self.a[j] < self.a[j + 1]:
# 左边小于右边,j设为右子节点下标
j += 1
# 如果子节点比t大,t的位置还要下移
if self.a[j] > t:
# 大于当前,节点上移
self.a[i] = self.a[j]
i = j # j成为新的空位,再查看新空位的左右子节点
else:
# 找到了t的位置,t大于所有子节点
break
# 把新元素放入正确的位置
self.a[i] = t
# 上浮操作
def _swim(self, i):
t = self.a[i]
# 父节点
while i > 0:
par = int((i - 1) / 2)
# 如果父节点比新元素小,父节点下沉
if self.a[par] < t:
self.a[i] = self.a[par]
i = par
else:
break
self.a[i] = t
def push(self, val):
# 在堆的尾巴上添加新元素
self.a.append(val)
# 新元素进行上浮
self._swim(self.n)
self.n += 1
def pop(self):
# 取出a[0]作为返回值
ret = self.a[0]
# 将a[n-1]放到a[0]中
self.a[0] = self.a[self.n - 1]
self.a.pop()
self.n -= 1
# a[0]进行下沉
self._sink(0)
return ret
def size(self):
return self.n
def getLeastNumbers(self, arr, k):
if k <= 0 or (not arr) or len(arr) == 0:
return []
self.a = []
self.n = 0
for i in arr:
# 加入集合
self.push(i)
while self.size() > k:
# 有k+1个元素时,弹出最大元素
self.pop()
return self.a
if __name__ == '__main__':
arr = [4, 5, 1, 6, 2, 7, 3, 8]
k = 4
s = Solution()
print(s.getLeastNumbers(arr, k))
例2-1:内置优先队列实现
'''
0620 数组最小的k个元素
内置的优先级队列
单调队列和优先级队列
单调队列:进队的时候可能会将队列中的元素清空,始终保持单调性,出队的时候,对比,相等就出队,不等时候早就被出队了
push时始终保持单调性,pop时和队首比较
优先级队列:进队的时候找一个合适的位置坐下,等着,出队的时候,始终都是最大的先走
支持push功能,不需要有序。pop时,总是把最大的元素扔出去
'''
import queue
import heapq
class Solution(object):
def getLeastNumbers(self, arr, k):
if k <= 0 or (not arr) or len(arr) == 0:
return []
# 内置优先队列实现
Q = queue.PriorityQueue() # 优先级队列,小堆,需要将元素取反放入
for i in arr:
Q.put(-i)
while Q.qsize() > k:
Q.get()
res = []
while not Q.empty():
res.append(-Q.get())
return res
'''
1.使用内置堆进行操作
heapq.heapify(arr)
return [heapq.heappop(arr) for _ in range(k)]
'''
if __name__ == '__main__':
arr = [4, 5, 1, 6, 2, 7, 3, 8]
k = 4
s = Solution()
print(s.getLeastNumbers(arr, k))
例2-2:前K个高频元素
'''
0620 前K个高频元素
任意顺序返回,如果一样取较小的
k大小的堆
高频元素就是把小的删掉,小堆
堆中存放元素和频率,以频率进行优先级设置
返回顺序任意
'''
import queue
import collections
import heapq
class Solution:
def topKFrequent(self, nums, k):
# 直接获取频率统计字典
dic = collections.Counter(nums)
# 内置优先队列实现
Q = queue.PriorityQueue()
res = []
for cnt, val in dic.items():
Q.put((val, cnt))
while Q.qsize() > k:
Q.get()
while not Q.empty():
res.append(Q.get()[1])
return res
'''
1.内置堆实现
dic = collections.Counter(nums)
heap = [(-freq, value) for value, freq in dic.items()] # 内置的堆是大堆
heapq.heapify(heap)
return [heapq.heappop(heap)[1] for _ in range(k)]
'''
if __name__ == '__main__':
nums = [1, 2, 1, 2, 1, 3]
k = 2
s = Solution()
print(s.topKFrequent(nums, k))
例2-3:前k个高频单词
'''
0621 前K个高频单词
返回顺序是频率由高到低,相同频率按照字典顺序排序
'''
import heapq
class Solution:
def topKFrequent(self, words, k):
# 字典统计数组中元素出现的个数
dic = {}
for x in words:
old = dic.get(x, 0)
dic[x] = old + 1
heap = [(-v, k) for k, v in dic.items()]
heapq.heapify(heap)
ans = sorted(heap)
return [heapq.heappop(ans)[1] for _ in range(k)]
'''
1.直接对字典进行排序
ans = sorted(dic.items(), key=lambda x: (-x[1], x[0]))
return [i for i, j in ans[:k]]
'''
if __name__ == '__main__':
words = ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"]
k = 4
s = Solution()
print(s.topKFrequent(words, k))
3.优先队列困难题目