本文是根据穷码农的LeetCode刷题建议而进行专项练习时记录的心得。
这次的题虽然只有三道,但都是Hard模式,花费了我很多时间。主要是自己以前从来没用过”堆“这个数据结构,导致自己需要相当一部分时间去从底层了解并实现它。做题时,自己也有很多思路没能想到,只能先参考大佬们的解题笔记。
不过,在自己了解了堆的特性后,觉得它理解起来并不困难,都是基于列表的一系列操作(优先队列)而实现的。这个专题之所以叫”双堆“,也是因为一般使用堆得同时用到”大顶堆“和”小顶堆“这两个概念,尤其是涉及到求中间值一类题型的时候。关于堆的具体实现,我参考了以下这篇文章:
【算法日积月累】9-堆与优先队列 | 算法与数据结构、机器学习、深度学习
下面是我从底层实现堆的代码片段:
"""
The implementation of data structure 'heap'(max and min) through "priority queue".
Reference: https://www.liwei.party/2019/01/10/algorithms-and-data-structures/priority-queue/
"""
class MaxHeap:
def __init__(self, capability):
"""
Initiate the queue.
Note: because the heap behaves like a tree, we use a list to "record" this tree. In addition, the first
element starts with index 1, not 0.
:return:
"""
# define how many elements it can contain
self.capability = capability
# define the list to store data (with pre-defined space). '+1': because index starts with 1
self.data = [None for _ in range(capability + 1)]
# define the number of elemente
self._count = 0
def get_size(self) -> int:
"""
get the size of the heap
:return:
"""
return self._count
def set_size(self, size):
"""
set the size of the heap
:return:
"""
self._count = size
def is_empty(self) -> bool:
"""
determine whether it is empty
:return:
"""
return self._count == 0
def insert(self, num):
"""
insert new data to heap
:param num: the new element
:return:
"""
if self._count == self.capability:
raise Exception("Heap reaches the limitation.")
# insert the element to the tail first
self._count += 1
self.data[self._count] = num
# see if it can be moved up
self.shift_up(self._count)
def shift_up(self, itemPos):
"""
'swim' the element to higher place if it is larger than others
:param numPos: the index of the element
:return:
"""
try:
target = self.data[itemPos]
# father: itemPos // 2
while itemPos > 1 and self.data[itemPos // 2] < target:
self.data[itemPos] = self.data[itemPos // 2]
itemPos //= 2
self.data[itemPos] = target
except TypeError:
print(TypeError)
return
def shift_down(self, itemPos):
"""
'sink' the element to lower place if it is smaller than others
method: as long as the 'tree' has children, it will keep replacing.
:param numPos: the index of the element
:return:
"""
try:
target = self.data[itemPos]
# children (left): itemPos * 2
# children (right): itemPos * 2 + 1
while itemPos * 2 <= self._count:
children = itemPos * 2
# attention to the next half: left children and right children needs to be compared
if children + 1 <= self._count and self.data[children + 1] > self.data[children]:
children = children + 1
# attention: check the false condition first. If the children is smaller than target,
# no need to keep looping (cannot write to another format —— 'itemPos' will keep changing)
if self.data[children] <= target:
break
self.data[itemPos] = self