数据结构之二叉搜索树和二叉平衡树学习笔记

Posted PrConstantin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之二叉搜索树和二叉平衡树学习笔记相关的知识,希望对你有一定的参考价值。

二叉搜索树(Binary Search Tree)

具有下列性质的二叉树被称为二叉搜索树:

(1)、若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值;
(2)、若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值;
(3)、它的左、右子树也分别为二叉查找树。

一些典型特点:
- 而中序遍历二叉搜索树可得到一个关于key的有序序列,一个无序序列可以通过构造一棵二叉搜索树变成一个有序序列。

- 对二叉搜索树的搜索,插入,删除操作的次数最多不·超过等于树高,即操作的时间复杂度为O(log(n)).

搜索、插入、删除操作的算法思路:
- 搜索操作:

在搜索元素x的时候,我们可以将x和根节点比较,有以下三种情况:
(1) 如果x等于根节点,那么找到x,停止搜索 (终止条件);
(2) 如果x小于根节点,那么搜索左子树;
(3) 如果x大于根节点,那么搜索右子树;
  • 插入操作:
(1) 若二叉搜索树中存在该关键字,则不插入,所以插入的结点总是作为某一叶子节点的子结点;
(2) 先检索二叉搜索树,看查找树中是否含有该关键字,若不存在,记录应当要插入位置的父节点;
  • 删除:
在二叉搜索树中删除一个给定的结点p有三种情况:
(1)叶节点可以直接删除;
(2)结点p有左子树(右子树)之一,则把p的左子树(右子树)接到p的父节点上;
(3)左右子树同时存在,则找到结点p的中序直接后继结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的右子树总最左的结点,因而s无左子树,所以直接把s的右子树接到s的父节点即可;

实现代码如下:

#include <iostream>
#include <cstdio>
using namespace std;
enum ResultCode  Underflow, Overflow, Success, Duplicate, NotPresent ;

template<class T>
struct BTNode

    BTNode()  lChild = rChild = NULL; 
    BTNode(const T& x)
    
        element = x;
        lChild = rChild = NULL;
    
    BTNode(const T& x, BTNode<T>* l, BTNode<T>* r)
    
        element = x;
        lChild = l;
        rChild = r;
    
    T  element;
    BTNode<T>* lChild, *rChild;
;

template<class T>
class BST 

public:
    BST()  root = NULL; 
    bool IsEmpty()const;
    void Clear();
    bool Root();
    void MakeTree(const T& x, BST<T>& left, BST<T>& right);
    void BreakTree(T &x, BST<T>& left, BST<T>& right);
    int Size();
    void PreOrder(void(*Visit)(T& x));
    void InOrder(void(*Visit)(T& x));
    void PostOrder(void(*Visit)(T& x));
    void FreeTree();
    ResultCode Search(T &x)const;
    ResultCode Insert(T &x);
    ResultCode Remove(T& x);
protected:
    BTNode<T>* root;
private:
    ResultCode Search(BTNode<T> *p, T& x)const;
    void Clear(BTNode<T>* &t);
    void PreOrder(void(*Visit)(T& x), BTNode<T>*t);
    void InOrder(void(*Visit)(T& x), BTNode<T>*t);
    void PostOrder(void(*Visit)(T& x), BTNode<T>*t);
    void FreeTree(BTNode<T>* t);
    int Size(BTNode<T>* t);
;

template <class T>
bool BST<T>::Root()

    if (root)
    
        cout << root->element << endl;
        return true;
    
    else
        return false;


template <class T>
void BST<T>::MakeTree(const T& x, BST<T>& left, BST<T>& right)

    if (root || &left == &right)
        return;
    root = new BTNode<T>(x, left.root, right.root);
    left.root = right.root = NULL;


template <class T>
void BST<T>::BreakTree(T& x, BST<T>& left, BST<T>& right)

    if (!root || &left == &right || left.root || right.root)
        return;
    x = root->element;
    left.root = root->lChlid;
    right.root = root->rChild;
    delete root;
    root = NULL;


template <class T>
void Visit(T &x)

    cout << x << " ";


template <class T>
void BST<T>::PreOrder(void(*Visit)(T& x))

    PreOrder(Visit, root);


template <class T>
void BST<T>::PreOrder(void(*Visit)(T& x), BTNode<T>* t)

    if (t)
    
        Visit(t->element);
        PreOrder(Visit, t->lChild);
        PreOrder(Visit, t->rChild);
    


template <class T>
void BST<T>::InOrder(void(*Visit)(T& x))

    InOrder(Visit, root);


template <class T>
void BST<T>::InOrder(void(*Visit)(T& x), BTNode<T>* t)

    if (t)
    
        InOrder(Visit, t->lChild);
        Visit(t->element);
        InOrder(Visit, t->rChild);
    


