平衡二叉树(AVL)(一)

来源:互联网 发布:窥一隅而知全貌的意思 编辑:程序博客网 时间:2024/06/06 12:24

平衡二叉搜索树是具有某些特殊性质的二叉搜索树,对于每一个节点 p , 它的左右子树的高度相差不超过 1。将二叉排序树维持在平衡状态。因此查找,删除,插入的操作的时间复杂度维持在O(log2n).
查找元素,遍历,找最大最小值,求树的高度,这些基本操作和普通的二叉搜索树几乎一样,直接借用二叉搜索树的成员函数,具体实现何以参见
二叉搜索树
对其进行插入元素时,首先按照普通的二叉搜索树插入,但是插入元素之后可能导致叶子节点的高度差大于1,这时候需要进行节点的旋转,恢复平衡二叉树的性质。
如下图插入键值为 7 节点,很显然新的排序二叉树的不再满足平衡二叉树的性质。
这里写图片描述

旋转

由于任意节点最多有两个儿子,高度不平衡时,此节点的两颗子树的高度相差2,有以下几种情况:
有以下几种情况

case1:左左情况 13 节点的左子树 8节点 的高度比右子树 15节点 的高度大 2, 左子树 8节点的 左子树 5节点 的高度大于 右子树 10节点

case2:左右情况 13 节点的左子树 8节点 的高度比右子树 15节点 的高度大 2, 左子树 8节点的 右子树 10节点 的高度大于 右子树 5节点

case3:右左情况 9 节点的右子树 15节点 的高度比左子树 8节点 的高度大 2, 右子树 15节点的 左子树 12节点 的高度大于 右子树 18节点

case3:右右情况 9 节点的右子树 15节点 的高度比左子树 8节点 的高度大 2, 右子树 15节点 的右子树 18节点 的高度大于 左子树 12节点

单旋转:
case1 和 case4 属于对称情况,只需要一次旋转就可以,下面分析case1 左左情况 singRotateLeft(k1) :(节点k1 不符合平衡二叉树的性质)

这里写图片描述
由对称性对于case4执行 singRotateRight(K1)。

void avlTree::singRotateLeft(Node *p) // case1:左左情况左旋{    Node *pLchild = p->lchild;    this->transplant(p, pLchild);    p->lchild = pLchild->rchild;    if (pLchild->rchild) // 可能待左转节点的左孩子没有右孩子    {        pLchild->rchild->parent = p;    }    pLchild->rchild = p;    p->parent = pLchild;}void avlTree::singRotateRight(Node *p) // case4:右右情况{    Node *pRchild = p->rchild;    this->transplant(p, pRchild);    p->rchild = pRchild->lchild;    if (pRchild->lchild) // 可能待右转节点的右孩子没有左孩子    {        pRchild->lchild->parent = p;    }    pRchild->lchild = p;    p->parent = pRchild;}
// 移植节点 void transplant(Node* oldNode, Node* newNode); // 移植节点

参见二叉搜索树 里的具体实现。
http://blog.csdn.net/lmx2014001/article/details/52072784
双旋转
case2 和 case3 属于对称情况,下面分析 case 2:(节点k1 不符合平衡二叉树的性质)
先以节点 k2 = k1-> lchild为研究对象进行一次 右右情况的旋转,singRotateRight(Node *p),将问题转化为 case1,然后在case1 情况下,再经过左左情况的旋转 singRotateLeft(k1)。
case2 左左情况:

void avlTree::doubleRotateLR(Node *p) // case2:左右情况{    this->singRotateRight(p->lchild);    this->singRotateLeft(p);}void avlTree::doubleRotateRL(Node *p) // case3:右左情况{    this->singRotateLeft(p->rchild);    this->singRotateRight(p);}

插入节点

插入节点分两步:
第一步,和普通的二叉排序树相同的步骤插入;
第二步,从插入新节点的父亲开始,沿着走向根节点的简单路径,不断检查是否满足平衡二叉树的性质。
在描述具体检测步骤之前,先引入 任意节点 p 的平衡因子,即该节点 p 的左子树高度与右子树高度之差,具体实现如下:
易知p>getBFac() 可能的返回值为 2, 1, 0, -1, -2,当取值为 2 和 - 2时不满足平衡性质。

int Node::getBFac(){    return this->lchild->getHight() - this->rchild->getHight();}

详细检查及恢复步骤:
1. 求得新插入节点 N 的的父亲结点 p 的平衡因子 p>getBFac(),可能取值只可能为 1,0,-1,图解如下:
这里写图片描述
case1,2不会改变以 父亲结点 p 为根节点的子树的高度,因此不会破坏整棵树的平衡性质。不需要进一步检测!
case3,4使父亲结点 p 为根节点的子树的高度由 1 (只有一个根节点 p)增加到了 1,因此可能破坏整棵树的平衡性质。需要进行下一步检测。

  1. 新插入节点N的父亲节点 p 的平衡因子为 1 时,进一步检查 节点N 的祖父节点G,如果 G>getBFac() 为2 或者 -2 ,则找到问题节点,并对其进行旋转操作,不再继续向上检测问题节点;如果 G>getBFac()=0 时才可以确定新插入节点并没有影响整棵树的平衡性质,则停止检查,不需要维护了;G>getBFac()为1 或者 -1, 则将可能出现的问题点上移。

  2. 不断重复步骤 2,直到不断上升的祖父节点 G 越过根节点变为 NULL。

void  avlTree::insertAvlNode(int newValue){    Node *newNode = new Node(newValue);    Node *p, *tempParent = new Node(), *tempGrandpa = new Node(), *tempuncle = new Node();    int parentFac, GrandpaFac;    if (this->getSize() == 0)    {        this->root = newNode;    }    else    {        p = this->root;        while (p)        {            // 保存父亲结点,万一他的孩子为空,跳出循环后还可以翻出来            tempParent = p;            if (newValue > p->data)            {                p = p->rchild;            }            else            {                p = p->lchild;            }        }        newNode->parent = tempParent;        if (newValue > tempParent->data)        {            tempParent->rchild = newNode;        }        else        {            tempParent->lchild = newNode;        }        // 以上步骤各普通的二叉排序树插入节点一样        parentFac = tempParent->getBFac();        if (parentFac) // 新插入节点的父节点 tempParent 平衡因子为非零,则tempParent 的高度肯定增加了 1,由原先的 0 变为1         {            int rFlag = 0; // 标记是否旋转,最多只需要旋转一次(doubleRotate 也算一次)            tempGrandpa = tempParent->parent;            while (tempGrandpa && !rFlag)            {                GrandpaFac = tempGrandpa->getBFac();                if (!GrandpaFac)                {                    break;                }                if (GrandpaFac == 2) //左                {                    if (parentFac == 1) // 左左                    {                        singRotateLeft(tempGrandpa);                    }                    else // 左右                    {                        doubleRotateLR(tempGrandpa);                    }                    rFlag = 1;                }                if (GrandpaFac == -2) //右                {                    if (parentFac == 1) // 右左                    {                        singRotateLeft(tempGrandpa);                    }                    else // 右右                    {                        singRotateRight(tempGrandpa);                    }                    rFlag = 1;                }                if (!rFlag) // 把可能出现问题的节点往上移动                {                    tempParent = tempGrandpa;                    parentFac = GrandpaFac;                    tempGrandpa = tempParent->parent;                }            }        }    }}

欢迎批评指正 !

0 0