堆之左式堆和斜堆
d-堆
類似于二叉堆,但是它有d個(gè)兒子,此時(shí),d-堆比二叉堆要淺很多,因此插入操作更快了,但是相對的刪除操作更耗時(shí)。因?yàn)?#xff0c;需要在d個(gè)兒子中找到最大的,但是很多算法中插入操作要遠(yuǎn)多于刪除操作,因此,這種加速是現(xiàn)實(shí)的。
除了不能執(zhí)行find去查找一般的元素外,兩個(gè)堆的合并也很困難。
左式堆
左式堆可以有效的解決上面說的堆合并的問題。合并就涉及插入刪除,很顯然使用數(shù)組不合適,因此,左式堆使用指針來實(shí)現(xiàn)。左式堆和二叉堆的區(qū)別:左式堆是不平衡的。它兩個(gè)重要屬性:鍵值和零距離
零距離(英文名NPL,即Null Path Length)則是從一個(gè)節(jié)點(diǎn)到一個(gè)沒有兩個(gè)兒子的節(jié)點(diǎn)(只有0個(gè)或1個(gè)兒子的節(jié)點(diǎn))的路徑長度。具有0個(gè)或1個(gè)兒子的節(jié)點(diǎn)的NPL為0,NULL節(jié)點(diǎn)的NPL為-1。
- 節(jié)點(diǎn)的左孩子的NPL >= 右孩子的NPL。
- 節(jié)點(diǎn)的NPL = 它的右孩子的NPL + 1。
- 在有路徑上有r個(gè)節(jié)點(diǎn)的左式堆必然至少有2^r - 1個(gè)節(jié)點(diǎn)。
?
合并
合并操作是左傾堆的重點(diǎn)。插入式合并的特殊情況。
合并兩個(gè)左傾堆(最小堆)的基本思想如下:
- 如果一個(gè)空左傾堆與一個(gè)非空左傾堆合并,返回非空左傾堆。
- 如果兩個(gè)左傾堆都非空,那么比較兩個(gè)根節(jié)點(diǎn),取較小堆的根節(jié)點(diǎn)為新的根節(jié)點(diǎn)。將"較小堆的根節(jié)點(diǎn)的右孩子"和"較大堆"進(jìn)行合并;該合并過程和上面的過程一樣,這樣遞歸合并下去,最終兩個(gè)堆合并完成。 但是新推可能不再滿足左式堆的性質(zhì),需要調(diào)整:(調(diào)整的過程是在合并的同時(shí)完成的)
- 如果新堆的右孩子的NPL > 左孩子的NPL,則交換左右孩子。
- 設(shè)置新堆的根節(jié)點(diǎn)的NPL = 右子堆NPL + 1
實(shí)現(xiàn)時(shí),通過遞歸自底向上合并并調(diào)整使得滿足左式堆的性質(zhì)。
LeftistNode* mergeLeftist(LeftistHeap x, LeftistHeap y){if (x == nullptr)return y;if (y == nullptr)return x;LeftistHeap l, r;//以l為根,l較小if (x->val < y->val){l = x;r = y;}else {l = y;r = x;}l->right = mergeLeftist(l->right,r);//合并l->right和rif (!l->left || l->left->npl < l->right->npl){//判斷是否需要交換左右子樹LeftistHeap temp = l->left;l->left = l->right;l->right = temp;}//更新nplif (!l->right || !l->left)l->npl = 0;else l->npl = l->left->npl > l->right->npl ? l->right->npl + 1 : l->left->npl + 1;return l; }合并左式堆的操作可以看出來,它的時(shí)間復(fù)雜度和有路徑的長成正比,因此復(fù)雜度O(logn)
添加節(jié)點(diǎn)就可以看做是一個(gè)左式堆和一個(gè)單點(diǎn)的左式堆合并;
刪除樹根節(jié)點(diǎn)可以看做是刪除樹根后,左右子樹的兩個(gè)左式堆合并;
因此,他們都可以通過合并來實(shí)現(xiàn)。它對應(yīng)的復(fù)雜度也是O(logn)
插入和刪除的實(shí)現(xiàn):
?
斜堆
斜堆是左式堆的自調(diào)節(jié)形式,左式堆和斜堆的關(guān)系類似于伸展樹和AVL樹的關(guān)系。斜堆具有堆序的性質(zhì),但是沒有結(jié)構(gòu)的限制,這樣的話一次的操作最壞的情況時(shí)O(n),但是連續(xù)m次操作總的復(fù)雜度O(mlogn)。
與左式堆相同,斜堆的基本操作也是合并操作。但是斜堆沒有零距離的屬性,合并的方法也有區(qū)別:
- 如果一個(gè)空斜堆與一個(gè)非空斜堆合并,返回非空斜堆。
- 如果兩個(gè)斜堆都非空,那么比較兩個(gè)根節(jié)點(diǎn),取較小堆的根節(jié)點(diǎn)為新的根節(jié)點(diǎn)。將"較小堆的根節(jié)點(diǎn)的右孩子"和"較大堆"進(jìn)行合并。
- 合并后,交換新堆根節(jié)點(diǎn)的左孩子和右孩子。
- 這一步是斜堆和左傾堆的合并操作差別的關(guān)鍵所在,如果是左傾堆,則合并后要比較左右孩子的零距離大小,若右孩子的零距離 > 左孩子的零距離,則交換左右孩子;最后,在設(shè)置根的零距離。
斜堆的結(jié)構(gòu)
typedef int Type;typedef struct _SkewNode{Type val;struct _SkewNode *left; // 左孩子struct _SkewNode *right; // 右孩子 }SkewNode, *SkewHeap;合并的實(shí)現(xiàn)
SkewNode* mergeSkewHeap(SkewHeap x, SkewHeap y){if (x == nullptr)return y;if (y == nullptr)return x;SkewHeap l, r;//以l為根,l較小if (x->val < y->val){l = x;r = y;}else {l = y;r = x;}SkewNode* temp = mergeSkewHeap(l->right, r);//合并l->right和r l->right = l->left;//交換左右子樹l->left = temp;return l; }同樣的道理,插入和刪除根節(jié)點(diǎn)的操作都可以使用合并來實(shí)現(xiàn)。
轉(zhuǎn)載于:https://www.cnblogs.com/yeqluofwupheng/p/7450643.html
總結(jié)
- 上一篇: 思科安全——企业安全棋局的“宇宙流”
- 下一篇: Netronome为中国云计算大幅提速升