template <class T>
void BST<T>::PostOrder(void(*Visit)(T& x))

    PostOrder(Visit, root);


template <class T>
void BST<T>::PostOrder(void(*Visit)(T& x), BTNode<T>* t)

    if (t)
    
        PostOrder(Visit, t->lChild);
        PostOrder(Visit, t->rChild);
        Visit(t->element);
    


template <class T>
int BST<T>::Size()

    return Size(root);


template <class T>
int BST<T>::Size(BTNode<T>* t)

    if (!t) return 0;
    else
        return Size(t->lChild) + Size(t->rChild) + 1;


template <class T>
void BST<T>::FreeTree()

    return FreeTree(root);


template <class T>
void BST<T>::FreeTree(BTNode<T>* t)

    if (t == NULL)
    
        return;
    
    if (t->lChild != NULL)
    
        FreeTree(t->lChild);
        t->lChild = NULL;
    
    if (t->rChild != NULL)
    
        FreeTree(t->rChild);
        t->rChild = NULL;
    
    if (t != NULL)
    
        free(t);
        t = NULL;
    


template<class T>
ResultCode BST<T>::Search(T &x)const

    return Search(root, x);

template<class T>
ResultCode BST<T>::Search(BTNode<T>* p, T& x)const

    if (!p)return NotPresent;
    else if (x<p->element) return Search(p->lChild, x);
    else if (x>p->element) return Search(p->rChild, x);
    else 
        x = p->element;
        return Success;
    


template<class T>
ResultCode BST<T>::Insert(T& x)

    BTNode<T> *p = root, *q = NULL;
    while (p)
    
        q = p;
        if (x<p->element) p = p->lChild;
        else if (x>p->element)p = p->rChild;
        else 
            x = p->element;
            return Duplicate;
        
    
    p = new BTNode<T>(x);
    if (!root) root = p;
    else if (x<q->element) q->lChild = p;
    else q->rChild = p;
    return Success;


template<class T>
ResultCode BST<T>::Remove(T &x)

    BTNode<T> *c, *s, *r, *p = root, *q = NULL;
    while (p && p->element != x)
    
        q = p;
        if (x<q->element)p = p->lChild;
        else p =p ->rChild;
    
    if (!p)return NotPresent;
    x = p->element;
    if (p->lChild&&p->rChild)
    
        s = p->rChild;
        r = p;
        while (s->lChild)
        
            r = s;
            s = s->lChild;
        
        p->element = s->element;
        p = s;
        q = r;
    
    if (p->lChild)c = p->lChild;
    else c = p->rChild;
    if (p == root)root = c;
    else if (p == q->lChild) q->lChild = c;
    else q->rChild = c;
    delete p;
    return Success;


int main()

    BST<int> a, b, o, p, q, w, x, y, z;
    int size;  
    /*下面代码建立一个形如:
        4
      /   \\
     2     6
    / \\   / \\
   1   3 5    7
    的二叉树
*/
    y.MakeTree(1, a, b);
    z.MakeTree(3, a, b);
    x.MakeTree(2, y, z);
    p.MakeTree(5, a, b);
    q.MakeTree(7, a, b);
    w.MakeTree(6, p, q);
    o.MakeTree(4, x, w);
    o.Root();                   
    o.PreOrder(Visit);          
    cout << endl;
    o.InOrder(Visit);          //中序遍历当前二叉,答案应该为'1 2 3 4 5 6 7 ' 
    cout << endl;                 
    o.PostOrder(Visit);
    cout << endl;
    size = o.Size();
    cout << size << endl;
    int v1 = 5,v2 = 8,v3 = 4,v4=-1;

    if (o.Search(v1) == Success)  //搜索元素5是否在树中,结果应该为Success
        cout << "Success" << endl;
    else
        cout << "No" << endl;

    if (o.Search(v2) == Success)  //搜索元素8是否在树中,结果应该为No
        cout << "Success" << endl;
    else
        cout << "No" << endl;

    o.Remove(v1);            //删除元素5

    o.InOrder(Visit);        //删除后进行中序遍历,结果应该为1,2,3,4,6,7
    cout << endl;                 

    o.Remove(v3);           //再删除元素4,结果应该为1,2,3,6,7
    o.InOrder(Visit);       
    cout << endl;

    o.Insert(v1);           //插入元素5,结果应该为1,2,3,5,6,7
    o.InOrder(Visit);
    cout << endl;

    o.Insert(v3);           //插入元素4,结果应该为1,2,3,4,5,6,7
    o.InOrder(Visit);
    cout << endl;

    o.Insert(v4);           //插入元素-1,结果应该为-1,1,2,3,4,5,6,7
    o.InOrder(Visit);
    cout << endl;
    return 0;

