二叉查找树:
二叉查找树(Binary Search Tree),(又二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
例子:
结点的删除操作:
分三种情况讨论:
结点为叶子结点时,直接删除,把父节点指向子节点的引用设为null
结点的度为1,分两种情况,判断是父节点的左子结点还是右子节点,把父节点的引用指向结点的子节点(子节点也要分左右子节点情况,相当于一共四种情况)
结点的度为2,由于二叉搜索树的特性,保证了某个结点的左子树的值都小于该节点,右子树的值都大于该节点,只需找到左子树中的最大值或者右子树中的最小值来替换该结点,即可保证节点删除后任为二叉搜索树。(下面代码中采用左子树的最大值代替结点)
代码实现:
/*
删除
先找到元素,再删除
分三种情况,删除的节点为叶子节点,此时直接删除,修改父节点的引用
度为1的节点,删除后修改父节点的引用,指向子节点
度为2的节点,删除后把左边最大的节点或者右边最小的节点替换到该节点
*/
public static boolean delete(int x, TreeNodeInt T) {
TreeNodeInt current = T;//需要删除的节点
TreeNodeInt parent = null;//需要删除的节点的父节点
boolean isLeftChild = true;//需要删除的节点是否为父节点的左子节点
//通过该循环来找到要删除节点的位置和他的父节点
while (true) {
if (x == current.value) {
break;
} else if (x < current.value) {
//删除的值小于当前节点
//查看左子树
isLeftChild = true;
parent = current;
current = current.lchild;
} else {
//删除的值大于当前节点
//查看右子树
isLeftChild = false;
parent = current;
current = current.rchild;
}
//找不到要删除的节点,直接返回null
if (current == null) {
return false;
}
}
//分情况考虑
//1.需要删除的节点为叶子节点
if (current.lchild == null && current.rchild == null) {
//如果该节点为根节点,把根节点设置为null
if (current == T) {
T = null;
} else {
//如果该叶节点时父节点的左子节点,将父节点的左子节点设置为null
if (isLeftChild) {
parent.lchild = null;
} else {
//如果该叶节点时父节点的右子节点,将父节点的右子节点设置为null
parent.rchild = null;
}
}
}
//2.需要删除的节点为度为1的节点,且该子节点为左子节点
else if (current.rchild == null) {
//如果该节点为根节点,将根节点的左子节点变为根节点
if (current == T) {
T = current.lchild;
} else {
//如果该节点是父节点的左子节点,则将该节点的左子节点变为父节点的左子节点
if (isLeftChild) {
parent.lchild = current.lchild;
}
//如果该节点是父节点的右子节点,则将该节点的左子节点变为父节点的右子节点
else {
parent.rchild = current.lchild;
}
}
}
//2.需要删除的节点为度为1的节点,且该子节点为右子节点
else if (current.lchild == null) {
//如果该节点为根节点,将根节点的右子节点变为根节点
if (current == T) {
T = current.rchild;
} else {
//如果该节点是父节点的左子节点,则将该节点的右子节点变为父节点的左子节点
if (isLeftChild) {
parent.lchild = current.rchild;
}
//如果该节点是父节点的右子节点,则将该节点的右子节点变为父节点的右子节点
else {
parent.rchild = current.rchild;
}
}
}
//3.需要删除的节点有两个子节点,需要寻找该节点的后续节点代替删除节点
//即用左子树的最大值或者右子树的最小值代替该节点,通知删除左子树的最大值或者右子树的最小值
else {
//得到当前位置左边最小值代替他并删除左边最小值结点
//由于此处的current为定位到要删除的位置,所以又写了一个函数来找到当前位置的继承人
getSuccessor(current);
}
return true;
}
/*
找到一个节点的继承人,即左边最大或右边最小,并删除继承人原来的位置
*/
public static void getSuccessor(TreeNodeInt T){
TreeNodeInt targetParent=T;
TreeNodeInt targetNode=T.lchild;
while (targetNode.rchild!=null){
targetParent=targetNode;
targetNode=targetNode.rchild;
isLeft=false;
}
if (isLeft){
targetParent.lchild=targetNode.lchild;
T.value=targetNode.value;
targetNode=null;
}else {
//当左边最大值有左结点时
if (targetNode.lchild!=null){
targetParent.rchild=targetNode.lchild;
T.value=targetNode.value;
targetNode=null;
}else {
//左边最大值为没有左结点时
targetParent.rchild=null;
T.value=targetNode.value;
targetNode=null;
}
}
}
总结:
对于二叉树,当操作涉及到左右子树时,很多情况需要判断是左子树还是右子树,需要进行分类