二叉排序树 BST & 平衡二叉查找树 AVL


基础知识——二叉树基本性质和操作

二叉排序树 BST

二叉排序树,又叫二叉查找树

性质:

1、左子树的所有结点的值均小于根结点的值

2、右子树的所有结点的值均大于根结点的值

3、左子树和右子树又各是一棵二叉排序树

4、二叉排序树的中序遍历是一个递增序列

图示

在这里插入图片描述

二叉排序树的查找

法1:

空间复杂度 T ( 1 ) T(1) T(1) 效率高

步骤如下:

1、首先判断树是否为空,并且判断根结点的值是否等于待查找的值

2、若树空或者根结点为待查找的值,则返回根结点

3、若树不为空且根结点不等于待查找的值:

  ①若待查找的值比根结点的值小,则查找左子树

  ②若待查找的值比根结点的值大,则查找右子树

Tree *BST_Search(Tree *T, int key, Tree *&p) {
    while (T && T->data != key) {
        p = T; //p为查找节点的parent
        if (key < T->data) {
            T = T->lchild;
        } else {
            T = T->rchild;
        }
    }
    return T;
}

法2:(递归实现)

空间复杂度 T ( H ) T(H) T(H) H为树的高度

步骤如下:

1、首先判断树是否为空,若树空返回NULL,若树不为空继续下面步骤

2、判断根结点是否等于待查找的值,若等于返回根结点,若不等于继续下面步骤

3、判断根结点的值和待查找的值的大小关系

  ①若待查找的值比根结点的值小,则递归查找左子树

  ②若待查找的值比根结点的值大,则递归查找右子树

Tree *BST_Search(Tree *T,int key){
    if(T==NULL){
        return NULL;
    }
    if(T->data==key){
        return T;
    }else if(key<T->data){
        return BST_Search(T->lchild,key);
    }else{
        return BST_Search(T->rchild,key);
    }
}

二叉排序树的插入

空间复杂度 T ( H ) T(H) T(H) H为树的高度

步骤如下:

1、首先判断树是否为空,若树空,则创建二叉树,待插入的值即为根结点
2、若树不为空,判断根结点是否等于待插入的值,若等于,则return
3、判断待插入的值与根结点的大小关系:
  ①若待插入的值比根结点的值小,则递归插入到左子树
  ②若待插入的值比根结点的值大,则递归插入到右子树

void BST_Insert(Tree *&T, int k) {
    if (T == NULL) {
        T = new Tree;
        T->data = k;
        T->lchild = T->rchild = NULL;
    } else if (k == T->data) {
        return;
    } else if (k < T->data) {
        BST_Insert(T->lchild, k);
    } else {
        BST_Insert(T->rchild, k);
    }
}

二叉排序树的创建

直接使用二叉排序树的插入算法进行创建

void Create_BST(Tree *&T,int a[],int n){
    T = NULL;
    int i=0;
    while(i<n){
        BST_Insert(T,a[i]);
        i++;
    }
}

二叉排序树的删除

二叉排序树的删除有多种情况:

1、待删除结点为叶子结点,直接删除
2、待删除结点有左孩子无右孩子或者有右孩子无左孩子,删除结点,连接双亲结点和存在的孩子结点
3、待删除的结点的左右孩子都存在,找该结点右子树中最小的结点进行连接

void BST_Delete(Tree *&T, int key) {
    Tree *parent;
    Tree *result = BST_Search(T, key, parent); //查找删除结点的parent
    //空节点
    if (result == NULL) {
        cout << "不存在此结点" << endl;
        return;
    } else {
        //左右结点均为NULL,此结点为叶子
        if (result->lchild == NULL && result->rchild == NULL) {
            if (parent->lchild == result) {
                parent->lchild = NULL;
            } else {
                parent->rchild = NULL;
            }
            delete result;
        }
            //左孩子为空,连接右孩子
        else if (result->rchild != NULL && result->lchild == NULL) {
            if (parent->lchild == result) {
                parent->lchild = result->rchild;
            } else {
                parent->rchild = result->rchild;
            }
            delete result;
        }
            //右孩子为空,连接左孩子
        else if (result->lchild != NULL && result->rchild == NULL) {
            if (parent->lchild == result) {
                parent->lchild = result->lchild;
            } else {
                parent->rchild = result->lchild;
            }
            delete result;
        }
            //左右结点都不为NULL,找右子树中最小的结点进行连接,即中序遍历右子树第一个结点
        else {
            Tree *q = result->rchild;
            while (q->lchild != NULL) {
                parent = q;
                q = q->lchild;
            }
            char ans = q->data; //存值
            BST_Delete(result,q->data); //先删
            result->data = ans; //后赋值
        }
    }
}

