简介
旋转是当增删处理完成之后,无法自平衡的最终解决手段,平衡二叉树和红黑树都会用到, 这里单独分一章节介绍,方便理解
旋转一共有两种:左旋、右旋,为对称效果,理解一个另一个即理解
节点基础数据结构如下
typedef struct TreeNode {
int data;
struct TreeNode *parentNode; //父节点
struct TreeNode *l, *r; //左右子节点
}LSTreeNode;
左旋
左旋如下图所示,算是比较完整的左旋图,如果E、F、G都消失,甚至B也消失的话,那么左旋过程仍然保持一致,空节点代替原节点参与旋转,这样可以保持旋转过程一致
实际旋转实现方式有两种:通过父节P参与旋转的对接、不通过P节点以替换式方案实现旋转
方案一:
如上图所示,通过父节P参与旋转的对接
旋转过程如下:
1.保存A节点
2.P节点指向A的右孩子C(相当于先断开了P-A键,再指向,也是保存A的原因)
3.A的右孩子指针,指向C的左孩子F
4.C的左孩子指针指向A
5.P指向C
可以看出,此过程参与左旋过程不能为空的节点的P、A、C,他们的孩子部分也会参与移动,其他的直接都不需要动即可
优点:交换方便,逻辑简单易懂
缺点:对父节点P比较依赖,对于根节点失衡的情况处理会不到位(A如果为根节点,如下图所示)
方案二(推荐):
如上图所示,不通过P节点以替换式方案实现旋转
旋转过程如下(写的过程可以参考下图,更清晰):
1.保存C节点和A节点,,A节点与C节点进行值的替换,节点关系保持不变,名称替换
2.C节点的右节点指针指向A的右节点D,此时C->A的指针消失
3.A的右孩子指针指向其左孩子,A的左孩子指针指向C的左孩子(这里默认C->B指针断裂,实际未断开)
4.将C的左孩子指向A(C->B指针实际断开,重新指向A)
方案二的旋转流程图:
方案二代码如下:
void swapLeftRotation(LSTreeNode *parentNode, LSTreeNode *rightNode) {
int data = parentNode->data;
parentNode->data = rightNode->data;
rightNode->data = data;
//因为bortherNode有临时指针,所以把bortherNode节点当做临时节点作为突破口转接,父节点转化记得变化
parentNode->r = rightNode->r;
rightNode->r->parentNode = parentNode;
rightNode->r = rightNode->l;
rightNode->l = parentNode->l;
if (parentNode->l) parentNode->l->parentNode = rightNode;
parentNode->l = rightNode;
}
右旋
右旋如下图所示,算是比较完整的右旋图,如果E、F、G都消失,甚至B也消失的话,那么左旋过程仍然保持一致,空节点代替原节点参与旋转,这样可以保持旋转过程一致
参考下图会发现,左旋后的结果,选另一边在右旋,会变回原来的样子
实际旋转实现方式有两种:通过父节P参与旋转的对接、不通过P节点以替换式方案实现旋转
方案一:
如上图所示,通过父节P参与旋转的对接
旋转过程如下:
1.保存C节点
2.P节点指向C的左孩子A(相当于先断开了P-C键,再指向,也是保存C的原因)
3.C的左孩子指针,指向A的右孩子F
4.A的右孩子指针指向C
5.P指向A
可以看出,此过程参与左旋过程不能为空的节点的P、C、A,他们的孩子部分也会参与移动,其他的直接都不需要动即可
优点:交换方便,逻辑简单易懂
缺点:对父节点P比较依赖,对于根节点失衡的情况处理会不到位(A如果为根节点,如下图所示)
方案二(推荐):
如上图所示,不通过P节点以替换式方案实现旋转
旋转过程如下(写的过程可以参考下图,更清晰):
1.保存C节点和A节点,C节点与A节点进行值的替换,节点关系保持不变,名称替换
2.A节点的左节点指针指向A的左节点B,此时A->C的指针消失
3.C的左孩子指针指向其右孩子,C的右孩子指针指向A的右孩子(这里默认A->B指针断裂,实际未断开)
4.将A的右孩子指向C(A->B指针实际断开,重新指向C)
方案二的旋转流程图:
方案二代码如下:
void swapRightRotation(LSTreeNode *parentNode, LSTreeNode *leftNode) {
int data = parentNode->data;
parentNode->data = leftNode->data;
leftNode->data = data;
//因为bortherNode有临时指针,所以把bortherNode节点当做临时节点作为突破口转接,父节点转化记得变化
parentNode->l = leftNode->l;
leftNode->l->parentNode = parentNode;
leftNode->l = leftNode->r;
leftNode->r = parentNode->r;
if (parentNode->r) parentNode->r->parentNode = leftNode;
parentNode->r = leftNode;
}
最后
了解了旋转逻辑之后,就可以进一步学习平衡二叉树和红黑树了