删除一个节点
前面博客只写到添加节点,接下来利用上一篇的博客前驱与后继,写下删除节点
删除主要包括
- 删除的节点度为0
- 删除的节点度为1
- 删除的节点度为2
1.叶子节点:直接删除
2.度为1的节点:用子节点替代删除的节点
3.度为2的节点:用前驱或后继节点取代删除的节点,然后删除这个前驱或后继的节点
- 如果一个节点的度为2,那么它的前驱或者后继节点的度只能为0或1
4.代码描述
- 1.度为2的节点,删除前驱或后继,即找到那个节点更新删除的节点node为前驱或后继即可
- 2.传入参数是需要删除的节点元素,需要封装通过元素返回节点对象的方法
/**
* 1.如果node.left != null || node.right != null,说明 node 是度为1的节点
* 1.1需要更新孩子节点的父节点parent为删除节点的父节点
* 用replacement保存是左节点还是右节点 node.left != null ? node.left : node.right;
* 1.2父节点为空,说明为根节点,更新root为replacement
* 1.3父节点不为空,判断需要删除的节点node为左子树还是右子树
* 1.3.1需要删除的节点为其父节点的左子树,则其父节点的左子树置为删除节点的孩子节点replacement node == node.parent.left
* 1.3.2需要删除的节点为其父节点的右子树,则其父节点的右子树置为删除节点的孩子节点replacement node == node.parent.right
* 2.否则说明 node 是度为0的节点
* 2.1为根节点,则root置为空
* 2.2为叶子节点,判断是左还是右
* 2.2.1需要删除的节点为为其父节点的左子树,则其父节点的左子树置为空 node == node.parent.left
* 2.2.2需要删除的节点为为其父节点的右子树,则其父节点的右子树置为空 node == node.parent.right
*/
public void remove(E element) {
remove(node(element));
}
private Node<E> node(E element) {
Node<E> node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
} else { // cmp < 0
node = node.left;
}
}
return null;
}
private void removeMy(Node<E> node){
if(node == null)
return;
size--;
// 度为2的节点
if(node.hasTwoChildren()){
// 找到后继节点
Node<E> s = successor(node);
// 用后继节点的值覆盖度为2的节点的值
node.element = s.element;
// 删除后继节点
node = s;
}
// 度为1的节点
if(node.left != null || node.right != null){
Node<E> replacement = node.left != null ? node.left : node.right;
replacement.parent = node.parent;
if(node.parent == null){
root = replacement;
} else if (node == node.parent.left){
node.parent.left = replacement;
} else if (node == node.parent.right){
node.parent.right = replacement;
}
} else { //度为0的节点
if (node.parent == null){
root = null;
} else if (node == node.parent.left){
node.parent.left = null;
} else if (node == node.parent.right){
node.parent.right = null;
}
}
}
总结:度为0的时候,删除节点需要考虑删除的节点是否为根节点,
度为1的时候,删除节点需要考虑删除的节点的父节点是否为根节点,