数据结构与算法----二叉树的旋转

655 阅读4分钟

简介

旋转是当增删处理完成之后,无法自平衡的最终解决手段,平衡二叉树和红黑树都会用到, 这里单独分一章节介绍,方便理解

旋转一共有两种:左旋、右旋,为对称效果,理解一个另一个即理解

节点基础数据结构如下

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;
}

最后

了解了旋转逻辑之后,就可以进一步学习平衡二叉树和红黑树了