二叉树旋转
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树旋转相关的知识,希望对你有一定的参考价值。
本文我们来学习二叉树的另一种操作——旋转。掌握了这个神技,你将会在平衡树的道路上所向披靡。
1. 什么是旋转
二叉树节点旋转一共有两种操作:左旋和右旋。
如图 1 所示,左边的二叉树通过左旋得到右边的二叉树;反之右旋同理。(a, b, c 表示子树,而不是单独表示一个节点)
- 左旋:是以节点的"右分支"为轴,进行逆时针旋转。我们将左旋操作定义为 left_rotate.
- 右旋:是以节点的“左分支"为轴,进行顺时针旋转。我们将右旋操作定义为 right_rotate.
在图 1 中,左侧二叉树经过 left_rotate(x) 可以得到右侧,右侧执行 right_rotate(y) 可以得到左侧。
2. 为什么要旋转
在解释这个道理之前,我们先看看执行旋转后,二叉树中节点的深度有什么变化。在图 1 中,二叉树执行左旋后,a 分支所有节点的深度比以前多 1,b 分支保持不变,c 分支所有节点比以前少 1.
这就意味着,通过合适的左旋和右旋操作,我们可以调整二叉树的深度。另一方面,通过合适的左旋和右旋,我们可以把二叉树变换成任意的形状!
思考一下:你有没有办法通过左旋和右旋,把二叉树转换成一条只有一个分支的,向右延展的链?
答案:
left_rotate(4);
right_rotate(10);
right_rotate(8);
right_rotate(5);
right_rotate(4);
right_rotate(2);
当然,你可以尝试更多其它的例子,在你的草稿本上画一画。
3. 旋转算法
有了上面的铺垫后,旋转操作是不是变的特别容易?接下来,我们给出左旋和右旋的伪代码。
下面是我们定义的数据结构:
// 表示二叉树
let tree =
root: null
;
// 表示节点
let node =
left: null, // 左孩子
right: null, // 右孩子
parent: null, // 父节点
key: 0
;
- 左旋
function left_rotate(x)
let y = detach(x.right); // 摘下 x 的右分支 y
transplant(x, y);
let b = detach(y.left); // 摘下 y 的左分支
x.right = b; // 挂到 x 的右侧
b.parent = x;
- 右旋
function left_rotate(y)
let x = detach(y.left);
transplant(y, x);
let b = detach(x.right);
y.left = b;
b.parent = y;
注意到上面有一步 transplant 操作,请参考上一篇文章:《二叉搜索树》。另一个 detach 操作,它的目的就是摘下节点,代码如下。
function deatch(z)
if (z.parent == null) return z;
if (z.parent.left == z)
z.parent.left = null;
else
z.parent.right = null;
z.parent = null;
return z;
4. 总结
- 知道旋转的步骤
- 知道为什么有旋转
- 知道怎样通过旋转对二叉树进行变换
以上是关于二叉树旋转的主要内容,如果未能解决你的问题,请参考以下文章