0
点赞
收藏
分享

微信扫一扫

平衡树的代码实现

点击前往博客阅读获得更优体验

为了美观,CSDN这里只给出 Kotlin的实现, C++版本的代码可以点击上面的链接到我的博客里面查看

文章目录


  在之前的博文中我们讨论了 二叉搜索树 与 平衡二叉搜索树 的理论实现,这次让我们用Kotlin来实现一个弱平衡树。

{% p blue center, 注意:以下代码实现的弱平衡树不是任何网上的弱平衡树(比如:红黑树、Splay等),是我完全按照前面的理论写的 %}

结构设计

  首先,我们肯定需要先设计一下代码结构。

  1. 需要一个类表示
  2. 需要一个类(结构体)表示节点

  这么看来,我们至少需要两个类:

class WeakBalanceTree<T : Comparable<T>> {
    
    class Node<T : Comparable<T>>: Comparable<T>
    
}

Node设计

  现在,我们再来看Node中需要记录些什么信息:

  1. 节点的value
  2. 与当前节点相连的其它三个节点
  3. 节点深度

  那么,我们很容易就能写出下面的实现:

class Node<T : Comparable<T>>(value: T) : Comparable<T> {
    
    var value: T = value
        internal set
    var deep = 1
        internal set
    var left: Node<T>? = null
        internal set
    var right: Node<T>? = null
        internal set
    var father: Node<T>? = null
        internal set
    
}

  那么,一个节点需要干什么活呢?我们先假设它需要负责这些功能:

  1. 检查以该节点为根的树是否平衡
  2. 判断以该节点为根的树的重心
  3. 旋转树
  4. 维护树的平衡
  5. 维护树的深度
  6. 查找左子树最大值(或右子树最小值)

  不过其中的34都涉及到了树根节点的变换(这两个过程可能会修改树的根节点),放在结构体中不太合适,所以将其放在WeakBalanceTree中,剩余的放入结构体中。

  现在我们遇到了一个问题:如何快速判断树的重心?我们不可能每次判断重心的时候都把子树遍历一遍,这样子效率太低了,实际上我们直接比较左右子树的高度即可。

  同时,判断重心时有三种可能:偏左、偏右以及中心。为了用一个变量清晰地表示所有可能,我们选择再声明一个枚举类:

enum class BalanceDirection {
    LEFT, RIGHT, CENTER
}

  代码写起来就很简单了:

class Node<T : Comparable<T>>(value: T) : Comparable<T> {

    var value: T = value
        internal set
    var deep = 1
        internal set
    var left: Node<T>? = null
        internal set
    var right: Node<T>? = null
        internal set
    var father: Node<T>? = null
        internal set

    override fun compareTo(other: T) = value.compareTo(other)

    fun hasLeft() = left != null
    fun hasRight() = right != null
    fun notLeft() = !hasLeft()
    fun notRight() = !hasRight()
    
    fun getLeftDeep() = if (notLeft()) 0 else left!!.deep
    fun getRightDeep() = if (notRight()) 0 else right!!.deep

    /**
     * 把当前节点的其中一个子树替换为指定子树
     * @param src 要被替换的子树对象
     * @param dist 目的子树
     */
    internal fun swapSonTree(src: Node<T>, dist: Node<T>?) {
        if (src == left) left = dist
        else right = dist
    }

    /** 交换两个节点中的值 */
    internal fun swapNode(that: Node<T>) {
        value = that.value.apply { that.value = value }
    }

    /** 检查以该节点为根的树是否平衡 */
    fun checkBalance() = abs(getLeftDeep() - getRightDeep()) < 2

    /** 判断平衡向哪边偏移 */
    fun getBalance(): BalanceDirection {
        val dif = getLeftDeep() - getRightDeep()
        if (dif < 0) return RIGHT
        if (dif == 0) return CENTER
        return LEFT
    }

    /** 获取左子树中的最大值,如果不存在左子树则返回节点本身 */
    internal fun findLeftMax(): Node<T> {
        if (notLeft()) return this
        var result = left
        while (result!!.hasRight()) {
            result = result.right
        }
        return result
    }

    /** 从指定节点开始向上维护节点深度 */
    internal fun repairDeep() {
        var point: Node<T>? = this
        while (point != null) {
            point = point.apply {
                val newDeep = max(getLeftDeep(), getRightDeep()) + 1
                if (newDeep == deep) return
                deep = newDeep
                father
            }
        }
    }

}

工具函数

  在实现树的插入/移除功能之前我们要实现一些需要用到的工具函数:

  1. 维护平衡的函数
  2. 查找指定元素
  3. 旋转

前置

  前两个函数实现起来非常简单,我们直接给出代码:

class WeakBalanceTree<T : Comparable<T>> {

    /** 检查指定元素在树中是否存在 */
    fun contain(value: T) = find(value) != null

    /**
     * 在树中查找指定元素
     * @return 元素在树中的数据,不存在则返回nullptr
     */
    fun find(value: T): Node<T>? {
        var it = root
        while (it != null) {
            val cmp = it.compareTo(value)
            if (cmp < 0) it = it.right
            else if (cmp != 0) it = it.left
            else return it
        }
        return null
    }
    
