1. 程式人生 > >伸展樹

伸展樹

#include "BST.hpp"

//伸展樹
template <typename T> class Splay : public BST<T> {
protected:
    BinNodePosi(T) splay(BinNodePosi(T) v); //將節點伸展至根
public:
    BinNodePosi(T) & search(const T& e) override; //查詢
    BinNodePosi(T) insert(const T& e) override; //插入
    bool remove(const T& e); //
刪除 }; //在節點*p與*lc(可能為空)之間建立父(左)子關係 template <typename NodePosi> inline void attachAsLChild(NodePosi p,NodePosi lc) { p -> lc = lc; if (lc) { lc -> parent = p; } } //在節點*p與*rc(可能為空)之間建立父(右)子關係 template <typename NodePosi> inline void attachAsRChild(NodePosi p,NodePosi rc) { p
-> rc = rc; if (rc) { rc -> parent = p; } } //Splay樹伸展演算法:從節點v出發逐層伸展 template <typename T> BinNodePosi(T) Splay<T>::splay(BinNodePosi(T) v) { //v為因最近訪問而需伸展的節點位置 if (!v) { return NULL; } BinNodePosi(T) p; BinNodePosi(T) g; //*v的父親與祖父
//自下而上,反覆對*v做雙層伸展 while ( (p = v->parent) && (g = p->parent) ) { BinNodePosi(T) gg = g -> parent; //每輪之後*v都以原曾祖父(great-grand parent)為父 if (IsLChild(*v)) { if (IsLChild(*p)) { //zig--zig attachAsLChild(g, p->rc); attachAsLChild(p, v->rc); attachAsRChild(p, g); attachAsRChild(v, p); }else{ //zig---zag attachAsLChild(p, v->rc); attachAsRChild(g, v->lc); attachAsLChild(v, g); attachAsRChild(v, p); } }else if (IsRChild(*p)) { //zag---zag attachAsRChild(g, p->lc); attachAsRChild(p, v->lc); attachAsLChild(p, g); attachAsLChild(v, p); }else{ //zag---zig attachAsRChild(p, v->lc); attachAsLChild(g, v->rc); attachAsRChild(v, g); attachAsLChild(v, p); } if (!gg) { //若*v原先的曾祖父*gg不存在,則*v現在應為樹根 v -> parent = NULL; }else{ //否則, *gg此後應該以*v作為左或右孩子 ( g == gg->lc) ? attachAsLChild(gg, v) : attachAsRChild(gg, v); } this -> updateHeight(g); this -> updateHeight(p); this -> updateHeight(v); }//雙層伸展結束時,必有g == NULL,但p可能非空 if ( (p = v->height) ) { //若p果真非空,則額外再做一次單旋 if (IsLChild(*v)) { attachAsLChild(p, v->rc); attachAsRChild(v, p); }else{ attachAsRChild(p, v->lc); attachAsLChild(v, p); } this -> updateHeight(p); this -> updateHeight(v); } v -> parent = NULL; return v; }//調整之後新樹根應為被伸展的節點,故返回該節點的位置以便上層函式更新樹根 template <typename T> BinNodePosi(T) & Splay<T>::search(const T &e) { BinNodePosi(T) p = searchIn(this->_root, e, this->_hot = NULL); this->_root = splay(p ? p : this->_hot); //將最後一個被訪問的節點伸展至根 return this->_root; }//與其他BST不同,無論查詢成功與否,_root都指向最後被訪問的節點 //將關鍵碼e插入伸展樹中 template <typename T> BinNodePosi(T) Splay<T>::insert(const T &e) { if (!this->_root) { //處理原樹為空的退化情況 this->_size++; return this->_root = new BinNode<T>(e); } if (e == search(e) -> data) { return this->_root; //確認目標節點不存在 } this -> _size++; BinNodePosi(T) t = this->_root; if (this->_root->data < e) { //插入新根,以t和t->rc為左、右孩子 t -> parent = this->_root = new BinNode<T>(e, NULL, t, t->rc); //2+3個 if (HasRChild(*t)) { t -> rc -> parent = this->_root; t -> rc = NULL; // <= 2個 } }else{ t -> parent = this->_root = new BinNode<T>(e, NULL, t->lc, t); //2+3個 if (HasLChild(*t)) { t->lc->parent = this->_root; t -> lc = NULL; } } this -> updateHeightAbove(t); //更新t及其祖先(實際上只有_root一個)的高度 return this->_root; //新節點必然置於樹根,返回之 }//無論e是否存在於原樹中,返回時總有_root->data==e template <typename T> bool Splay<T>::remove(const T &e) { if (!this->_root || (e != search(e) ->data )){ //若樹空或目標不存在,則無法刪除 return false; } BinNodePosi(T) w = this->_root; if (!HasLChild(*this->_root)) { //若無左子樹,則直接刪除 this->_root = this->_root->rc; if (this->_root) { this->_root->parent = NULL; } }else if( !HasRChild(*this->_root) ){ //若無右子樹,也直接刪除 this -> _root = this->_root->lc; if (this -> _root) { this -> _root -> parent = NULL; } }else{ //若左右子樹同時存在,則暫時將左子樹切除,只保留右子樹 BinNodePosi(T) lTree = this->_root->lc; lTree -> parent = NULL; this->_root->lc = NULL; this->_root = this->_root->rc; this->_root->parent = NULL; //以原樹根為目標,做一次(必定失敗的)查詢 search(w->data); //至此,右子樹中最小節點必伸展至根,且(因無雷同節點)其左子樹必空,於是只需將原左子樹接回原位即可 this->_root->lc = lTree; lTree->parent = this->_root; } delete w->data; delete w; this->_size--; if (this -> _root) { //此後,若樹非空,則樹根的高度需要更新 this->updateHeight(this->_root); } return true; }//若目標節點存在且被刪除,返回true,否則返回false