1 二叉搜索树(BST)
1.1 二叉搜索树的优点
树的结构使得二分查找算法如鱼得水,使用树进行查找、删除和插入等操作综合了列表与向量的优点,在动态与静态之间达到了一个平衡。
其中,树搜索的时间复杂度与树高直接相关,树越高,时间复杂度越大。本文讲述二叉搜索树的基本原型以及保证树平衡的自平衡二叉搜索树的原理。
首先,介绍二叉搜索树的基本原型。二叉搜索树继承了二叉树类,扩写了查找,插入与删除等操作。
#pragma once
#include "../dsa_bintree_20200720/bintree.h"
#include "../dsa_bintree_20200720/release.h"
#include <iostream>
template<typename T> class BST:public BinTree<T>{
protected:
BinNodePosi(T) _hot;//“命中”节点的父亲
BinNodePosi(T) connect34(//3+4调整
BinNodePosi(T),BinNodePosi(T),BinNodePosi(T),
BinNodePosi(T),BinNodePosi(T),BinNodePosi(T),BinNodePosi(T));
BinNodePosi(T) rotateAt(BinNodePosi(T) x);//对x及父亲、祖父做统一的旋转调整
public:
virtual BinNodePosi(T) &search(const T& e);//查找
virtual BinNodePosi(T) insert(const T& e);//插入
virtual bool remove( const T& e);//删除
};
//查找调用的函数
template <typename T>
static BinNodePosi(T) & searchIn( BinNodePosi(T) & v, const T &e, BinNodePosi(T) & hot){
if( !v || (e == v->data)) return v;
hot = v;
return searchIn((( e < v->data ) ? v->lc : v->rc), e , hot );
}
//查找
template <typename T> BinNodePosi(T) & BST<T>::search (const T & e)
{ return searchIn( this->_root, e, _hot = nullptr);}
//插入
template <typename T> BinNodePosi(T) BST<T>::insert (const T & e){
BinNodePosi(T) & x = search( e ); if(x) return x;
x = new BinNode<T> ( e, _hot);
this->_size ++;
this->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 ); this->_size --;
this->updateHeightAbove( _hot );
return true;
}
//删除调用的函数
template <typename T>
static BinNodePosi(T) removeAt( BinNodePosi(T)& x , BinNodePosi(T) & hot ){
BinNodePosi(T) w = x;
BinNodePosi(T) succ = nullptr;
if( !HasLChild( *x ))
succ = x = x->rc;
else if ( !HasRChild( *x ))
succ = x = x->lc;
else{
w = w->succ();
T tmp = x->data;
x->data = w->data;
w->data = tmp;
//swap ( x->data, w->data);
BinNodePosi(T) u = w->parent;
((u == x) ? u->rc : u->lc) = succ = w->rc;
}
hot = w->parent;
if( succ ) succ->parent = hot;
dtl::release( w->data ); dtl::release( w ); return succ;
}
//3+4连接
template <typename T> BinNodePosi(T) BST<T> :: connect34(
BinNodePosi(T) a, BinNodePosi(T) b, BinNodePosi(T) c,
BinNodePosi(T) T0, BinNodePosi(T) T1, BinNodePosi(T) T2, BinNodePosi(T) T3
){
a->lc = T0; if( T0 ) T0->parent = a;
a->rc = T1; if( T1 ) T1->parent = a; BinTree<T>::updateHeight(a);
c->lc = T2; if( T2 ) T2->parent = c;
c->rc = T3; if( T3 ) T3->parent = c; BinTree<T>::updateHeight(c);
b->lc = a; a->parent = b;
b->rc = c; c->parent = b; BinTree<T>::updateHeight(b);
return b;
}
//3+4重平衡
template <typename T> BinNodePosi(T) BST<T>::rotateAt( BinNodePosi(T) v){
BinNodePosi(T) p = v->parent; BinNodePosi(T) g = p->parent;
if( p->data < g->data)/*zig*/
if( v->data < p->data){/*zig-zig*/
p->parent = g->parent;
std::cout << v->data << std::endl;
return connect34( v, p, g, v->lc, v->rc, p->rc, g->rc);
}else{/*zig-zag*/
v->parent = g->parent;
return connect34( p, v, g, p->lc, v->lc, v->rc, g->rc);
}
else/*zag*/
if( v->data >= p->data){/*zag-zag*/
p->parent = g->parent;
return connect34(g, p, v, g->lc, p->lc, v->lc, v->rc);
}else{/*zag-zig*/
v->parent = g->parent;
return connect34( g, v, p, g->lc, v->lc, v->rc, p->rc);
}
}
1.2 二叉搜索树的测试
以下是关于二叉搜索树的测试,结果如注释所示,中序遍历的结果显然自然排序,说明测试成功。
#include "BST.h"
#include <iostream>
using namespace std;
template<typename T> void returnValue(T& a)
{
cout << "return_value: " << a << endl;
}
int main(int argc,char* argv[])
{
//构造树
BST<int> bt_test;
bt_test.insert(36);
bt_test.insert(27);
bt_test.insert(58);
bt_test.insert(53);
bt_test.insert(69);
bt_test.insert(50);
bt_test.insert(54);
//测试
cout << bt_test.search(53)->data << endl;//搜索
bt_test.insert(59);//插入
//bt_test.remove(50);//删除
bt_test.insert(51);
void (* visit)(int& ) = &returnValue;//中序遍历
bt_test.traverse(bt_test.root(),visit);
}
/*
53
return_value: 27
return_value: 36
return_value: 50
return_value: 51
return_value: 53
return_value: 54
return_value: 58
return_value: 59
return_value: 69
*/
2 自平衡二叉搜索树(AVL)
2.1 AVL树类
以上的方案基本实现了二叉搜索树的各种操作,可是存在无法控制树高的问题。无法控制树高,树的搜索效率就可能很低。自平衡二叉搜索树(AVL)是对于二叉搜索树的一种改进,AVL树使用旋转实现了各个节点的平衡,保障了树的各种操作的效率。
#include "BST.h"
#define Balanced(x) ( stature( (x).lc ) == stature( (x).rc ))//理想平衡条件
#define BalFac(x) ( stature( (x).lc ) - stature( (x).rc ))//平衡因子
#define AvlBalanced(x) ( ( -2 < BalFac(x) ) && ( BalFac(x) < 2 ))//AVL平衡条件
//在左右孩子中取最高者
#define tallerChild(x)( \
stature( (x)->lc) > stature( (x)->rc) ? (x)->lc :( \
stature( (x)->lc) < stature( (x)->rc) ? (x)->rc :( \
IsLChild( *(x))? (x)->lc : (x)->rc \
)\
)\
)
template <typename T> class AVL : public BST<T> {
public:
BinNodePosi(T) insert( const T& e);//插入
bool remove( const T& e);//删除
};
//插入
template <typename T> BinNodePosi(T) AVL<T>::insert( const T& e ){
BinNodePosi(T) & x = BST<T>::search( e ); if( x ) return x;
BinNodePosi(T) xx = x = new BinNode<T> ( e, BST<T>::_hot); BST<T>::_size ++;
for( BinNodePosi(T) g = BST<T>::_hot; g; g = g->parent ){
if( !AvlBalanced( *g )) {
if( IsRoot(*g)){
this->_root = BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
else if( g == (*g).parent->lc){
(*g).parent->lc=BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
else{
(*g).parent->rc=BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
//std::cout << g->data << std::endl;
break;
}else
BST<T>::updateHeight(g);
}
return xx;
}
//删除
template <typename T> bool AVL<T>::remove( const T& e){
BinNodePosi(T) & x = BST<T>::search (e); if( !x ) return false;
removeAt( x, BST<T>::_hot); BST<T>::_size--;
for( BinNodePosi(T) g = BST<T>::_hot; g; g = g->parent ){
if( !AvlBalanced( *g ))
if( IsRoot(*g)){
this->_root = BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
else if(g == (*g).parent->lc){
(*g).parent->lc=BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
else{
(*g).parent->rc=BST<T>::rotateAt( (tallerChild(tallerChild(g))));
}
BST<T>::updateHeight(g);
}
return true;
}
2.2 旋转的实现
AVL旋转的原理表面看似操作比较复杂,但使用化整为零的方法,就可以方便地用两个函数解决旋转的问题。其代码在BST类中,如下所示。
//3+4连接
template <typename T> BinNodePosi(T) BST<T> :: connect34(
BinNodePosi(T) a, BinNodePosi(T) b, BinNodePosi(T) c,
BinNodePosi(T) T0, BinNodePosi(T) T1, BinNodePosi(T) T2, BinNodePosi(T) T3
){
a->lc = T0; if( T0 ) T0->parent = a;
a->rc = T1; if( T1 ) T1->parent = a; BinTree<T>::updateHeight(a);
c->lc = T2; if( T2 ) T2->parent = c;
c->rc = T3; if( T3 ) T3->parent = c; BinTree<T>::updateHeight(c);
b->lc = a; a->parent = b;
b->rc = c; c->parent = b; BinTree<T>::updateHeight(b);
return b;
}
//3+4重平衡
template <typename T> BinNodePosi(T) BST<T>::rotateAt( BinNodePosi(T) v){
BinNodePosi(T) p = v->parent; BinNodePosi(T) g = p->parent;
if( p->data < g->data)/*zig*/
if( v->data < p->data){/*zig-zig*/
p->parent = g->parent;
std::cout << v->data << std::endl;
return connect34( v, p, g, v->lc, v->rc, p->rc, g->rc);
}else{/*zig-zag*/
v->parent = g->parent;
return connect34( p, v, g, p->lc, v->lc, v->rc, g->rc);
}
else/*zag*/
if( v->data >= p->data){/*zag-zag*/
p->parent = g->parent;
return connect34(g, p, v, g->lc, p->lc, v->lc, v->rc);
}else{/*zag-zig*/
v->parent = g->parent;
return connect34( g, v, p, g->lc, v->lc, v->rc, p->rc);
}
}
2.3 AVL的测试
AVL的测试程序如下所示,注释中可清楚地看出树的平衡转换情况。
#include "AVL.h"
#include <iostream>
template<typename T> void returnValue(T& a)
{
std::cout << "return_value: " << a << std::endl;
}
int main(){
AVL<int> avl;
avl.insertAsRoot(36);
avl.insert(27);
avl.insert(58);
avl.insert(21);
avl.insert(35);
/* 36
27 58
21 35
*/
avl.remove(58); //前序遍历结果:35,27,21,36
/* 35
27 36
21
*/
avl.insert(15);//前序遍历结果:35,21,15,27,36
/* 35
21 36
15 27
*/
void (* visit)(int& ) = &returnValue;//中序遍历
avl.traverse(avl.root(),visit);
}
3 总结
BST是一种综合了列表的插入、删除等动态操作的优势,与向量的查找的静态操作优势的数据结构。AVL是BST的一种改进,通过旋转实现了树的自平衡,保证了BST的操作优势不被树的增高而消除。此外,AVL旋转的化整为零的实现方法十分精彩!