Heap - 堆

一般情况下,堆通常指的是二叉堆二叉堆是一个近似完全二叉树的数据结构,即披着二叉树羊皮的数组,故使用数组来实现较为便利。子结点的键值或索引总是小于(或者大于)它的父节点,且每个节点的左右子树又是一个二叉堆(大根堆或者小根堆)。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常被用作实现优先队列。

特点

  1. 以数组表示,但是以完全二叉树的方式理解
  2. 唯一能够同时最优地利用空间和时间的方法——最坏情况下也能保证使用 $$2N \log N$$ 次比较和恒定的额外空间。
  3. 在索引从0开始的数组中:
    • 父节点 i 的左子节点在位置(2*i+1)
    • 父节点 i 的右子节点在位置(2*i+2)
    • 子节点 i 的父节点在位置floor((i-1)/2)

堆的基本操作

以大根堆为例,堆的常用操作如下。

  1. 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
  2. 创建最大堆(Build_Max_Heap):将堆所有数据重新排序
  3. 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

其中步骤1是给步骤2和3用的。

Heapsort-example

Python

  1. class MaxHeap:
  2. def __init__(self, array=None):
  3. if array:
  4. self.heap = self._max_heapify(array)
  5. else:
  6. self.heap = []
  7. def _sink(self, array, i):
  8. # move node down the tree
  9. left, right = 2 * i + 1, 2 * i + 2
  10. max_index = i
  11. # should compare two chidren then determine which one to swap with
  12. flag = array[left] > array[right]
  13. if left < len(array) and array[left] > array[max_index] and flag:
  14. max_index = left
  15. if right < len(array) and array[right] > array[max_index] and not flag:
  16. max_index = right
  17. if max_index != i:
  18. array[i], array[max_index] = array[max_index], array[i]
  19. self._sink(array, max_index)
  20. def _swim(self, array, i):
  21. # move node up the tree
  22. if i == 0:
  23. return
  24. father = (i - 1) / 2
  25. if array[father] < array[i]:
  26. array[father], array[i] = array[i], array[father]
  27. self._swim(array, father)
  28. def _max_heapify(self, array):
  29. for i in xrange(len(array) / 2, -1, -1):
  30. self._sink(array, i)
  31. return array
  32. def push(self, item):
  33. self.heap.append(item)
  34. self._swim(self.heap, len(self.heap) - 1)
  35. def pop(self):
  36. self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0]
  37. item = self.heap.pop()
  38. self._sink(self.heap, 0)
  39. return item