实际运行结果如下图:

运行环境VS2015

平衡二叉树(Balanced Binary Tree)

平衡二叉树又称为AVL树
它具有以下性质:

(1)是一棵二叉搜索树(每个节点是惟一出现);

(2)每个节点左右子树的高度之差(平衡因子)相差最多为1,平衡因子定义为:左子树高度 - 右子树高度;
(3)左右两个子树都是一棵平衡二叉树;


- 搜索算法同上的二叉搜索树。

但是注意和二叉查找树相比,查找方法没有变法,不过根据存储的特性,AVL树能维持在一个O(logN)的稳定的时间,而二叉查找树则相当不稳定。


此处资料学习并参考自:
http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html
- 插入、删除的关键实现原理—失衡旋转:

平衡二叉树在进行插入和删除元素后,平衡性质可能要被打破,也称为失衡,所以要对树进行调整以保证平衡二叉树的平衡性质被保持住。解决的方法就是旋转。

旋转又分为单旋转和双旋转,针对不同的情况选择不同的旋转方案:

 对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:

 1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。

  2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。

  3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。

  4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。

  从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
  • 单旋转
  单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的左子树X子树,所以属于左左情况。

 为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。

  这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根节点的路径就不需要继续旋转了。
  • 双旋转
  对于左右和右左这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。

为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右旋转,旋转之后就变成了左左情况,所以第二步再进行一次左左旋转,最后得到了一棵以k2为根的平衡二叉树树。
  • 插入
 插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。

插入代码如下:

//插入
template<class T>
void AVLTree<T>::insertpri(TreeNode<T>* &node,T x)

    if(node==NULL)//如果节点为空,就在此节点处加入x信息
    
        node=new TreeNode<T>();
        node->data=x;
        return;
    
    if(node->data>x)//如果x小于节点的值,就继续在节点的左子树中插入x
    
        insertpri(node->lson,x);
        if(2==height(node->lson)-height(node->rson))
            if(x<node->lson->data)
                SingRotateLeft(node);
            else
                DoubleRotateLR(node);
    
    else if(node->data<x)//如果x大于节点的值,就继续在节点的右子树中插入x
    
        insertpri(node->rson,x);
        if(2==height(node->rson)-height(node->lson))//如果高度之差为2的话就失去了平衡,需要旋转
            if(x>node->rson->data)
                SingRotateRight(node);
            else
                DoubleRotateRL(node);
    
    node->hgt=Max(height(node->lson),height(node->rson));
  • 删除
 删除的方法也和二叉查找树的一致,区别是,删除完成后,需要从删除节点的父亲开始向上维护树的平衡一直到根节点。

删除代码如下:

//删除
template<class T>
void AVLTree<T>::Deletepri(TreeNode<T>* &node,T x)

    if(node==NULL) return ;//没有找到值是x的节点
    if(x < node->data)
    
         Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
         if(2==height(node->rson)-height(node->lson))
            if(node->rson->lson!=NULL&&(height(node->rson->lson)>height(node->rson->rson)) )
                DoubleRotateRL(node);
            else
                SingRotateRight(node);
    

    else if(x > node->data)
    
         Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
         if(2==height(node->lson)-height(node->rson))
            if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
                DoubleRotateLR(node);
            else
                SingRotateLeft(node);
    

    else//如果相等,此节点就是要删除的节点
    
        if(node->lson&&node->rson)//此节点有两个儿子
        
            TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
            while(temp->lson!=NULL) temp=temp->lson;//找到右子树中值最小的节点
            //把右子树中最小节点的值赋值给本节点
            node->data=temp->data;
            node->freq=temp->freq;
            Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
            if(2==height(node->lson)-height(node->rson))
            
                if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) ))
                    DoubleRotateLR(node);
                else
                    SingRotateLeft(node);
            
        
        else//此节点有1个或0个儿子
        
            TreeNode<T>* temp=node;
            if(node->lson==NULL)//有右儿子或者没有儿子
            node=node->rson;
            else if(node->rson==NULL)//有左儿子
            node=node->lson;
            delete(temp);
            temp=NULL;
        
    
    if(node==NULL) return;
    node->hgt=Max(height(node->lson),height(node->rson))+1;
    return;

全部实现代码如下,测试见main函数:

#include <iostream>
#include <algorithm>
using namespace std;

template <class T>
struct AVLNode

    T key;
    int height;
    AVLNode(T e, AVLNode<T> *l = NULL, AVLNode<T> *r = NULL) :
        key(e), height(0), lChild(l), rChild(r)
    AVLNode<T> *lChild, *rChild;
;

template <class T>
class AVLTree

