AVL树
平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
平衡因子
某结点的左子树与右子树的 高度(深度)差 即为该结点的 平衡因子(BF,Balance Factor)。平衡二叉树上所有结点的平衡因子只可能是 -1,0 或 1。如果某一结点的平衡因子绝对值大于1则说明此树不是平衡二叉树。为了方便计算每一结点的平衡因子我们可以为每个节点赋予 height 这一属性,表示此节点的高度。
节点的 height 是获取该节点最低叶子所需的步数。 例如,在下面的树中,从A到E需要三个步,因此A的高度为3。B的高度为2,C的高度为1,其他的高度为0,因为它们是叶节点。
高度和深度是相反的表示,深度是 从上到下 数的,而高度是 从下往上 数。
我们先来看看高度和深度的定义,某节点的深度是指从根节点到该节点的最长简单路径边的条数,而高度是指从该节点到叶子节点的最长简单路径边的条数。
基础设计
首先我们可以设计出 AVL
树节点,并且实现一些简单通用的方法供后续操作。
/**
* AVLTree是BST,所以节点值必须是可比较的
*/
public class AvlTree<E: Comparable>{
private class Node {
public var e: E
public var left: Node? = nil
public var right: Node? = nil
public var height: Int = 1
public init(e: E){
self.e = e
}
}
private var root: Node?
private var size: Int
public init() {
root = nil
size = 0
}
//获取某一结点的高度
private func getHeight(node: Node?) -> Int {
guard let node = node else {
return 0
}
return node.height
}
public func getSize() -> Int {
return size
}
public func isEmpty() -> Bool {
return size == 0
}
/**
* 获取节点的平衡因子
* @param node
* @return
*/
private func getBalanceFactor(node: Node?) -> Int {
guard let node = node else {
return 0
}
return abs(getHeight(node: node.left) - getHeight(node: node.right))
}
//判断树是否为平衡二叉树
public func isBalanced() -> Bool {
return isBalanced(node: root)
}
private func isBalanced(node: Node?) -> Bool {
guard let node = node else {
return true
}
let balanceFactory = getBalanceFactor(node: node)
if balanceFactory > 1 {
return false
}
return isBalanced(node: node.left) && isBalanced(node: node.right)
}
}
添加节点
往平衡二叉树中添加节点很可能会导致二叉树失去平衡,所以我们需要在每次插入节点后进行平衡的维护操作。插入节点破坏平衡性有如下四种情况:
LL(需要右旋平衡)
LL的意思是向左子树(L)的左孩子(L)中插入新节点后导致不平衡,这种情况下需要 右旋操作,而不是说LL的意思是右旋,后面的也是一样。
我们将这种情况抽象出来,得到下图:
我们需要对节点y进行平衡的维护。步骤如下图所示:
/**
* 右旋转,返回根节点
*/
private func rightRotate(y: Node?) -> Node? {
guard let y = y else {
return nil
}
let x = y.left
let t3 = x?.right
x?.right = y
y.left = t3
//更新height
y.height = max(getHeight(node: y.left),getHeight(node: y.right))+1
x?.height = max(getHeight(node: x?.left),getHeight(node: x?.right))+1
return x
}
RR(需要左旋平衡)
我们将这种情况抽象出来,得到下图:
我们需要对节点y进行平衡的维护。步骤如下图所示:
/**
* 左旋转
*/
private func leftRotate(y: Node?) -> Node? {
guard let y = y else {
return nil
}
let x = y.right
let t3 = x?.left
x?.left = y
y.right = t3
//更新height
y.height = max(getHeight(node: y.left),getHeight(node: y.right)) + 1
x?.height = max(getHeight(node: x?.left),getHeight(node: x?.right)) + 1
return x
}
LR(先左旋后右旋)
我们将这种情况抽象出来,得到下图:
我们需要对节点y进行平衡的维护。步骤如下图所示:
RL(先右旋后左旋)
我们将这种情况抽象出来,得到下图:
我们需要对节点y进行平衡的维护。步骤如下图所示:
添加节点代码
extension AvlTree {
// 向二分搜索树中添加新的元素(key, value)
public func add(e: E) {
root = add(node: root, e: e)
}
// 向以node为根的二分搜索树中插入元素(key, value),递归算法
// 返回插入新节点后二分搜索树的根
private func add(node: Node?, e: E) -> Node? {
guard let node = node else {
size += 1
return Node(e: e)
}
//判断是插入根节点的左子树还是右子树
if e < node.e {
node.left = add(node: node.left, e: e)
} else if e > node.e {
node.right = add(node: node.right, e: e)
}
//更新height
node.height = 1 + max(getHeight(node: node.left),getHeight(node: node.right))
//计算平衡因子
let balanceFactor = getBalanceFactor(node: node)
if balanceFactor > 1 && getBalanceFactor(node: node.left) > 0 {
//右旋LL
return rightRotate(y: node)
}
if balanceFactor < -1 && getBalanceFactor(node: node.right) < 0 {
//左旋RR
return leftRotate(y: node)
}
//LR
if balanceFactor > 1 && getBalanceFactor(node: node.left) < 0 {
node.left = leftRotate(y: node.left)
return rightRotate(y: node)
}
//RL
if balanceFactor < -1 && getBalanceFactor(node: node.right) > 0 {
node.right = rightRotate(y: node.right)
return leftRotate(y: node)
}
return node
}
}
删除节点
在删除AVL树节点前需要知道二分搜索树的节点删除操作【点此学习吧!】,和二分搜索树删除节点不同的是我们删除AVL树的节点后需要进行平衡的维护操作。
extension AvlTree {
//查找二叉搜索树的节点
private func getNode(root: Node?, e: E) -> Node? {
guard let root = root else {
return nil
}
if(root.e == e) {
return root
} else if e > root.e {
return getNode(root: root.right, e: e)
} else {
return getNode(root: root.left, e: e)
}
return nil
}
//移出节点
public func remove(e: E) -> E? {
guard let node = getNode(root: root, e: e) else {
return nil
}
root = remove(node: root, e: e)
return node.e
}
//二叉搜索树的最小值一直找左孩子就行了
private func minNode(node: Node?) -> Node? {
guard let node = node else {
return nil
}
if let node = node.left {
return minNode(node: node.left)
} else {
return node
}
}
private func remove(node: Node?, e: E) -> Node? {
guard let node = node else {
return nil
}
var retNode: Node?
if e < node.e {
node.left = remove(node: node.left , e: e)
retNode = node
} else if e > node.e {
node.right = remove(node: node.right, e: e)
retNode = node
} else {
// 待删除节点左子树为空的情况
if node.left == nil {
let rightNode = node.right
node.right = nil
size -= 1
retNode = rightNode
} else if node.right == nil {
// 待删除节点右子树为空的情况
let leftNode = node.left
node.left = nil
size -= 1
retNode = leftNode
} else {
// 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
let successor = minNode(node: node.right)!
successor.right = remove(node: node.right, e: successor.e)
successor.left = node.left
node.left = nil
node.right = nil
retNode = successor
}
}
guard let retNodeTmp = retNode else {
return nil
}
//维护平衡
//更新height
retNodeTmp.height = 1 + max(getHeight(node: retNodeTmp.left),getHeight(node: retNodeTmp.right))
//计算平衡因子
let balanceFactor = getBalanceFactor(node: retNodeTmp)
if balanceFactor > 1 && getBalanceFactor(node: retNodeTmp.left) >= 0 {
//右旋LL
return rightRotate(y: retNodeTmp)
}
if balanceFactor < -1 && getBalanceFactor(node: retNodeTmp.right) <= 0 {
//左旋RR
return leftRotate(y: retNodeTmp)
}
//LR
if balanceFactor > 1 && getBalanceFactor(node: retNodeTmp.left) < 0 {
node.left = leftRotate(y: retNodeTmp.left)
return rightRotate(y: retNodeTmp)
}
//RL
if balanceFactor < -1 && getBalanceFactor(node: retNodeTmp.right) > 0 {
node.right = rightRotate(y: retNodeTmp.right)
return leftRotate(y: retNodeTmp)
}
return retNodeTmp
}
}
参考文章