C++

  1. #ifndef HEAP_H
  2. #define HEAP_H
  3. #include <algorithm>
  4. #include <functional>
  5. #include <stdexcept>
  6. #include <unordered_map>
  7. #include <utility>
  8. #include <vector>
  9. template <typename T, typename TComparator = std::equal_to<T>,
  10. typename PComparator = std::less<double>,
  11. typename Hasher = std::hash<T> >
  12. class Heap {
  13. public:
  14. /// Constructs an m-ary heap. M should be >= 2
  15. Heap(int m = 2, const PComparator &c = PComparator(),
  16. const Hasher &hash = Hasher(), const TComparator &tcomp = TComparator());
  17. /// Destructor as needed
  18. ~Heap();
  19. /// Adds an item with the provided priority
  20. void push(double pri, const T &item);
  21. /// returns the element at the top of the heap
  22. /// max (if max-heap) or min (if min-heap)
  23. T const &top() const;
  24. /// Removes the top element
  25. void pop();
  26. /// returns true if the heap is empty
  27. bool empty() const;
  28. /// decreaseKey reduces the current priority of
  29. /// item to newpri, moving it up in the heap
  30. /// as appropriate.
  31. void decreaseKey(double newpri, const T &item);
  32. private:
  33. /// Add whatever helper functions you need below
  34. void trickleUp(int loc);
  35. void trickleDown(int loc);
  36. // These should be all the data members you need.
  37. std::vector<std::pair<double, T> > store_;
  38. int m_; // degree
  39. PComparator c_;
  40. std::unordered_map<T, size_t, Hasher, TComparator> keyToLocation_;
  41. };
  42. // Complete
  43. template <typename T, typename TComparator, typename PComparator,
  44. typename Hasher>
  45. Heap<T, TComparator, PComparator, Hasher>::Heap(int m, const PComparator &c,
  46. const Hasher &hash,
  47. const TComparator &tcomp)
  48. : store_(), m_(m), c_(c), keyToLocation_(100, hash, tcomp) {
  49. }
  50. // Complete
  51. template <typename T, typename TComparator, typename PComparator,
  52. typename Hasher>
  53. Heap<T, TComparator, PComparator, Hasher>::~Heap() {
  54. }
  55. template <typename T, typename TComparator, typename PComparator,
  56. typename Hasher>
  57. void Heap<T, TComparator, PComparator, Hasher>::push(double priority,
  58. const T &item) {
  59. // You complete.
  60. std::pair<double, T> temp(priority, item);
  61. store_.push_back(temp);
  62. keyToLocation_[item] = store_.size();
  63. // insert(std::make_pair(item, store_.size()));
  64. trickleUp(store_.size()-1);
  65. }
  66. template <typename T, typename TComparator, typename PComparator,
  67. typename Hasher>
  68. void Heap<T, TComparator, PComparator, Hasher>::trickleUp(int loc) {
  69. int parent = (loc-1)/m_;
  70. while(parent >= 0 && c_(store_[loc].first, store_[parent].first)) {
  71. //swap loc with parent
  72. std::pair<double, T> temp = store_[loc];
  73. store_[loc] = store_[parent];
  74. store_[parent] = temp;
  75. double to_swap = keyToLocation_[store_[loc].second];
  76. keyToLocation_[store_[loc].second] = keyToLocation_[store_[parent].second];
  77. keyToLocation_[store_[parent].second] = to_swap;
  78. loc = parent;
  79. parent = (loc-1)/m_;
  80. }
  81. }
  82. template <typename T, typename TComparator, typename PComparator,
  83. typename Hasher>
  84. void Heap<T, TComparator, PComparator, Hasher>::decreaseKey(double priority,
  85. const T &item) {
  86. std::pair<double, T> temp = store_[keyToLocation_[item]];
  87. temp.first = priority;
  88. trickleUp(keyToLocation_[item]);
  89. }
  90. template <typename T, typename TComparator, typename PComparator,
  91. typename Hasher>
  92. T const &Heap<T, TComparator, PComparator, Hasher>::top() const {
  93. // Here we use exceptions to handle the case of trying
  94. // to access the top element of an empty heap
  95. if (empty()) {
  96. throw std::logic_error("can't top an empty heap");
  97. }
  98. return store_[0].second;
  99. }
  100. /// Removes the top element
  101. template <typename T, typename TComparator, typename PComparator,
  102. typename Hasher>
  103. void Heap<T, TComparator, PComparator, Hasher>::pop() {
  104. if (empty()) {
  105. throw std::logic_error("can't pop an empty heap");
  106. }
  107. store_[0] = store_[store_.size()-1];
  108. keyToLocation_.erase(store_[0].second);
  109. store_.pop_back();
  110. if(empty()) return;
  111. trickleDown(0);
  112. }
  113. template <typename T, typename TComparator, typename PComparator,
  114. typename Hasher>
  115. void Heap<T, TComparator, PComparator, Hasher>::trickleDown(int loc) {
  116. if (loc*m_+1 > store_.size()-1) return;
  117. int smallerChild = m_*loc+1; // start w/ left
  118. for (size_t i = 1; i < m_; i++) {
  119. if (m_*loc+i < store_.size()) {//if the right exist
  120. int rChild = m_*loc+i+1;
  121. if(c_(store_[rChild].first, store_[smallerChild].first)) {
  122. smallerChild = rChild;
  123. }
  124. }
  125. }
  126. if(c_(store_[smallerChild].first, store_[loc].first)) {
  127. //swap smallerChild and loc
  128. std::pair<double, T> temp = store_[loc];
  129. store_[loc] = store_[smallerChild];
  130. store_[smallerChild] = temp;
  131. double to_swap = keyToLocation_[store_[loc].second];
  132. keyToLocation_[store_[loc].second] = keyToLocation_[store_[smallerChild].second];
  133. keyToLocation_[store_[smallerChild].second] = to_swap;
  134. trickleDown(smallerChild);
  135. }
  136. }
  137. /// returns true if the heap is empty
  138. template <typename T, typename TComparator, typename PComparator,
  139. typename Hasher>
  140. bool Heap<T, TComparator, PComparator, Hasher>::empty() const {
  141. return store_.empty();
  142. }
  143. #endif