    /** 以指定节点为根维护树的平衡 */
    private fun repairBalance(start: Node<T>) {
        if (!start.checkBalance()) {
            if (start.getBalance() == LEFT) rotateRight(start)
            else rotateLeft(start)
        }
    }
    
}

旋转

  现在来到了重点,旋转树地代码应该怎么写?如果你还记得我们前面说的 旋转的规律 ,那么实现起来就很简单:

class WeakBalanceTree<T : Comparable<T>> {
    
    /**
     * 以指定节点为根进行左旋
     * @throws IllegalArgumentException 如果右子树不存在
     */
    private fun rotateLeft(point: Node<T>) {
        if (point.notRight()) throw IllegalArgumentException("右子树不存在")
        val srcRight = point.right!!
        val srcLeftDeep = point.getLeftDeep()
        //如果右子树平衡偏左,先右旋
        if (srcRight.getBalance() == LEFT) rotateRight(srcRight)
        with (point) {
            //更新父亲节点的信息
            if (father != null) father!!.swapSonTree(this, srcRight)
            //将右子树的左子树设置为当前节点的左子树
            right = srcRight.left
            //将右子树的左子树设置为根
            srcRight.left = this
            //更新两个节点的父亲节点
            srcRight.father = father
            father = srcRight
            //维护深度
            deep = srcLeftDeep + 1
            srcRight.repairDeep()
            //必要时修改树的根节点
            if (this == root) root = srcRight
        }
    }

    /**
     * 以指定节点为根进行右旋
     * @throws IllegalArgumentException 如果左子树不存在
     */
    private fun rotateRight(point: Node<T>) {
        if (point.notLeft()) throw IllegalArgumentException("左子树不存在")
        val srcLeft = point.left!!
        val srcRightDeep = point.getRightDeep()
        if (srcLeft.getBalance() == RIGHT) rotateLeft(srcLeft)
        with (point) {
            if (father != null) father!!.swapSonTree(this, srcLeft)
            left = srcLeft.right
            srcLeft.right = this
            srcLeft.father = father
            father = srcLeft
            deep = srcRightDeep + 1
            srcLeft.repairDeep()
            if (this == root) root = srcLeft
        }
    }
    
}

编辑树

  有了上面的基础,那么我们实现插入和移除就很简单了:

class WeakBalanceTree<T : Comparable<T>> {

    /**
     * 向树中插入指定元素
     *
     * @return 插入的元素再树中的节点
     */
    fun insert(value: T): Node<T> {
        if (root == null) {
            root = Node(value)
            return root!!
        }
        var it = root
        val result: Node<T>
        while (true) {
            val cmp = it!!.compareTo(value)
            if (cmp < 0) {
                if (it.right == null) {
                    result = Node(value)
                    it.right = result
                    break
                } else it = it.right
            } else if (cmp != 0) {
                if (it.left == null) {
                    result = Node(value)
                    it.left = result
                    break
                } else it = it.left
            } else return it
        }
        result.father = it
        result.repairDeep()
        if (it!!.father != null) repairBalance(it.father!!)
        return result
    }

    /**
     * 从树中删除指定节点
     *
     * 注意:**函数内会修改传入的point的内容**
     */
    fun erase(point: Node<T>) {
        val max = point.findLeftMax()
        point.swapNode(max)
        max.father!!.swapSonTree(max, null)
        max.father!!.repairDeep()
        repairBalance(max.father!!)
    }

    /** 从树中删除指定节点 */
    fun erase(value: T): Boolean {
        val point = find(value) ?: return false
        erase(point)
        return true
    }
    
}

  至此,我们就成功的写出了一个弱平衡树,我们这里再贴出完整的代码:

import WeakBalanceTree.BalanceDirection.*
import kotlin.math.abs
import kotlin.math.max

/**
 * 弱平衡树
 * @author EmptyDreams
 */
class WeakBalanceTree<T : Comparable<T>> {

    /** 根节点 */
    private var root: Node<T>? = null

    /**
     * 向树中插入指定元素
     *
     * @return 插入的元素再树中的节点
     */
    fun insert(value: T): Node<T> {
        if (root == null) {
            root = Node(value)
            return root!!
        }
        var it = root
        val result: Node<T>
        while (true) {
            val cmp = it!!.compareTo(value)
            if (cmp < 0) {
                if (it.right == null) {
                    result = Node(value)
                    it.right = result
                    break
                } else it = it.right
            } else if (cmp != 0) {
                if (it.left == null) {
                    result = Node(value)
                    it.left = result
                    break
                } else it = it.left
            } else return it
        }
        result.father = it
        result.repairDeep()
        if (it!!.father != null) repairBalance(it.father!!)
        return result
    }

    /** 检查指定元素在树中是否存在 */
    fun contain(value: T) = find(value) != null

