点击前往博客阅读获得更优体验
为了美观,CSDN这里只给出
Kotlin
的实现,
C++
版本的代码可以点击上面的链接到我的博客里面查看
文章目录
在之前的博文中我们讨论了 二叉搜索树 与 平衡二叉搜索树 的理论实现,这次让我们用Kotlin
来实现一个弱平衡树。
{% p blue center, 注意:以下代码实现的弱平衡树不是任何网上的弱平衡树(比如:红黑树、Splay等),是我完全按照前面的理论写的 %}
结构设计
首先,我们肯定需要先设计一下代码结构。
- 需要一个类表示
树
- 需要一个类(结构体)表示
节点
这么看来,我们至少需要两个类:
class WeakBalanceTree<T : Comparable<T>> {
class Node<T : Comparable<T>>: Comparable<T>
}
Node设计
现在,我们再来看Node
中需要记录些什么信息:
- 节点的
value
- 与当前节点相连的其它三个节点
- 节点深度
那么,我们很容易就能写出下面的实现:
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
}
那么,一个节点需要干什么活呢?我们先假设它需要负责这些功能:
- 检查以该节点为根的树是否平衡
- 判断以该节点为根的树的重心
- 旋转树
- 维护树的平衡
- 维护树的深度
- 查找左子树最大值(或右子树最小值)
不过其中的3
和4
都涉及到了树根节点的变换(这两个过程可能会修改树的根节点),放在结构体中不太合适,所以将其放在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
}
}
}
}
工具函数
在实现树的插入/移除功能之前我们要实现一些需要用到的工具函数:
- 维护平衡的函数
- 查找指定元素
- 旋转
前置
前两个函数实现起来非常简单,我们直接给出代码:
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