基本树节点和二叉查找树定义
public class BinarySearchTree {
private TreeNode root;
//节点
static class TreeNode {
private int val;
private TreeNode left;
private TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
}
添加节点,对于可重复Node的添加操作,遇到相同节点继续插入到右子树中,如图
示例Java代码
public void add(int val) {
TreeNode newNode = new TreeNode(val);
if (root == null) { //空树
root = newNode;
return;
}
TreeNode cur = root;
for (; ; ) { //循环遍历
if (cur.val > val) { //小于当前节点值则走左子树
if (cur.left == null) { //找到添加位置
cur.left = newNode;
return;
}
cur = cur.left;
} else {
//相同重复值继续在右子树中进行添加
if (cur.right == null) {
cur.right = newNode;
return;
}
cur = cur.right;
}
}
}
核心在于将重复值也归于右子树
查询节点 ,直接遍历递归查询即可,将所有可能出现的值存在容器中返回即可
public List<TreeNode> find(int val) {
if(root == null) return Collections.emptyList();
List<TreeNode> result = new ArrayList<>();
TreeNode cur = root;
while(cur!=null) {
if(val>=cur.val) { //右子树查询
if(val==cur.val) { //找到相同值则添加到result结果集中
result.add(cur);
}
cur = cur.right; //继续遍历
}else {
cur = cur.left;
}
}
return result;
}
删除节点,分三种情况
- 要删除的节点是叶子节点,则直接将父节点对其指向指向null即可
- 删除的节点有一个子节点(左右节点都可), 则将父节点对删除节点的指向指向该子节点即可
- 删除节点有两个子节点 , 则将要删除的节点值和其右子树的最小节点值进行交互,然后将当前的删除节点转移到其右子树的最小节点进行上面两种情况的删除步骤即可
对于多个重复值的删除,通过查询节点,查询到个数,然后逐个删除即可
具体删除逻辑如下:
//删除所有值相同的节点
public void removeAllNode(int val) {
List<TreeNode> allNode = findAllNode(val); //查询该值的所有节点
if (allNode.size() == 0) return;
IntStream.rangeClosed(1, allNode.size()).forEach(i -> remove(val)); //逐次删除
}
public void remove(int val) {
if (root == null) return;
//查询要删除的节点并记录父节点位置
TreeNode p = root;
TreeNode pp = null;
while (p != null && p.val!=val) {
pp = p;
if (val > p.val) {
p = p.right;
} else if (val < p.val) {
p = p.left;
}
}
if (p == null) return;
//删除两个节点
if (p.left != null && p.right != null) {
//获取右子树中的最小节点
TreeNode minP = p.right;
TreeNode minPP = p;
while (minP.left != null) {
minP = minP.left;
minPP = minP;
}
p.val = minP.val;
//这一步的结果是,交换了需要删除节点和右子树最小节点之后只需要删除最小节点即可
//于是乎将 minPP minP -> pp p执行删除只有一个子节点或者没有子节点的逻辑
pp = minPP;
p = minP;
}
//处理删除有一个子节点和0个子节点,这一步主要是记录要指向的新节点
TreeNode tmp;
if (p.left != null && p.right == null) {
tmp = p.left;
} else if (p.left == null && p.right != null) {
tmp = p.right;
} else {
tmp = null;
}
//根据之前的 pp -> p的指向方向的不同,设置新节点在left还是right子节点上
if (pp == null) {
root = p;
} else if (pp.left == p) {
pp.left = tmp;
} else {
pp.right = tmp;
}
}