平衡二叉搜索樹的實現

概念介紹

  • AVL樹可以將高度控制在o(logn)以內
  • 平衡因子
    • 左孩子樹高 - 右孩子樹高
    • AVL就是平衡因子受限的搜索樹,其中各個節點的平衡因子的絕對值不超過1
  • 等價二叉搜索樹
    • 兩個二叉搜索樹的中序遍歷序列相同,則稱它們等價;這個平衡算法的依據
  • 重新平衡的方法
    • AVL的insert,remove導致的失衡通過單旋和雙旋的方式實現重新平衡
  • 失衡傳播
    • AVL樹刪除節點重新平衡之後,祖先可能失衡,這時候需要由下而上的進行重新平衡操作
  • 統一平衡算法rotateAt
    • 不在乎旋轉的過程,而是窮舉了旋轉過後所有的情況;對,就是根據旋轉之後的各個節點的位置關係做出來的
    • 平衡是3個節點(祖g 父p 子v)和四個子樹的關係,而他們組合在一起總是可以實現下面的模型模型
  • zip 順時針旋轉
    zip
  • zap – zip 雙旋轉
    zap-zip
  • zap
    zap
  • zip – zap
    在這裏插入圖片描述

代碼實現

  • AVL樹
// 引入搜索二叉樹
#include <BstTree.h>

// 理想平衡條件 (左右孩子的高度一樣)
#define Balanced(x) (stature((x).rc) == stature((x).lc))

// 平衡因子
#define BalFac(x) (stature((x).lc) - stature((x).rc))

// AVL平衡條件
#define AvlBalanced(x) (BalFac(x) > -2 && BalFac(x) < 2)

// 找到失衡節點g(x)對應的孩子和孩子的孩子
template<typename T> BinNodePosi(T) tallerChild(BinNodePosi(T) x) {
    // 取孩子高度最高的那個,一樣高的時候取同側的那個
    if (stature(x->lc) > stature(x->rc)){
        return x->lc;
    }

    if (stature(x->lc) < stature(x->rc)) {
        return x->rc;
    }

    // 取同側孩子
    return IsLChild(*x) ? x->lc : x->rc;
}

// 定義平衡二叉樹模板類
template <typename T> class AVL:public BST {
public :
    BinNodePosi(T) insert(T const & e); // 插入(重寫)
    // 刪除(重寫)
    bool remove(T const& e);
};

// 二叉平衡樹 插入操作
template <typename T> BinNodePosi(T) AVL<T>::insert(const T &e) {
    // e存在的話 沒有插入的必要
    BinNodePosi(T) x = serach(e);
    if (x) {
        return x;
    }

    // 插入新節點
    BinNodePosi(T) new_child = new BinNode<T>(e, _hot);
    _size ++;

    // 解決因插入數據而導致的失衡的問題
    BinNodePosi(T) g = _hot;
    for (; g; g= g->parent){
        // 節點失衡
        if (!AvlBalanced(*g)){
            // 失衡節點的孫子節點
            BinNodePosi(T) v = tallerChild(tallerChild(g));
            FromParentTo(*g) = rotateAt(v);

            // 此時子樹高度復原,g祖先的高度也是會復原, 調整結束
            break;
        } else {
            // 即使未失衡但是高度也可能變化
            updateHeight(g);
        }
    }

    return new_child;
}

// 刪除算法
template <typename T> bool AVL::remove(const T &e) {
    // 如果不存在 則不需要刪除
    BinNodePosi(T)& x = search(e);
    if (!x) {
        return false;
    }

    // 按照二叉搜索樹的規則刪除
    removeAt(x, _hot);
    --size;

    // 平衡祖先節點
    BinNodePosi(T) g = _hot;
    for (; g ; g = g->parent) {
        // 失衡節點使用3+4平衡算法平衡
        if (!AvlBalanced(g)) {
            BinNodePosi(T) v = tallerChild(tallerChild(g));

            // 執行平衡算法,將新子樹關聯到原父親;   但是g的位置也要更新成子樹根節點
            g = FromParentTo(*g) = rotateAt(v);

            // 刪除節點會造成祖先節點失衡傳播,所以還是需要進行後續的輪詢
        }

        // 即使未失衡但是高度也可能變化
        updateHeight(g);
    }

    return true;
}

  • 二叉搜索樹

// 引入二叉樹
#include "BinTree.h"

//定義二叉搜索樹模板類
template<typename T>
class BST : public BinTree {
protected:
    // 命中節點的父親
    BinNodePosi(T)_hot;

    // 按照3+4結構連接3個節點和四顆子樹
    BinNodePosi(T)connect34(BinNodePosi(T) a, BinNodePosi(T) b, BinNodePosi(T) c, BinNodePosi(T) T0,
                            BinNodePosi(T) T1, BinNodePosi(T) T2, BinNodePosi(T) T3);