平衡二叉查找树 AVL

平衡二叉查找树:带有平衡条件的二叉查找树

平衡因子:某个结点的左右子树高度差

最小不平衡子树:距离插入结点最近,且平衡因子的绝对值大于1的结点为根的子树

性质

1、一棵BST树

2、每个结点的平衡因子只能是 -1,0,1

分类

导致二叉树不平衡有4种插入方式:LL,RR,LR,RL

LL

左孩子的左子树插入导致不平衡(右单旋转)

原树:

C < B < D < A < E

在这里插入图片描述

插入结点:

F < C < B < D < A < E

在这里插入图片描述

右单旋转:

F < C < B < D < A < E

B作为根结点,A向右旋转作为B的右子树,由于结点D满足B < D < A,所以将D插入到B的右子树,A的左子树上

在这里插入图片描述

代码实现:

A->lchild = B->rchild;
B->rchild = A;
A->parent->lchild/rchild = B;

RR

右孩子的右子树插入导致不平衡(左单旋转)

原树:

B < A < D < C < E

在这里插入图片描述

插入结点:

B < A < D < C < E < F

在这里插入图片描述

左单旋转:

B < A < D < C < E < F

C作为根结点,A向左旋转作为C的左子树,由于结点D满足A < D < C,所以将D插入到C的左子树,A的右子树上

在这里插入图片描述

代码实现:

A->rchild = C->lchild;
C->lchild = A;
A->parent->lchild/rchild = C;

LR

左孩子的右子树插入导致不平衡(先左后右双旋转)

原树:

D < B < E < A < C

在这里插入图片描述

插入结点:

D < B < E < F < A < C

以插入到左孩子的右子树的右子树导致不平衡为例:

在这里插入图片描述

左旋:

B左旋,E代替B,B成为E的左子树,F为E的右子树,此时:

D < B < E < F < A < C

在这里插入图片描述

右旋:

A右旋,A成为E的右子树,E的右孩子F成为A的左孩子,此时:

D < B < E < F < A < C

在这里插入图片描述

RL

右孩子的左子树插入导致不平衡(先右后左双旋转)

原树:

B < A < D < C < E

在这里插入图片描述

插入结点:

B < A < F < D < C <E

以插入到右孩子的左子树的左子树导致不平衡为例:

在这里插入图片描述

右旋:

C左旋,D代替C,C成为D的右子树,F为D的左子树,此时:

B < A < F < D < C < E

在这里插入图片描述

左旋:

A左旋,A成为D的左子树,D的左孩子F成为A的右孩子,此时:

B < A < F < D < C < E

在这里插入图片描述

小结

1、调整的都是最小不平衡子树

2、

类型原因调整
LL在A的左孩子的左子树插入导致不平衡A的左孩子结点右上旋
RR在A的右孩子的右子树插入导致不平衡A的右孩子结点左上旋
LR在A的左孩子的右子树插入导致不平衡A的左孩子的右孩子 先左上旋再右上旋
RL在A的右孩子的左子树插入导致不平衡A的右孩子的左孩子 先右上旋再左上旋

3、含有n的结点的AVL的最大深度和平均查找长度均为: O ( l o g 2 N ) O(log_2N) O(log2N)

结语

BST和AVL理解上较为容易,但是代码比较难写,要是思路有问题或者不理解的地方请在下面评论或者联系我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值