平衡搜索二叉树-指针粗糙版
文章目录
两个旋转,四种情况!
平衡因子
平衡因子BF(Balance Factor),等于左子树的高度减去右子树。
-
规定: 空节点的高度为0,叶子节点的高度为1。
-
|BF|= 0时
很平衡,没什么问题。此时以此结点为根的子树平衡,且其高度不变。因此从根结点到此结点的路径上各结点子树高度不变,从而各结点的平衡因子不变。此时可结束重新平衡过程。
- |BF|= 1时
此时虽然还是满足平衡条件(|BF|<=1)的,但高度已经发生变化,所以应该继续考察当前节点的父节点。
- |BF|= 2时
此时就不满足平衡条件了,要开始旋转了。
结构体定义如下:
typedef struct node{
int val;
int h;
struct node* left;
struct node* right;
}avlnode;
typedef avlnode* avlptr;
//求平衡因子的函数。
int getF(avlptr root){
return (root->left!=nullptr?root->left->h:0 )
-
(root->right!=nullptr?root->right->h:0);
}
两个旋转
主要就是左旋和右旋两种操作。
左旋:
如图所示,左旋是为了解决右子树过高问题的操作。注意这边不要在意图上给出的平衡因子,因为他这个图这边好像还把平衡因子算错了,应该是-2和-1,这边主要考虑的是为什么左旋,原因其实就是因为右边高了,要左旋。
个人一点粗浅的看法:很多兄弟在学这个的时候会感觉很晕,其实就是把左旋也算进失衡的那四种情况里面了,实际上左旋和右旋只是两种旋转的方法,我们在不同的情况中运用不同的旋转方法,仅此而已
左旋的操作流程是: 找出失衡的结点A,因为是右边过高,所以A的右子树B将会是新的树的根,然后A成为B的左子树,这个旋转操作在B有左子树的时候会比较复杂,需要把B的左子树作为A的右子树,
avlptr leftRotate(avlptr root){
avlptr right = root->right; //right将是新树的根
root->right = right->left; //把B的左子树作为A的右子树
right->left = root; //A成为B的左子树
right->left->h = getH(right->left);
right->h = getH(right);
//这两行代表的是更新B和A的高度或者平衡因子。具体实现操作因人而异,我这边是自己写了个求高度的函数
//这种记录高度的方式在求平衡因子的时候要多一步麻烦一点,但是会更直观一点。
//存在直接修改平衡因子的方法,但是会比较难搞懂一点。
return right; //返回新的树根结点
}
int getH(avlptr root){
if(root==nullptr){
return 0;
}
else{
return max(
root->left!=nullptr?root->left->h:0 //左子树高度
,
root->right!=nullptr?root->right->h:0 //右子树高度
)+1; //最高子树高度+1
}
}
//本来应该用递归的方法来求高度的,但是我为了节省时间复杂度,在插入的时候就更新节点的高度,所以在调整树需要旋转的时候,能保证相关树的高度都是更新过的,以下是能保证完全正确并且好理解的一种求高度的递归函数
int getHeight(avlptr root) {
if(root == NULL) {
return 0;
} else {
return max(getHeight(root->left),getHeight(root->right)) + 1;
}
}
右旋:
右旋和左旋完全相反,只要能懂一个,另一个就能懂,不再赘述
avlptr rightRotate(avlptr root){
avlptr left = root->left;
root->left = left->right;
left->right = root;
left->right->h = getH(left->right);
left->h = getH(left);
return left;
}
四种失衡情况
前面说到,当|BF|>1时,就能判断出这个结点失衡了,那么就对目前的情况进行分类,就能得到四种情况,也就是我们常听说的LL LR RR RL四种情况。
BF=2时
因为是正数,所以是左边子树高了,所以要观察左子树的情况
bf(left)=1 ---- LL
可以知道是左子树的左子树高了,也就是说新插入的结点是插入在左子树的左子树上,这就是LL情况,两个L的意思就是这个。
- 对于这种情况,只要做一次右旋即可。
bf(left)=-1 ---- LR
可以知道是左子树的右子树高了,也就是说新插入的结点是插入在左子树的右子树上。
- 因为是左子树的右边高了,所以应该对左子树进行一次左旋。回到根节点,是左子树高了,所以要对根节点进行一次右旋。
BF=-2时
因为是负数,所以是右边子树高了,所以要看向右子树的情况
bf(left)=-1 ---- RR
可以知道是右子树的右子树高了,也就是说新插入的结点是插入在右子树的右子树上,这就是RR情况,两个R的意思就是这个。
- 对于这种情况,只要做一次左旋即可。
bf(left)=1 ---- RL
可以知道是右子树的左子树高了,也就是说新插入的结点是插入在右子树的坐子树上。
- 因为是右子树的左边高了,所以应该对右子树进行一次右旋。回到根节点,是右子树高了,所以要对根节点进行一次左旋。
以上便是四种失衡情况即应对方式。
插入操作
avlptr insert(avlptr root,int val){
if(root==nullptr){
root = init();
root->val = val;
root->h=1;
root->left = nullptr;
root->right = nullptr;
return root;
}//初始化操作,记得要把指针变量赋值好,要不然可能会出奇奇怪怪的问题。
else{//插入和普通二叉搜索树没有什么差别
if(root->val==val){
//有些题目会保证不重复。
}
else if(root->val > val){
root->left = insert(root->left,val);
}else{
root->right = insert(root->right,val);
}
//在插入完成后的操作
root->h = getH(root); //调整高度
root = rebalance(root); //对结点进行平衡操作,由于是递归,平衡操作会从插入结点回溯到根节点
}
return root;
}
avlptr rebalance(avlptr root){
int factor = getF(root); //求出这个树的平衡因子
if(factor>1){ //BF=2 左高 看向左节点
if(getF(root->left)>0){
//LL
return rightRotate(root);
}else if(getF(root->left)<=0){
//LR
root->left = leftRotate(root->left);
return rightRotate(root);
}
}
else if(factor<-1){ //BF=-2 右高 看向右节点
if(getF(root->right)<0){
//RR
return leftRotate(root);
}
else if(getF(root->right)>=0){
//RL
root->right = rightRotate(root->right);
return leftRotate(root);
}
}
else{ //平衡状态 直接返回就好
return root;
}
}
删除操作
删除操作是比较复杂的,因为搜索二叉树的删除本身就比较复杂,涉及到先序节点和后继节点的查找。可以去这个博客学习如何删除二叉搜索树中的节点。同样也是用回溯的思想进行平衡操作。
void del(int val,avlptr* rootptr){
avlptr root = *rootptr;
if(root==nullptr){
return ;
}
if(val<root->val){
del(val,&(root->left));
}
else if(val>root->val){
del(val,&(root->right));
}
else{//val==root->val 找到了
if(root->left==nullptr&&root->right==nullptr){
*rootptr = nullptr;
return;
}
else if(root->left!=nullptr){
avlptr ps = preSuccessor(root);
root->val = ps->val;
del(ps->val,&(root->left));
}else{
avlptr s = successor(root);
root->val = s->val;
del( s->val,&(root->right));
}
}
(*rootptr)->h = getHeight(*rootptr);
*rootptr = rebalance(*rootptr);
}
// NOTICE : 默认左子树不为空!!
avlptr preSuccessor(avlptr root){
if(root==nullptr){
return nullptr;
}else{
avlptr left = root->left;
while(left->right){
left = left->right;
}
return left;
}
}
// NOTICE : 默认右子树不为空!!
avlptr successor(avlptr root){
if(root==nullptr){
return nullptr;
}else{
avlptr right = root->right;
while(right->left){
right = right->left;
}
return right;
}
}
全部代码
说实话用指针是真的难受,不知道考试的时候能不能用链式数组代替吧,指针是真的难受…还是Java比较Flexible一点…
#include<bits/stdc++.h>
using namespace std;
typedef struct node{
int val;
int h;
struct node* left;
struct node* right;
}avlnode;
typedef avlnode* avlptr;
avlptr init(){
return (avlptr)malloc(sizeof(avlnode));
}
int getH(avlptr root){
if(root==nullptr){
return 0;
}
else{
return max(root->left!=nullptr?root->left->h:0,root->right!=nullptr?root->right->h:0)+1;
}
}
avlptr rightRotate(avlptr root){
avlptr left = root->left;
root->left = left->right;
left->right = root;
left->right->h = getH(left->right);
left->h = getH(left);
return left;
}
avlptr leftRotate(avlptr root){
avlptr right = root->right;
root->right = right->left;
right->left = root;
right->left->h = getH(right->left);
right->h = getH(right);
return right;
}
int getF(avlptr root){
return (root->left!=nullptr?root->left->h:0 )- (root->right!=nullptr?root->right->h:0);
}
avlptr rebalance(avlptr root){
int factor = getF(root);
//cout<<"bf="<<factor<<endl;
if(factor>1){
if(getF(root->left)>0){
//LL
return rightRotate(root);
}else if(getF(root->left)<=0){
//LR
root->left = leftRotate(root->left);
return rightRotate(root);
}
}
else if(factor<-1){
if(getF(root->right)<0){
//RR
return leftRotate(root);
}
else if(getF(root->right)>=0){
//RL
root->right = rightRotate(root->right);
return leftRotate(root);
}
}
else{
return root;
}
}
avlptr insert(avlptr root,int val){
if(root==nullptr){
root = init();
root->val = val;
root->h=1;
root->left = nullptr;
root->right = nullptr;
return root;
}
else{
if(root->val==val){
//有些题目会保证不重复。
}
else if(root->val > val){
root->left = insert(root->left,val);
}else{
root->right = insert(root->right,val);
}
//root->h = max(root->left!=nullptr?root->left->h:0,root->right!=nullptr?root->right->h:0)+1;
root->h = getH(root);
root = rebalance(root);
}
return root;
}
// NOTICE : 默认左子树不为空!!
avlptr preSuccessor(avlptr root){
if(root==nullptr){
return nullptr;
}else{
avlptr left = root->left;
while(left->right){
left = left->right;
}
return left;
}
}
// NOTICE : 默认右子树不为空!!
avlptr successor(avlptr root){
if(root==nullptr){
return nullptr;
}else{
avlptr right = root->right;
while(right->left){
right = right->left;
}
return right;
}
}
int getHeight(avlptr root) {
if(root == NULL) {
return 0;
} else {
return max(getHeight(root->left),getHeight(root->right)) + 1;
}
}
void del(int val,avlptr* rootptr){
avlptr root = *rootptr;
if(root==nullptr){
return ;
}
if(val<root->val){
del(val,&(root->left));
}
else if(val>root->val){
del(val,&(root->right));
}
else{//val==root->val 找到了
if(root->left==nullptr&&root->right==nullptr){
//cout<<root->val<<endl;
*rootptr = nullptr;
//cout<<(*rootptr)->val<<endl;
return;
}
else if(root->left!=nullptr){
avlptr ps = preSuccessor(root);
root->val = ps->val;
del(ps->val,&(root->left));
}else{
avlptr s = successor(root);
root->val = s->val;
del( s->val,&(root->right));
}
}
cout<<"huisu:"<<root->val<<endl;
(*rootptr)->h = getHeight(*rootptr);
*rootptr = rebalance(*rootptr);
}
void print(){
}
void inorder(avlptr root){
if(root==nullptr) return;
else{
inorder(root->left);
cout<<root->val<<"("<<getF(root)<<") ";
inorder(root->right);
}
}
void preorder(avlptr root){
if(root==nullptr) return;
cout<<root->val<<" ";
preorder(root->left);
preorder(root->right);
}
int main(){
int n;cin>>n;
avlptr root = nullptr;
//cout<<root<<endl;
while(n--){
int op;
cin>>op;
if(op==1){
int val;
cin>>val;
root = insert(root,val);
}
if(op==2){
int val;
cin>>val;
del(val,&root);
}
}
inorder(root);
return 0;
}