一、概念
任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值。
二、二叉排序树的创建
&&小技巧。二叉树有一个重要的算法就是递归,当要处理子树时一般都会用到,而能够方便的使用递归的一定又是节点。所以通常是在树里调用节点的方法,在节点里完成一些递归操作。
下面是构造二叉排序树的核心方法:
public void add(Node node) { //其实构造一颗树和构造链表很像,最终变为链表的构建
if(node == null) { //递归结束的条件
return;
} else {
//判断是比当前大还是小
//比当前值小,在左边
if(this.value > node.value) {
//判断是否有位置
if(this.leftNode != null) {
this.leftNode.add(node);
}
else {
this.leftNode = node;
}
//比当前值大,在右边
} else {
if(this.rightNode != null) {
this.rightNode.add(node);
} else {
this.rightNode = node;
}
}
}
}
三、二叉排序树中查找节点
public Node search(int i) {//可以想像二分查找使用递归
if(this.value == i) {
return this;
} else {
if(i < this.value) {
if(leftNode == null) {
return null;
}
return leftNode.search(i);
} else {
if(rightNode == null) {
return null;
}
return rightNode.search(i);
}
}
}
&&写这个查找函数的时候遇到了个经典的错误,虽然简单,但足够经典,当时我自作聪明的把代码简化为如下:
public Node search(int i) {
if(this.value == i) {
return this;
} else {
if(i < this.value) {
if(leftNode != null) {
leftNode.search(i);
}
} else {
if(rightNode != null) {
rightNode.search(i);
}
}
}
return null;
}
由于使用了递归,一定注意只能设置一个递归结束条件,反正是具有某种意义上的对称性的。
四、二叉排序树中删除节点
1.先找到需要删除的节点,命名为target,还需要父节点parent;(target==null记得return)
还需要searchParent(int value)寻找父节点的方法,因为二叉树类似单链表;
2.分类abc,每一种分类都要分为删除的目标节点是父节点的左子节点还是右子节点;
a.要删除的目标节点是叶子节点(没有左右儿子)
b.要删除的目标节点有一个左儿子和有一个右儿子
——找到后继节点(目标节点右子树的最小值)
——删除后继节点
——将后继节点的值赋给目标节点,相当于实现了删除节点的操作
定义一个deleteMin的方法,相当于输入子树的根节点,返回最小值并删除对应最小值的节点;
c.要删除的目标节点有一个左儿子或者有一个右儿子
——有一个左儿子
——有一个右儿子
代码实现:
public void delete(int value) {
//如果是空树,直接return
if(root == null) {
return;
} else {
//先找到需要删除的节点
Node target = root.search(value);
//如果没找到,则return
if(null == target) {
return;
}
//找到要删除的节点的父节点,因为删除其实就是使父节点不指向要删除的节点
Node parent = root.searchParent(value);
//删除将面临三种情况
//要删除的目标节点是叶子节点
if(null == target.leftNode && null == target.rightNode) {
//删除叶子节点是父节点的左叶子节点
if(parent.leftNode == target) {
parent.leftNode = null;
//删除叶子节点是父节点的右叶子节点
} else {
parent.rightNode = null;
}
//要删除的目标节点有一个左儿子和一个右儿子
} else if(target.leftNode != null && target.rightNode != null) {
//只需要找到目标节点的后继节点,删除后继节点,再把后继节点的value赋给target即可
//而后继节点就相当于target的右子树的最小值
//找到对应最小值,并且删除对应的节点
int min = deleteMin(target.rightNode);
//再把后继节点的value赋给target即可
target.value = min;
//要删除的目标节点有一个左儿子或者有一个右儿子
} else {
//要删除的目标节点有一个左儿子
if(target.leftNode != null) {
//要删除的目标节点是父节点左儿子
if(parent.leftNode == target) {
parent.leftNode = target.leftNode;
//要删除的目标节点是父节点右儿子
} else {
parent.rightNode = target.leftNode;
}
//要删除的目标节点有一个右儿子
} else {
//要删除的目标节点是父节点左儿子
if(parent.leftNode == target) {
parent.leftNode = target.rightNode;
//要删除的目标节点是父节点右儿子
} else {
parent.rightNode = target.rightNode;
}
}
}
}
}
下面来分段分析:
1.先找到需要删除的节点,命名为target,还需要父节点parent;(target==null记得return)
还需要searchParent(int value)寻找父节点的方法,因为二叉树类似单链表;
public Node searchParent(int value) {
if(null == root) {
return null;
} else {
return root.searchParent(value);
}
}
public Node searchParent(int value) {
if((this.leftNode != null && this.leftNode.value == value) || (this.rightNode != null && this.rightNode.value == value)){
return this;
} else {
if(value > this.value && this.rightNode != null) {
return this.rightNode.searchParent(value);
} else if(value < this.value && this.leftNode != null) {
return this.leftNode.searchParent(value);
}
return null;
}
}
2.分类abc,每一种分类都要分为删除的目标节点是父节点的左子节点还是右子节点;
a.要删除的目标节点是叶子节点(没有左右儿子)
if(null == target.leftNode && null == target.rightNode) {
//删除叶子节点是父节点的左叶子节点
if(parent.leftNode == target) {
parent.leftNode = null;
//删除叶子节点是父节点的右叶子节点
} else {
parent.rightNode = null;
}
b.要删除的目标节点有一个左儿子和有一个右儿子
——找到后继节点(目标节点右子树的最小值)
——删除后继节点
——将后继节点的值赋给目标节点,相当于实现了删除节点的操作
定义一个deleteMin的方法,相当于输入子树的根节点,返回最小值并删除对应最小值的节点;
//要删除的目标节点有一个左儿子和一个右儿子
} else if(target.leftNode != null && target.rightNode != null) {
//只需要找到目标节点的后继节点,删除后继节点,再把后继节点的value赋给target即可
//而后继节点就相当于target的右子树的最小值
//找到对应最小值,并且删除对应的节点
int min = deleteMin(target.rightNode);
//再把后继节点的value赋给target即可
target.value = min;
private int deleteMin(Node node) {
Node target = node;
//不断向左找最小值;
while(target.leftNode != null) {
target = target.leftNode;
}
//最小值可能有右子节点,也一定是右子节点,
//有左节点说明找到的并不是最小的
//但是delete都考虑这些情况
this.delete(target.value);
return target.value;
}
c.要删除的目标节点有一个左儿子或者有一个右儿子
——有一个左儿子
//要删除的目标节点有一个左儿子或者有一个右儿子
} else {
//要删除的目标节点有一个左儿子
if(target.leftNode != null) {
//要删除的目标节点是父节点左儿子
if(parent.leftNode == target) {
parent.leftNode = target.leftNode;
//要删除的目标节点是父节点右儿子
} else {
parent.rightNode = target.leftNode;
}
——有一个右儿子
//要删除的目标节点有一个右儿子
} else {
//要删除的目标节点是父节点左儿子
if(parent.leftNode == target) {
parent.leftNode = target.rightNode;
//要删除的目标节点是父节点右儿子
} else {
parent.rightNode = target.rightNode;
}
}