一. 节点的插入
节点的插入会破坏红黑树结构,所以我们要做变色和旋转操作,节点的变色和旋转是为了修正被破坏的红黑树, 使其符合红黑树的规则,从新达到平衡状态。红黑树的节点插入与二叉查找树的插入的过程是一样的,只是最后多了一步平衡调整操作,插入到红色节点下时才需要调整,因为插入到红色节点下违反了两个红色节点不能相邻规则。
二. 插入规则
插入节点平衡调整的几种情况如下:
-
情况一:新插入节点的父节点是红色,其父节点的兄弟节点也是红色时,通过变色操作调整平衡。
此时将父节点和父节点的兄弟节点(叔叔节点)变为黑色,将祖父节点(父节点的父节点)变为红色,接下来以祖父节点为基础继续向上验证修复,但最后根节点必须为黑色 -
情况二:新插入节点的父节点为红色,父节点的兄弟节点为黑色时
父节点、祖父节点和新节点3个节点在同一方向上(一条直线),如果为偏左的一条直线(如:和这个“/”方向一致),此时需要右旋(对祖父节点)。
如果为偏右的一条直线(如:和这个“\”方向一致),此时需要左旋。然后新节点的父节点变为黑色,其兄弟节点(原始的祖父节点)变为红色。 -
情况三:新插入节点的父节点为红色,父节点的兄弟节点为黑色时,且父节点、祖父节点和新节点3个节点不在同一方向上(不是一条直线)。
如果新节点是父节点的右子节点(此时,父节点在祖父节点的左侧,此时这三个节点构成这样“<”方向非直线),此时需要先左旋(变为一条直线)再右旋,两次旋转。
如果新节点是父节点的左子节点(此时,父节点在祖父节点的右侧,此时这三个节点构成这样“>”方向非直线),此时需要先右旋(变为一条直线)再左旋,两次旋转。
三. 代码实现如下
/// 在插入并修复
func insertAndFix(insertNode: Node?) {
var node = insertNode
/// 新插入节点颜色设置默认未红色
node?.color = .red
// 不是根节点且父节点为红色且不为空
while node != nil && node != root && node?.parent?.color == .red {
/// 父节点在祖父节点的左侧
if node?.parent == node?.parent?.parent?.left {
/// 叔叔节点(祖父节点的儿子)
let s = node?.parent?.parent?.right
/// 叔叔节点为红色
if s?.color == .red {
/// 父节点设置为黑色 叔叔节点设置好为黑色 祖父节点设置为红色
node?.parent?.color = .black
s?.color = .black
node?.parent?.parent?.color = .red
node = node?.parent?.parent
} else { /// 叔叔节点为黑色
/// 插入节点为右节点时候 形状如 《
if node == node?.parent?.right {
node = node?.parent
/// 左旋操作
leftROtate(node: node)
}
node?.parent?.color = .black
node?.parent?.parent?.color = .red
rightRotate(node: node?.parent?.parent)
}
} else { /// 对称的。 父节点在祖父节点右侧
let s = node?.parent?.parent?.left
/// 叔叔节点为红色
if s?.color == .red {
node?.parent?.color = .black
s?.color = .black
node?.parent?.parent?.color = .red
node = node?.parent?.parent
} else {
/// 插入节点为右节点时候 形状如 >
if node == node?.parent?.left {
node = node?.parent
/// 左旋操作
rightRotate(node: node)
}
node?.parent?.color = .black
node?.parent?.parent?.color = .red
leftROtate(node: node?.parent?.parent)
}
}
}
/// 根节点始终要为黑色
root?.color = .black
}