堆定義
- 堆是一個樹形結構,其底層實現是一棵完全二叉樹。而完全二叉樹是一層一層按照進入的順序排成的。按照這個特性,我們可以用數組來按照完全二叉樹實現堆;
堆的一些性質
一個大頂堆示例
上面的示例就是一個完全二叉樹,也是一個大頂堆。而大頂堆有一個性質:每一個節點的值都小于它父節點的值。(特別注意,每一個節點的值的大小與它所處的深度沒有必然的聯系。)
我們可以很容易的根據任意一個節點的索引(除去根節點)找到它的父節點的索引以及其左右子節點的索引,如果當前節點的索引為index,那么:
- 當前節點的父節點 = (index-1) / 2 (這里我們將結果取整);
- 當前節點的左子節點 = index * 2 + 1;
- 當前節點的右子節點 = index * 2 + 2。
代碼實現一個堆
根據上述的堆的一些性質,我們可以自己實現一個堆。
# 實現一個最大堆
class MaxHeap(object):
"""
實現一個大頂堆
"""
def __init__(self):
self.array = [] # 用一個數組存放堆中元素
def heapify(self, array):
"""
對傳入的數組進行堆化
"""
for a in array:
self.push(a)
return self.array
def get_size(self):
"""
返回堆的大小
"""
return len(self.array)
def _parent(self, index):
"""
返回某個節點的父節點
"""
if index == 0:
raise Exception('Index 0 has no parent')
return int((index - 1) / 2)
def _left_child(self, index):
"""
返回左孩子節點的索引
"""
return index * 2 + 1
def _right_child(self, index):
"""
返回右邊孩子節點的索引
"""
return index * 2 + 2
def _shift_up(self, k):
"""
節點上移動,將當前節點與其父親節點比較大小,如果比父親節點大,
則交換其位置,重復只執行上述過程,直到當前節點比父親節點小。
"""
while k > 0 and self.array[self._parent(k)] < self.array[k]:
# 交換節點與父節點的值
self.array[self._parent(k)], self.array[k] = self.array[k], self.array[self._parent(k)]
k = self._parent(k)
def _shift_down(self, k):
"""
節點下移動, 當前節點與它左右孩子中最大的節點交換位置
"""
while self._left_child(k) < self.get_size():
choose_index = self._left_child(k) # 左孩子的索引
# 先比較左右孩子的大小,選擇較大的那個孩子再與父親節點進行比較
if choose_index + 1 < self.get_size() and self.array[choose_index + 1] > self.array[choose_index]:
choose_index = self._right_child(k)
if self.array[choose_index] <= self.array[k]: # 如果最大的孩子比父親節點小,退出循環
break
# 否則父親節點和最大的子節點交換位置
self.array[choose_index], self.array[k] = self.array[k], self.array[choose_index]
k = choose_index # 進行下次循環
def push(self, value):
"""
添加一個元素后,需要對堆重新進行堆化,具體過程就是對堆尾元素執行上移操作;
"""
self.array.append(value)
self._shift_up(self.get_size() - 1) # 相當于對堆進行重新堆化
def pop(self):
"""
返回堆頂元素,將堆頂元素和堆最后一個元素交換,
然后返回最后一個元素,最后對堆頂元素進行下沉操作(重新堆化)
"""
ret = self.find_max()
self.array[0], self.array[self.get_size() - 1] = self.array[self.get_size() - 1], self.array[0]
self.array.pop(-1) # 刪除最后一個元素
self._shift_down(0)
return ret
def find_max(self):
"""
查看堆中的最大值
"""
if self.array:
return self.array[0]
else:
raise Exception('Empty heap has no value')
# 測試我們實現的大頂堆
test = [12, 11, 10, 9, 6, 7, 8, 13]
max_heap = MaxHeap()
print(max_heap.heapify(test)) # 對一個數組執行堆化
print(max_heap.pop()) # 彈出堆頂元素
print(max_heap.array)
max_heap.push(14) # 推入一個元素
print(max_heap.array)