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

来源:互联网 发布:相册制作软件下载 编辑:程序博客网 时间:2024/06/07 15:15

二叉搜索树(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;}

实际运行结果如下图:

Alt text

运行环境VS2015

平衡二叉树(Balanced Binary Tree)

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

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


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

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


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

平衡二叉树在进行插入和删除元素后,平衡性质可能要被打破,也称为失衡,所以要对树进行调整以保证平衡二叉树的平衡性质被保持住。解决的方法就是旋转。旋转又分为单旋转和双旋转,针对不同的情况选择不同的旋转方案: 对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:

Alt text

 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子树,所以属于左左情况。

Alt text

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

Alt text

为使树恢复平衡,我们需要进行两步,第一步,把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))            {                LeftRotate(t->lChild);                RightRotate(t);            }            else                LeftRotate(t);        }    }    else    {        if (t->lChild && t->rChild)        {            if (GetHeight(t->lChild) > GetHeight(t->rChild))            {                AVLNode<T> *s = t->lChild;                //while (s->lChild)                    //s = s->lChild;                t->key = s->key;                Remove(t->lChild, s->key);            }            else            {                AVLNode<T> *s = t->rChild;                //while (s->lChild L)                //  s = s->lChild;                t->key = s->key;                Remove(t->rChild, s->key);            }        }        else        {            AVLNode<T> *p = t;            t = t->lChild ? t->lChild : t->rChild;            delete p;        }    }    return true;}int main(void){    AVLTree<int> T;    for (int i = 1; i <= 7; i++)        T.Insert(i);      /*下面代码建立一个形如:        4      /   \     2     6    / \   / \   1   3 5    7的二叉树*/    T.InOrder();      // 进行中序遍历,结果应该为:1,2,3,4,5,6,7    cout << endl;       T.PreOrder();     //进行先序遍历,结果应为:4,2,1,3,6,5,7    cout << endl;    T.Remove(5);     //移除元素5,再进行中序遍历,结果应为:1,2,3,4,6,7    T.InOrder();    T.Insert(8);    //插入元素8    T.InOrder();    //此时要经过失衡旋转,再进行中序遍历结果应为:1,2,3,4,6,7,8    cout << endl;    T.PreOrder();   //进行先序遍历,结果应为:4,2,1,3,7,6,8    cout << endl;    T.Insert(9);   //在插入元素9,进行中序遍历,结果应为:1,2,3,4,6,7,8,9    T.InOrder();    cout << endl;    bool v = T.Remove(9);  //删除元素9,再进行中序遍历,结果应为:1,2,3,4,6,7,8    T.InOrder();    cout << endl;    T.PreOrder();        //再进行先序遍历,结果应为:4,2,1,3,7,6,8    cout << endl;    cout << endl;    return 0;}

运行结果如下,运行环境为VS2015:
Alt text

学习及资料参考:
《数据结构—使用C++语言描述》(第2版) 人民邮电出版社 陈慧南
《数据结构(C++语言版)》(第3版)清华大学出版社 邓俊辉
C小加博客之一步一步写平衡二叉树(AVL树)http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html

0 0
原创粉丝点击