文章目录
基础知识——二叉树基本性质和操作
二叉排序树 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理解上较为容易,但是代码比较难写,要是思路有问题或者不理解的地方请在下面评论或者联系我