    /**
     * 在树中查找指定元素
     * @return 元素在树中的数据,不存在则返回nullptr
     */
    fun find(value: T): Node<T>? {
        var it = root;
        while (it != null) {
            val cmp = it.compareTo(value)
            if (cmp < 0) it = it.right
            else if (cmp != 0) it = it.left
            else return it
        }
        return null
    }

    /**
     * 从树中删除指定节点
     *
     * 注意:**函数内会修改传入的point的内容**
     */
    fun erase(point: Node<T>) {
        val max = point.findLeftMax()
        point.swapNode(max)
        max.father!!.swapSonTree(max, null)
        max.father!!.repairDeep()
        repairBalance(max.father!!)
    }

    /** 从树中删除指定节点 */
    fun erase(value: T): Boolean {
        val point = find(value) ?: return false
        erase(point)
        return true
    }

    /** 以指定节点为根维护树的平衡 */
    private fun repairBalance(start: Node<T>) {
        if (!start.checkBalance()) {
            if (start.getBalance() == LEFT) rotateRight(start)
            else rotateLeft(start)
        }
    }

    /**
     * 以指定节点为根进行左旋
     * @throws IllegalArgumentException 如果右子树不存在
     */
    private fun rotateLeft(point: Node<T>) {
        if (point.notRight()) throw IllegalArgumentException("右子树不存在")
        val srcRight = point.right!!
        val srcLeftDeep = point.getLeftDeep()
        //如果右子树平衡偏左,先右旋
        if (srcRight.getBalance() == LEFT) rotateRight(srcRight)
        with (point) {
            //更新父亲节点的信息
            if (father != null) father!!.swapSonTree(this, srcRight)
            //将右子树的左子树设置为当前节点的左子树
            right = srcRight.left
            //将右子树的左子树设置为根
            srcRight.left = this
            //更新两个节点的父亲节点
            srcRight.father = father
            father = srcRight
            //维护深度
            deep = srcLeftDeep + 1
            srcRight.repairDeep()
            //必要时修改树的根节点
            if (this == root) root = srcRight
        }
    }

    /**
     * 以指定节点为根进行右旋
     * @throws IllegalArgumentException 如果左子树不存在
     */
    private fun rotateRight(point: Node<T>) {
        if (point.notLeft()) throw IllegalArgumentException("左子树不存在")
        val srcLeft = point.left!!
        val srcRightDeep = point.getRightDeep()
        if (srcLeft.getBalance() == RIGHT) rotateLeft(srcLeft)
        with (point) {
            if (father != null) father!!.swapSonTree(this, srcLeft)
            left = srcLeft.right
            srcLeft.right = this
            srcLeft.father = father
            father = srcLeft
            deep = srcRightDeep + 1
            srcLeft.repairDeep()
            if (this == root) root = srcLeft
        }
    }

    /** 树节点 */
    class Node<T : Comparable<T>>(value: T) : Comparable<T> {

        var value: T = value
            internal set
        var deep = 1
            internal set
        var left: Node<T>? = null
            internal set
        var right: Node<T>? = null
            internal set
        var father: Node<T>? = null
            internal set

        override fun compareTo(other: T) = value.compareTo(other)

        fun hasLeft() = left != null
        fun hasRight() = right != null
        fun notLeft() = !hasLeft()
        fun notRight() = !hasRight()

        fun getLeftDeep() = if (notLeft()) 0 else left!!.deep
        fun getRightDeep() = if (notRight()) 0 else right!!.deep

        /**
         * 把当前节点的其中一个子树替换为指定子树
         * @param src 要被替换的子树对象
         * @param dist 目的子树
         */
        internal fun swapSonTree(src: Node<T>, dist: Node<T>?) {
            if (src == left) left = dist
            else right = dist
        }

        /** 交换两个节点中的值 */
        internal fun swapNode(that: Node<T>) {
            value = that.value.apply { that.value = value }
        }

        /** 检查以该节点为根的树是否平衡 */
        fun checkBalance() = abs(getLeftDeep() - getRightDeep()) < 2

        /** 判断平衡向哪边偏移 */
        fun getBalance(): BalanceDirection {
            val dif = getLeftDeep() - getRightDeep()
            if (dif < 0) return RIGHT
            if (dif == 0) return CENTER
            return LEFT
        }

        /** 获取左子树中的最大值,如果不存在左子树则返回节点本身 */
        internal fun findLeftMax(): Node<T> {
            if (notLeft()) return this
            var result = left
            while (result!!.hasRight()) {
                result = result.right
            }
            return result
        }

        /** 从指定节点开始向上维护节点深度 */
        internal fun repairDeep() {
            var point: Node<T>? = this
            while (point != null) {
                point = point.apply {
                    val newDeep = max(getLeftDeep(), getRightDeep()) + 1
                    if (newDeep == deep) return
                    deep = newDeep
                    father
                }
            }
        }

    }

    enum class BalanceDirection {
        LEFT, RIGHT, CENTER
    }

}

创作不易,去 源站支持一下我吧ヾ(≧▽≦*)o

举报

相关推荐

0 条评论