    // 對父親祖父做同意旋轉調整
    BinNodePosi(T)rotateAt(BinNodePosi(T)x);

public:
    // 基本接口
    // 查找
    virtual BinNodePosi(T)&search(T const &e);

    // 插入
    virtual BinNodePosi(T)insert(T const &e);

    // 刪除
    virtual bool remove(T const &e);
};

// 二叉搜索樹查找
template<typename T>
BinNodePosi(T)& BST::search(T const &e) {
    return searchIn(_root, _hot = NULL, e);
}

// 遞歸查找
template <typename T> BinNodePosi(T)& searchIn(BinNodePosi(T)& v, BinNodePosi(T) & hot, T const &e){
    // 遞歸基
    if (!v || v->data == e) {
        return v;
    }

    // 設置父指針
    hot = v;

    // 做減法
    v = e > v->data ? v->rc : v->lc;
    return searchIn(v, hot, e);
}

// 插入操作
template <typename T> BinNodePosi(T) BST<T>::insert(const T &e) {
    BinNodePosi(T) x = search(e);
    // 如果已經存在 則沒有意義
    if (x) {
        return x;
    }

    // 創建新節點
    x = new BinNode(e, hot);

    // 更新規模
    _size++;

    // 更新x祖先高度
    updateHeightAbove(x);

    return x;
}

// 刪除算法
template <typename T> bool BST<T>::remove(const T &e) {
    BinNodePosi(T) x = search(e);

    // 目標不存在 則刪除失敗
    if (!x) {
        return false;
    }

    // 實施刪除
    removeAt(x, _hot);

    // 更新規模
    _size --;

    // 更新祖先節點的高度
    updateHeightAbove(_hot);

    return true;
};

// 實際刪除操作
template <typename T> static BinNodePosi(T) removeAt(BinNodePosi(T) &x, BinNodePosi(T) & hot) {
    // 實際被刪除的節點
    BinNodePosi(T) w = x;

    // 被刪除節點的替換者
    BinNodePosi(T) succ = NULL;

  // 單分支直接刪除
  if (!HasLChild(x)) {
      // *x替換成右子樹
      x = succ = x->rc;
  } else if (!HasRChild(x)) {
      // *x替換成其左子樹
      x = succ = x->rc;
  } else {
      // 雙分支 被刪除的是直接後繼節點
      w = w->succ();

      // 置換x和直接後繼之間的數據
      swap(x->data, w->data);

      // 直接後繼肯定是沒有左孩子的
      FromParentTo(w) = succ = w->rc;

  }

  // 記錄實際被刪除的孩子的父親
  hot = w->parent;

  // 關聯hot和替換者
  if (succ) {
      succ->parent = hot;
  }

  // 釋放被刪除的節點
  release(w->data);
  release(w);
  return succ;
};

// BST系欸但那旋轉變化統一算法, 返回調整之後局部子樹根節點的位置
template <typename T> BinNodePosi(T) BST<T>::rotateAt(BinNodePosi(T) x) {
  // 父親節點
  BinNodePosi(T) p = x->parent;

  // 祖父節點
  BinNodePosi(T) g = p->parent;

  if (IsLChild(*p)) { // zip
      if (IsLChild(*v)) { // zip--zip
          return connect34(v,p,g,v->lc, v->rc, p->rc,g->rc)
      } else {
          // zip --zap
        return connect34(p,v,g, p->lc,v->lc, v->rc, g->rc);
      }
  } else {
      if (IsRChild(*v)) {
        return connect34(g, p, v, g->lc, p->lc, v->lc, v->rc);
      } else {
        return connect34(g,v,p, g->lc, v->lc, v->rc, p->rc);
      }
  }
};

// 按照3+4的接口連接3個節點和4顆子樹, 返回局部子樹的根節點
template <typename T> BinNodePosi(T) BST<T>::connect34(BinNode<T> *a, BinNode<T> *b, BinNode<T> *c, BinNode<T> *T0,
                                                       BinNode<T> *T1, BinNode<T> *T2, BinNode<T> *T3) {
    // 設置b,a,c之間關係
    b->lc = a;
    a->parent = b;
    b->rc = c;
    c->parent = b;

    // 設置a的左右孩子
    a->lc = T0;
    if (T0) {
        T0->parent = a;
    }

    a->rc = T1;
    if (T1) {
        T1->parent = a;
    }

    // 設置c的左右孩子
    c->lc = T2;
    if (T2) {
        T2->parent = c;
    }

    c->rc = T3;
    if (T3) {
        T3->parent = c;
    }

    return b;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章