平衡二叉树的实现
平衡二叉树需要树节点的左子树和右子树高度差不大于等于2,所有普通的二叉树在插入节点后,需要更新父节点的高度,同时递归父节点及其以上的树结构,更新节点高度,判断节点的左子树和右子树的高度差,如果等于2,则需要平衡当前节点(通过左旋,右旋操作),如果等于1,则继续更新当前节点的高度,递归它的父节点,直到节点为根节点。
节点的左旋操作和右旋操作在红黑树中已经实现,此处直接引用代码:
右旋:
- node* rightRotate(node *target){
- node *left=target->left;
- node *grandNode=target->parent;
- if(grandNode!=NULL){
- left->parent=grandNode;
- if(target==grandNode->left)
- grandNode->left=left;
- else
- grandNode->right=left;
- }
- node *move=left->right;
- left->parent=target->parent;
- target->parent=left;
- left->right=target;
- target->left=move;
- if(move!=NULL)
- move->parent=target;
- if(target==root){
- root=left;
- }
- target->height=max(height(target->left),height(target->right))+1;
- left->height=max(height(left->left),height(left->right))+1;
- return left;
- }
- node* leftRotate(node *target){
- node *right=target->right;
- node *grandNode=target->parent;
- if(grandNode!=NULL){
- right->parent=grandNode;
- if(target==grandNode->left)
- grandNode->left=right;
- else
- grandNode->right=right;
- }
- node *move=right->left;
- right->parent=target->parent;
- target->parent=right;
- right->left=target;
- target->right=move;
- if(move!=NULL)
- move->parent=target;
- if(target==root){
- root=right;
- }
- target->height=max(height(target->left),height(target->right))+1;
- right->height=max(height(right->left),height(right->right))+1;
- return right;
- }
这里需要注意的是,旋转操作会导致某些节点的高度产生变化,产生变化的节点就是初始的头节点和旋转后的头节点,这里需要重新设置这两个节点的高度(如图:右旋操作)
这里4节点和2节点需要重新设置节点高度,由于4节点的高度依赖于2节点的高度,所以要先更新2节点高度,再更新4节点的高度
删除节点操作在删除节点的两个子节点都不为空时需要选出节点的后继节点来替换删除节点的值。后继节点的代码引用红黑树中代码
- node* succussor(node* target){
- node* t=target;
- if(target->right!=NULL){
- t=target->right;
- while(t->left!=NULL)
- t=t->left;
- return t;
- }
- while(t->parent!=NULL&&t->parent->right==t){
- t=t->parent;
- }
- return t->parent;
- }
平衡化二叉树的处理,有四种情况
1.左单旋
2.右单旋
3.先右单旋子节点,在左旋
4.先左单旋字节点,再右旋
前面两种情况在红黑树中已经讲解(纯粹左旋右旋),另外两种
这种情况下需要围绕1节点做左旋,但是3节点没有右孩子,需要3节点那个位置有右孩子,因为如果没有的话旋转过后还是节点高度差2,所以应该首先让3节点有右孩子,直接围绕3节点右单旋,然后转换到中间状态,然后再次左单旋即可。
转换情况和上面类似,同时需要注意,同时注意在第一步局部旋转的过程中也需要更新节点的高度。
插入节点代码:
- 插入代码:
- void insert(node *parent,int data){
- if(parent->value>data){
- if(parent->left==NULL){
- node *result=(node *)malloc(sizeof(node));
- result->value=data;
- result->parent=parent;
- parent->left=result;
- result->height=1; //新节点高度都是1
- }else{
- insert(parent->left,data);
- if(height(parent->left)-height(parent->right)==2){
- if(data<parent->left->value){
- parent=rightRotate(parent); //case2 右单旋
- }else{
- leftRotate(parent->left); //case4 左单旋,右单旋
- parent=rightRotate(parent);
- }
- }
- }
- }else{
- if(parent->right==NULL){
- node *result=(node *)malloc(sizeof(node));
- result->value=data;
- result->parent=parent;
- parent->right=result;
- result->height=1;
- }else{
- insert(parent->right,data);
- if(height(parent->right)-height(parent->left)==2){
- if(data>parent->right->value){
- parent=leftRotate(parent); //case1 左单旋
- }else{
- rightRotate(parent->right); //case3 右单旋 左单旋
- parent=leftRotate(parent);
- }
- }
- }
- }
- parent->height=max(height(parent->left),height(parent->right))+1;
- //在递归过程中更新影响节点的高度
- }
在这个递归插入过程中最重要的就是最后一句,作用是在递归过程中,可以认为是更新的节点影响了当前递归过程中的parent节点,要更新该节点的高度,同时外面的递归过程又会以parent节点为基础,更新当前递归函数中的parent节点的高度。
节点删除操作:
二叉树节点的删除操作,参见红黑树中的二叉树节点删除原理:
代码:
- void delete(int data){
- node* target=findNode(data);
- if(target==NULL)
- return;
- node* p;
- if(target->left!=NULL&&target->right!=NULL){ //有两个子节点,寻找代替节点
- node* replace=succussor(target);
- target->value=replace->value;
- node* child=replace->right;
- p=replace->parent;
- if(p!=NULL&&p->left==replace)
- p->left=child;
- else if(p!=NULL&&p->right==replace)
- p->right=child;
- }else{ //有一个子节点,直接删除
- node* t=target->left==NULL?target->right:target->left;
- if(target->parent==NULL)
- root=t;
- p=target->parent;
- if(p!=NULL&&p->left==target)
- p->left=t;
- else if(p!=NULL&&p->right==target)
- p->right=t;
- }
- if(p!=NULL) //影响了节点的高度,需要更新节点高度
- deleteAdjust(p);
- }
二叉树节点的删除会导致原本parent节点左右子树高度的变化,这个变化会影响到父节点及其以上的数据结构,首先更新父节点的高度,同时递归计算左右子树的高度差如果大于等于2,就根据平衡的那几个case执行旋转操作,继续递归该节点的父节点,直到根节点结束。
节点高度调整代码:
- void deleteAdjust(node* target){
- while(target!=NULL){
- target->height=max(height(target->left),height(target->right))+1; //首先更新节点的高度
- if(height(target->right)-height(target->left)==2){
- if(target->right->right==NULL){
- rightRotate(target->right); //case3
- }
- target=leftRotate(target); //case1
- }else if(height(target->left)-height(target->right)==2){
- if(target->left->left==NULL){
- leftRotate(target->left); //case4
- }
- target=rightRotate(target); //case2
- }
- target=target->parent; //递归父节点
- }
- }