概要
上一章介紹了左傾堆的基本概念,並通過C語言實現了左傾堆。本章是左傾堆的C++實現。
目錄
1. 左傾堆的介紹
2. 左傾堆的圖文解析
3. 左傾堆的C++實現(完整源碼)
4. 左傾堆的C++測試程序
轉載請注明出處:http://www.cnblogs.com/skywang12345/p/3638342.html
更多內容:數據結構與算法系列 目錄
(01) 左傾堆(一)之 圖文解析 和 C語言的實現
(02) 左傾堆(二)之 C++的實現
(03) 左傾堆(三)之 Java的實現
左傾堆的介紹
左傾堆(leftist tree 或 leftist heap),又被成為左偏樹、左偏堆,最左堆等。
它和二叉堆一樣,都是優先隊列實現方式。當優先隊列中涉及到"對兩個優先隊列進行合並"的問題時,二叉堆的效率就無法令人滿意了,而本文介紹的左傾堆,則可以很好地解決這類問題。
左傾堆的定義
上圖是一顆左傾樹,它的節點除了和二叉樹的節點一樣具有左右子樹指針外,還有兩個屬性:鍵值和零距離。
(01) 鍵值的作用是來比較節點的大小,從而對節點進行排序。
(02) 零距離(英文名NPL,即Null Path Length)則是從一個節點到一個"最近的不滿節點"的路徑長度。不滿節點是指該該節點的左右孩子至少有有一個為NULL。葉節點的NPL為0,NULL節點的NPL為-1。
左傾堆有以下幾個基本性質:
[性質1] 節點的鍵值小於或等於它的左右子節點的鍵值。
[性質2] 節點的左孩子的NPL >= 右孩子的NPL。
[性質3] 節點的NPL = 它的右孩子的NPL + 1。
左傾堆的圖文解析
合並操作是左傾堆的重點。合並兩個左傾堆的基本思想如下:
(01) 如果一個空左傾堆與一個非空左傾堆合並,返回非空左傾堆。
(02) 如果兩個左傾堆都非空,那么比較兩個根節點,取較小堆的根節點為新的根節點。將"較小堆的根節點的右孩子"和"較大堆"進行合並。
(03) 如果新堆的右孩子的NPL > 左孩子的NPL,則交換左右孩子。
(04) 設置新堆的根節點的NPL = 右子堆NPL + 1
下面通過圖文演示合並以下兩個堆的過程。
提示:這兩個堆的合並過程和測試程序相對應!
第1步:將"較小堆(根為10)的右孩子"和"較大堆(根為11)"進行合並。
合並的結果,相當於將"較大堆"設置"較小堆"的右孩子,如下圖所示:
第2步:將上一步得到的"根11的右子樹"和"根為12的樹"進行合並,得到的結果如下:
第3步:將上一步得到的"根12的右子樹"和"根為13的樹"進行合並,得到的結果如下:
第4步:將上一步得到的"根13的右子樹"和"根為16的樹"進行合並,得到的結果如下:
第5步:將上一步得到的"根16的右子樹"和"根為23的樹"進行合並,得到的結果如下:
至此,已經成功的將兩棵樹合並成為一棵樹了。接下來,對新生成的樹進行調節。
第6步:上一步得到的"樹16的右孩子的NPL > 左孩子的NPL",因此交換左右孩子。得到的結果如下:
第7步:上一步得到的"樹12的右孩子的NPL > 左孩子的NPL",因此交換左右孩子。得到的結果如下:
第8步:上一步得到的"樹10的右孩子的NPL > 左孩子的NPL",因此交換左右孩子。得到的結果如下:
至此,合並完畢。上面就是合並得到的左傾堆!
下面看看左傾堆的基本操作的代碼
1. 基本定義
template <class T> class LeftistNode{ public: T key; // 關鍵字(鍵值) int npl; // 零路經長度(Null Path Length) LeftistNode *left; // 左孩子 LeftistNode *right; // 右孩子 LeftistNode(T value, LeftistNode *l, LeftistNode *r): key(value), npl(0), left(l),right(r) {} };
LeftistNode是左傾堆對應的節點類。
template <class T> class LeftistHeap { private: LeftistNode<T> *mRoot; // 根結點 public: LeftistHeap(); ~LeftistHeap(); // 前序遍歷"左傾堆" void preOrder(); // 中序遍歷"左傾堆" void inOrder(); // 后序遍歷"左傾堆" void postOrder(); // 將other的左傾堆合並到this中。 void merge(LeftistHeap<T>* other); // 將結點(key為節點鍵值)插入到左傾堆中 void insert(T key); // 刪除結點(key為節點鍵值) void remove(); // 銷毀左傾堆 void destroy(); // 打印左傾堆 void print(); private: // 前序遍歷"左傾堆" void preOrder(LeftistNode<T>* heap) const; // 中序遍歷"左傾堆" void inOrder(LeftistNode<T>* heap) const; // 后序遍歷"左傾堆" void postOrder(LeftistNode<T>* heap) const; // 交換節點x和節點y void swapNode(LeftistNode<T> *&x, LeftistNode<T> *&y); // 合並"左傾堆x"和"左傾堆y" LeftistNode<T>* merge(LeftistNode<T>* &x, LeftistNode<T>* &y); // 將結點(z)插入到左傾堆(heap)中 LeftistNode<T>* insert(LeftistNode<T>* &heap, T key); // 刪除左傾堆(heap)中的結點(z),並返回被刪除的結點 LeftistNode<T>* remove(LeftistNode<T>* &heap); // 銷毀左傾堆 void destroy(LeftistNode<T>* &heap); // 打印左傾堆 void print(LeftistNode<T>* heap, T key, int direction); };
LeftistHeap是左傾堆類,它包含了左傾堆的根節點,以及左傾堆的操作。
2. 合並
/* * 合並"左傾堆x"和"左傾堆y" */ template <class T> LeftistNode<T>* LeftistHeap<T>::merge(LeftistNode<T>* &x, LeftistNode<T>* &y) { if(x == NULL) return y; if(y == NULL) return x; // 合並x和y時,將x作為合並后的樹的根; // 這里的操作是保證: x的key < y的key if(x->key > y->key) swapNode(x, y); // 將x的右孩子和y合並,"合並后的樹的根"是x的右孩子。 x->right = merge(x->right, y); // 如果"x的左孩子為空" 或者 "x的左孩子的npl<右孩子的npl" // 則,交換x和y if(x->left == NULL || x->left->npl < x->right->npl) { LeftistNode<T> *tmp = x->left; x->left = x->right; x->right = tmp; } // 設置合並后的新樹(x)的npl if (x->right == NULL || x->left == NULL) x->npl = 0; else x->npl = (x->left->npl > x->right->npl) ? (x->right->npl + 1) : (x->left->npl + 1); return x; } /* * 將other的左傾堆合並到this中。 */ template <class T> void LeftistHeap<T>::merge(LeftistHeap<T>* other) { mRoot = merge(mRoot, other->mRoot); }
merge(x, y)是內部接口,作用是合並x和y這兩個左傾堆,並返回得到的新堆的根節點。
merge(other)是外部接口,作用是將other合並到當前堆中。
3. 添加
/* * 將結點插入到左傾堆中,並返回根節點 * * 參數說明: * heap 左傾堆的根結點 * key 插入的結點的鍵值 * 返回值: * 根節點 */ template <class T> LeftistNode<T>* LeftistHeap<T>::insert(LeftistNode<T>* &heap, T key) { LeftistNode<T> *node; // 新建結點 // 新建節點 node = new LeftistNode<T>(key, NULL, NULL); if (node==NULL) { cout << "ERROR: create node failed!" << endl; return heap; } return merge(mRoot, node); } template <class T> void LeftistHeap<T>::insert(T key) { mRoot = insert(mRoot, key); }
insert(heap, key)是內部接口,它是以節點為操作對象的。
insert(key)是外部接口,它的作用是新建鍵值為key的節點,並將其加入到當前左傾堆中。
4. 刪除
/* * 刪除結點,返回根節點 * * 參數說明: * heap 左傾堆的根結點 * 返回值: * 根節點 */ template <class T> LeftistNode<T>* LeftistHeap<T>::remove(LeftistNode<T>* &heap) { if (heap == NULL) return NULL; LeftistNode<T> *l = heap->left; LeftistNode<T> *r = heap->right; // 刪除根節點 delete heap; return merge(l, r); // 返回左右子樹合並后的新樹 } template <class T> void LeftistHeap<T>::remove() { mRoot = remove(mRoot); }
remove(heap)是內部接口,它是以節點為操作對象的。
remove()是外部接口,它的作用是刪除左傾堆的最小節點。
注意:關於左傾堆的"前序遍歷"、"中序遍歷"、"后序遍歷"、"打印"、"銷毀"等接口就不再單獨介紹了。后文的源碼中有給出它們的實現代碼,Please RTFSC(Read The Fucking Source Code)!
左傾堆的C++實現(完整源碼)
左傾堆的實現文件(LeftistHeap.h)

1 /** 2 * C++: 左傾堆 3 * 4 * @author skywang 5 * @date 2014/03/31 6 */ 7 8 #ifndef _LEFTIST_TREE_HPP_ 9 #define _LEFTIST_TREE_HPP_ 10 11 #include <iomanip> 12 #include <iostream> 13 using namespace std; 14 15 template <class T> 16 class LeftistNode{ 17 public: 18 T key; // 關鍵字(鍵值) 19 int npl; // 零路經長度(Null Path Length) 20 LeftistNode *left; // 左孩子 21 LeftistNode *right; // 右孩子 22 23 LeftistNode(T value, LeftistNode *l, LeftistNode *r): 24 key(value), npl(0), left(l),right(r) {} 25 }; 26 27 template <class T> 28 class LeftistHeap { 29 private: 30 LeftistNode<T> *mRoot; // 根結點 31 32 public: 33 LeftistHeap(); 34 ~LeftistHeap(); 35 36 // 前序遍歷"左傾堆" 37 void preOrder(); 38 // 中序遍歷"左傾堆" 39 void inOrder(); 40 // 后序遍歷"左傾堆" 41 void postOrder(); 42 43 // 將other的左傾堆合並到this中。 44 void merge(LeftistHeap<T>* other); 45 // 將結點(key為節點鍵值)插入到左傾堆中 46 void insert(T key); 47 // 刪除結點(key為節點鍵值) 48 void remove(); 49 50 // 銷毀左傾堆 51 void destroy(); 52 53 // 打印左傾堆 54 void print(); 55 private: 56 57 // 前序遍歷"左傾堆" 58 void preOrder(LeftistNode<T>* heap) const; 59 // 中序遍歷"左傾堆" 60 void inOrder(LeftistNode<T>* heap) const; 61 // 后序遍歷"左傾堆" 62 void postOrder(LeftistNode<T>* heap) const; 63 64 // 交換節點x和節點y 65 void swapNode(LeftistNode<T> *&x, LeftistNode<T> *&y); 66 // 合並"左傾堆x"和"左傾堆y" 67 LeftistNode<T>* merge(LeftistNode<T>* &x, LeftistNode<T>* &y); 68 // 將結點(z)插入到左傾堆(heap)中 69 LeftistNode<T>* insert(LeftistNode<T>* &heap, T key); 70 // 刪除左傾堆(heap)中的結點(z),並返回被刪除的結點 71 LeftistNode<T>* remove(LeftistNode<T>* &heap); 72 73 // 銷毀左傾堆 74 void destroy(LeftistNode<T>* &heap); 75 76 // 打印左傾堆 77 void print(LeftistNode<T>* heap, T key, int direction); 78 }; 79 80 /* 81 * 構造函數 82 */ 83 template <class T> 84 LeftistHeap<T>::LeftistHeap():mRoot(NULL) 85 { 86 } 87 88 /* 89 * 析構函數 90 */ 91 template <class T> 92 LeftistHeap<T>::~LeftistHeap() 93 { 94 destroy(mRoot); 95 } 96 97 /* 98 * 前序遍歷"左傾堆" 99 */ 100 template <class T> 101 void LeftistHeap<T>::preOrder(LeftistNode<T>* heap) const 102 { 103 if(heap != NULL) 104 { 105 cout<< heap->key << " " ; 106 preOrder(heap->left); 107 preOrder(heap->right); 108 } 109 } 110 111 template <class T> 112 void LeftistHeap<T>::preOrder() 113 { 114 preOrder(mRoot); 115 } 116 117 /* 118 * 中序遍歷"左傾堆" 119 */ 120 template <class T> 121 void LeftistHeap<T>::inOrder(LeftistNode<T>* heap) const 122 { 123 if(heap != NULL) 124 { 125 inOrder(heap->left); 126 cout<< heap->key << " " ; 127 inOrder(heap->right); 128 } 129 } 130 131 template <class T> 132 void LeftistHeap<T>::inOrder() 133 { 134 inOrder(mRoot); 135 } 136 137 /* 138 * 后序遍歷"左傾堆" 139 */ 140 template <class T> 141 void LeftistHeap<T>::postOrder(LeftistNode<T>* heap) const 142 { 143 if(heap != NULL) 144 { 145 postOrder(heap->left); 146 postOrder(heap->right); 147 cout<< heap->key << " " ; 148 } 149 } 150 151 template <class T> 152 void LeftistHeap<T>::postOrder() 153 { 154 postOrder(mRoot); 155 } 156 157 /* 158 * 交換兩個節點的內容 159 */ 160 template <class T> 161 void LeftistHeap<T>::swapNode(LeftistNode<T> *&x, LeftistNode<T> *&y) 162 { 163 LeftistNode<T> *tmp = x; 164 x = y; 165 y = tmp; 166 } 167 168 169 /* 170 * 合並"左傾堆x"和"左傾堆y" 171 */ 172 template <class T> 173 LeftistNode<T>* LeftistHeap<T>::merge(LeftistNode<T>* &x, LeftistNode<T>* &y) 174 { 175 if(x == NULL) 176 return y; 177 if(y == NULL) 178 return x; 179 180 // 合並x和y時,將x作為合並后的樹的根; 181 // 這里的操作是保證: x的key < y的key 182 if(x->key > y->key) 183 swapNode(x, y); 184 185 // 將x的右孩子和y合並,"合並后的樹的根"是x的右孩子。 186 x->right = merge(x->right, y); 187 188 // 如果"x的左孩子為空" 或者 "x的左孩子的npl<右孩子的npl" 189 // 則,交換x和y 190 if(x->left == NULL || x->left->npl < x->right->npl) 191 { 192 LeftistNode<T> *tmp = x->left; 193 x->left = x->right; 194 x->right = tmp; 195 } 196 // 設置合並后的新樹(x)的npl 197 if (x->right == NULL || x->left == NULL) 198 x->npl = 0; 199 else 200 x->npl = (x->left->npl > x->right->npl) ? (x->right->npl + 1) : (x->left->npl + 1); 201 202 return x; 203 } 204 205 /* 206 * 將other的左傾堆合並到this中。 207 */ 208 template <class T> 209 void LeftistHeap<T>::merge(LeftistHeap<T>* other) 210 { 211 mRoot = merge(mRoot, other->mRoot); 212 } 213 214 /* 215 * 將結點插入到左傾堆中,並返回根節點 216 * 217 * 參數說明: 218 * heap 左傾堆的根結點 219 * key 插入的結點的鍵值 220 * 返回值: 221 * 根節點 222 */ 223 template <class T> 224 LeftistNode<T>* LeftistHeap<T>::insert(LeftistNode<T>* &heap, T key) 225 { 226 LeftistNode<T> *node; // 新建結點 227 228 // 新建節點 229 node = new LeftistNode<T>(key, NULL, NULL); 230 if (node==NULL) 231 { 232 cout << "ERROR: create node failed!" << endl; 233 return heap; 234 } 235 236 return merge(mRoot, node); 237 } 238 239 template <class T> 240 void LeftistHeap<T>::insert(T key) 241 { 242 mRoot = insert(mRoot, key); 243 } 244 245 /* 246 * 刪除結點,返回根節點 247 * 248 * 參數說明: 249 * heap 左傾堆的根結點 250 * 返回值: 251 * 根節點 252 */ 253 template <class T> 254 LeftistNode<T>* LeftistHeap<T>::remove(LeftistNode<T>* &heap) 255 { 256 if (heap == NULL) 257 return NULL; 258 259 LeftistNode<T> *l = heap->left; 260 LeftistNode<T> *r = heap->right; 261 262 // 刪除根節點 263 delete heap; 264 265 return merge(l, r); // 返回左右子樹合並后的新樹 266 } 267 268 template <class T> 269 void LeftistHeap<T>::remove() 270 { 271 mRoot = remove(mRoot); 272 } 273 274 /* 275 * 銷毀左傾堆 276 */ 277 template <class T> 278 void LeftistHeap<T>::destroy(LeftistNode<T>* &heap) 279 { 280 if (heap==NULL) 281 return ; 282 283 if (heap->left != NULL) 284 destroy(heap->left); 285 if (heap->right != NULL) 286 destroy(heap->right); 287 288 delete heap; 289 } 290 291 template <class T> 292 void LeftistHeap<T>::destroy() 293 { 294 destroy(mRoot); 295 } 296 297 /* 298 * 打印"二叉查找樹" 299 * 300 * key -- 節點的鍵值 301 * direction -- 0,表示該節點是根節點; 302 * -1,表示該節點是它的父結點的左孩子; 303 * 1,表示該節點是它的父結點的右孩子。 304 */ 305 template <class T> 306 void LeftistHeap<T>::print(LeftistNode<T>* heap, T key, int direction) 307 { 308 if(heap != NULL) 309 { 310 if(direction==0) // heap是根節點 311 cout << setw(2) << heap->key << "(" << heap->npl << ") is root" << endl; 312 else // heap是分支節點 313 cout << setw(2) << heap->key << "(" << heap->npl << ") is " << setw(2) << key << "'s " << setw(12) << (direction==1?"right child" : "left child") << endl; 314 315 print(heap->left, heap->key, -1); 316 print(heap->right,heap->key, 1); 317 } 318 } 319 320 template <class T> 321 void LeftistHeap<T>::print() 322 { 323 if (mRoot != NULL) 324 print(mRoot, mRoot->key, 0); 325 } 326 #endif
左傾堆的測試程序(LeftistHeapTest.cpp)

1 /** 2 * C 語言: 左傾堆 3 * 4 * @author skywang 5 * @date 2013/11/07 6 */ 7 8 #include <iostream> 9 #include "LeftistHeap.h" 10 using namespace std; 11 12 int main() 13 { 14 int i; 15 int a[]= {10,40,24,30,36,20,12,16}; 16 int b[]= {17,13,11,15,19,21,23}; 17 int alen=sizeof(a)/sizeof(a[0]); 18 int blen=sizeof(b)/sizeof(b[0]); 19 LeftistHeap<int>* ha=new LeftistHeap<int>(); 20 LeftistHeap<int>* hb=new LeftistHeap<int>(); 21 22 cout << "== 左傾堆(ha)中依次添加: "; 23 for(i=0; i<alen; i++) 24 { 25 cout << a[i] <<" "; 26 ha->insert(a[i]); 27 } 28 cout << "\n== 左傾堆(ha)的詳細信息: " << endl; 29 ha->print(); 30 31 32 cout << "\n== 左傾堆(hb)中依次添加: "; 33 for(i=0; i<blen; i++) 34 { 35 cout << b[i] <<" "; 36 hb->insert(b[i]); 37 } 38 cout << "\n== 左傾堆(hb)的詳細信息: " << endl; 39 hb->print(); 40 41 42 // 將"左傾堆hb"合並到"左傾堆ha"中。 43 ha->merge(hb); 44 cout << "\n== 合並ha和hb后的詳細信息: " << endl; 45 ha->print(); 46 47 48 // 銷毀 49 ha->destroy(); 50 51 return 0; 52 }
左傾堆的C++測試程序
左傾堆的測試程序已經包含在它的實現文件(LeftistHeapTest.cpp)中了,這里僅給出它的運行結果:
== 左傾堆(ha)中依次添加: 10 40 24 30 36 20 12 16 == 左傾堆(ha)的詳細信息: 10(2) is root 24(1) is 10's left child 30(0) is 24's left child 36(0) is 24's right child 12(1) is 10's right child 20(0) is 12's left child 40(0) is 20's left child 16(0) is 12's right child == 左傾堆(hb)中依次添加: 17 13 11 15 19 21 23 == 左傾堆(hb)的詳細信息: 11(2) is root 15(1) is 11's left child 19(0) is 15's left child 21(0) is 15's right child 13(1) is 11's right child 17(0) is 13's left child 23(0) is 13's right child == 合並ha和hb后的詳細信息: 10(2) is root 11(2) is 10's left child 15(1) is 11's left child 19(0) is 15's left child 21(0) is 15's right child 12(1) is 11's right child 13(1) is 12's left child 17(0) is 13's left child 16(0) is 13's right child 23(0) is 16's left child 20(0) is 12's right child 40(0) is 20's left child 24(1) is 10's right child 30(0) is 24's left child 36(0) is 24's right child