二叉堆是一種特殊的堆,二叉堆是完全二元樹(二叉樹)或者是近似完全二元樹(二叉樹)。
二叉堆有兩種:最大堆和最小堆。
最大堆:父結點的鍵值總是大於或等於任何一個子結點的鍵值;
最小堆:父結點的鍵值總是小於或等於任何一個子節點的鍵值。
二叉堆一般都通過"數組"來實現。數組實現的二叉堆,父節點和子節點的位置存在一定的關系。我們將"二叉堆的第一個元素"放在數組索引0的位置。
假設"第一個元素"在數組中的索引為 0 的話,則父節點和子節點的位置關系如下:
1、索引為i的左孩子的索引是 (2*i+1);
2、索引為i的左孩子的索引是 (2*i+2);
3、索引為i的父結點的索引是 floor((i-1)/2);
二叉堆這種有序隊列如何入隊呢?
假設要在這個二叉堆里入隊一個單元,只要在數組末尾加入這個元素,然后把這個元素往上挪,直到挪不動,經過了這種復雜度為Ο(logn)的操作,二叉堆性質沒有變化。
那如何出隊呢?
我們習慣將二叉堆畫成樹的形式,但本質上還是用數組實現的。
具體代碼如下:
1 #include <vector> 2 #include <iostream> 3 using namespace std; 4 5 template <typename T> 6 class MinHeap 7 { 8 public: 9 vector<T> m_array; 10 int m_size;//總容量 11 private: 12 //最小堆的向下調整算法 13 void FilterDown(int start, int end); 14 //最小堆的向上調整算法 15 void FilterUp(int start); 16 public: 17 MinHeap(); 18 MinHeap(int capacity); 19 MinHeap(vector<T> data); 20 ~MinHeap(); 21 22 //返回data在vector中的索引 23 int GetIndex(T data); 24 //刪除最小堆中的data 25 bool Remove(T data); 26 //將data插入到最小堆中 27 void Insert(T data); 28 //打印 29 void Print(); 30 }; 31 32 template <typename T> 33 MinHeap<T>::MinHeap(vector<T> data) 34 { 35 for (auto val : data) 36 { 37 Insert(val); 38 } 39 } 40 41 42 template <typename T> 43 MinHeap<T>::MinHeap(int capacity) 44 { 45 m_array.reserve(capacity); 46 m_capacity = capacity; 47 } 48 49 template <typename T> 50 MinHeap<T>::MinHeap() 51 { 52 m_array.reserve(100); 53 } 54 55 56 template <typename T> 57 MinHeap<T>::~MinHeap() 58 { 59 m_size = 0; 60 m_array.clear(); 61 } 62 63 //得到data的索引,-1表示未找到 64 template <typename T> 65 int MinHeap<T>::GetIndex(T data) 66 { 67 for (int i = 0; i < m_size; ++i) 68 { 69 if (m_array[i] == data) 70 return i; 71 } 72 return -1; 73 } 74 75 76 /* 77 * 最小堆的向下調整算法 78 * 79 * 數組實現的堆中,第N個節點的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。 80 * 81 * start -- 被下調節點的起始位置(一般為0,表示從第1個開始) 82 * end -- 截至范圍(一般為數組中最后一個元素的索引) 83 */ 84 template <typename T> 85 void MinHeap<T>::FilterDown(int start, int end) 86 { 87 int current = start;//當前結點位置 88 int left = 2 * current + 1;//左兒子位置 89 T tmp = m_array[current];//當前結點大小 90 91 while (left <= end) 92 { 93 if (left < end && m_array[left] > m_array[left + 1]) 94 { 95 left++;//左右孩子選較小者 96 } 97 if (tmp <= m_array[left]) 98 break;//調整結束 99 else 100 { 101 m_array[current] = m_array[left]; 102 current = left; 103 left = 2 * left + 1; 104 } 105 } 106 m_array[current] = tmp; 107 } 108 109 //刪除最小堆中的data 110 template <typename T> 111 bool MinHeap<T>::Remove(T data) 112 { 113 int index; 114 if (m_size == 0) 115 return false; 116 index = GetIndex(data); 117 if (index == -1) 118 return false; 119 m_array[index] = m_array[--m_size]; 120 m_array.erase(m_array.end() - 1); 121 FilterDown(index, m_size - 1); 122 return true; 123 } 124 125 //向上調整 126 template <typename T> 127 void MinHeap<T>::FilterUp(int start) 128 { 129 int current = start; 130 int p = (current - 1) / 2;//父結點位置 131 T tmp = m_array[current]; 132 while (current > 0) 133 { 134 if (m_array[p] <= tmp) 135 break; 136 else 137 { 138 m_array[current] = m_array[p]; 139 current = p; 140 p = (p - 1) / 2; 141 } 142 } 143 m_array[current] = tmp; 144 } 145 146 147 template <typename T> 148 void MinHeap<T>::Insert(T data) 149 { 150 m_array.push_back(data); 151 m_size++; 152 FilterUp(m_size - 1); 153 } 154 155 156 template <typename T> 157 void MinHeap<T>::Print() 158 { 159 for (auto val : m_array) 160 { 161 cout << val << " "; 162 } 163 }
測試:
1 #include "BinaryHeap.h" 2 3 int main() 4 { 5 int tmp; 6 vector<int> vec{ 80, 40, 30, 60, 90, 70, 10, 50, 20 }; 7 MinHeap<int> heap(vec); 8 cout << "最小堆為:"; 9 heap.Print(); 10 cout << "\n請輸入要添加的元素"; 11 cin >> tmp; 12 heap.Insert(tmp); 13 cout << "添加之后最小堆為:"; 14 heap.Print(); 15 cout << "\n請輸入要刪除的元素:"; 16 cin >> tmp; 17 heap.Remove(tmp); 18 cout << "刪除之后最小堆為:"; 19 heap.Print(); 20 21 }