protected:
    void InOrder(AVLNode<T> *&t) const;
    bool Insert(AVLNode<T> *&t, T key);
    bool Remove(AVLNode<T> *&t, T key);
    void LeftRotate(AVLNode<T> *&p);
    void RightRotate(AVLNode<T> *&p);
    void PreOrder(AVLNode<T> *&t) const;
public:
    AVLTree()  root = NULL; 
    ~AVLTree()  Clear(root); 
    int GetHeight(AVLNode<T> *t);
    void InOrder();
    void PreOrder();
    bool Insert(T key);
    bool Remove(T key);
    void Clear(AVLNode<T> *&t);
    AVLNode<T> *root;
;


template <class T>
void AVLTree<T>::Clear(AVLNode<T> *&t)

    if (!t)
        return;
    Clear(t->lChild);
    Clear(t->rChild);
    delete t;


template <class T>
int AVLTree<T>::GetHeight(AVLNode<T> *t)

    return t ? t->height : 0;


template <class T>
void AVLTree<T>::PreOrder(AVLNode<T> *&t) const

    if (!t)
        return;
    cout << t->key << " ";
    PreOrder(t->lChild);
    PreOrder(t->rChild);


template <class T>
void AVLTree<T>::PreOrder()

    if (!root)
        return;
    cout << "前序遍历为:" << endl;
    PreOrder(root);
    cout << endl;




template <class T>
void AVLTree<T>::InOrder(AVLNode<T> *&t) const

    if (!t)
        return;
    InOrder(t->lChild);
    cout << t->key << " ";
    InOrder(t->rChild);


template <class T>
void AVLTree<T>::InOrder()

    if (!root)
        return;
    cout << "中序遍历为:" << endl;
    InOrder(root);
    cout << endl;



template <class T>
inline void AVLTree<T>::RightRotate(AVLNode<T> *&p)

    AVLNode<T> *l = p->lChild;
    p->lChild = l->rChild;
    l->rChild = p;
    p->height = max(GetHeight(p->lChild), GetHeight(p->rChild)) + 1;
    l->height = max(GetHeight(l->lChild), p->height) + 1;
    p = l;


template <class T>
inline void AVLTree<T>::LeftRotate(AVLNode<T> *&p)

    AVLNode<T> *r = p->rChild;
    p->rChild = r->lChild;
    r->lChild = p;
    p->height = max(GetHeight(p->lChild), GetHeight(p->rChild)) + 1;
    r->height = max(GetHeight(r->rChild), p->height) + 1;
    p = r;



template <class T>
bool AVLTree<T>::Insert(T key)

    return Insert(root, key);


template <class T>
bool AVLTree<T>::Insert(AVLNode<T> *&t, T key)

    if (!t)
    
        t = new AVLNode<T>(key);
        if (!t)
            return false;
    
    if (key < t->key)
    
        if (!Insert(t->lChild, key))
            return false;
        if (2 == GetHeight(t->lChild) - GetHeight(t->rChild))
        
            if (key < t->lChild->key)
            
                RightRotate(t);
            
            else
            
                LeftRotate(t->lChild);
                RightRotate(t);
            
        
    
    else if (key > t->key)
    
        if (!Insert(t->rChild, key))
            return false;
        if (2 == GetHeight(t->rChild) - GetHeight(t->lChild))
        
            if (key > t->rChild->key)
            
                LeftRotate(t);
            
            else
            
                RightRotate(t->rChild);
                LeftRotate(t);
            
        
    
    t->height = max(GetHeight(t->lChild), GetHeight(t->rChild)) + 1;
    return true;




template <class T>
bool AVLTree<T>::Remove(T key)

    return Remove(root, key);

template <class T>
bool AVLTree<T>::Remove(AVLNode<T> *&t, T key)

    if (!t)
        return false;
    if (key < t->key)
    
        if (!Remove(t->lChild, key))
            return false;
        if (GetHeight(t->rChild) - GetHeight(t->lChild) == 2)
        
            AVLNode<T> *r = t->rChild;
            if (GetHeight(r->lChild) > GetHeight(r->rChild))
            
                RightRotate(t->rChild);
                LeftRotate(t);
            
            else
                RightRotate(t);
        
    
    else if (key > t->key)
    
        if (!Remove(t->rChild, key))
            return false;
        if (GetHeight(t->lChild) - GetHeight(t->rChild) == 2)
        
            AVLNode<T> *l = t->lChild;
            if (GetHeight(l->rChild) > GetHeight(l->lChild))
  

以上是关于数据结构之二叉搜索树和二叉平衡树学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

二叉搜索树以及对二叉搜索树平衡调整

数据结构学习笔记——广义表以及树和二叉树的基本知识

:树和二叉树

二叉树和二叉搜索树

二叉树学习笔记

二叉搜索树和最优二叉搜索树的时间复杂度各是多少?