package binarysorttree;
/**
*二叉排序树:BST: (Binary Sort(Search) Tree), 对于二叉排序树的任何一个非叶子节点,
* 要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
* 特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点(当前代码是把相同的值放入到右子结点)
*
* 二叉排序树的删除
* 第一种情况:
* 删除叶子节点 (比如:2, 5, 9, 12)
* 思路
* (1) 需求先去找到要删除的结点 targetNode
* (2) 找到targetNode 的 父结点 parent
* (3) 确定 targetNode 是 parent的左子结点 还是右子结点
* (4) 根据前面的情况来对应删除
* 左子结点 parent.left = null
* 右子结点 parent.right = null;
*
* 第二种情况: 删除只有一颗子树的节点 比如 1
* 思路
* (1) 需求先去找到要删除的结点 targetNode
* (2) 找到targetNode 的 父结点 parent
* (3) 确定targetNode 的子结点是左子结点还是右子结点
* (4) targetNode 是 parent 的左子结点还是右子结点
* (5) 如果targetNode 有左子结点
* 5. 1 如果 targetNode 是 parent 的左子结点
* parent.left = targetNode.left;
* 5.2 如果 targetNode 是 parent 的右子结点
* parent.right = targetNode.left;
* (6) 如果targetNode 有右子结点
* 6.1 如果 targetNode 是 parent 的左子结点
* parent.left = targetNode.right;
* 6.2 如果 targetNode 是 parent 的右子结点
* parent.right = targetNode.right
*
* 情况三 : 删除有两颗子树的节点. (比如:7, 3,10 )
* 思路
* (1) 需求先去找到要删除的结点 targetNode
* (2) 找到targetNode 的 父结点 parent
* (3) 从targetNode 的右子树找到最小的结点
* (4) 用一个临时变量,将 最小结点的值保存 temp = 11
* (5) 删除该最小结点
* (6) targetNode.value = temp
*
*
* 疑惑解析
* (1)待删除的结点 无子树 和 有两个子树容易判断,那有一个子树的情况如何判断?
* 使用if(无子树){}
* else if(有两个子树){}
* else(有一个子树){}
* 在处理其他情况下,可以找容易判断的情况,然后else(对立的情况){} 巧妙解决不容易判断的情况
* (2)return this.left.searchParent(value);
* this.left 相当于指向当前节点左子节点 相当于 node = node.left
* 写错的点 // searchParent( value);默认调用对象是this,所以这里并没有指向下一个节点,一直在当前节点死循环,造成栈溢出
* root.add(node);//add(node); 栈溢出错误
*
* //return (返回)结束该层递归
*
* (3)为什么在处理第三种情况的时候parentNode.left != null可以不用判断??????????
* 晓得了
* 如果待删除的节点有一个子树,parentNode的left和right指针域一定不会为空
* 比如待删除的节点有一个子树为第一层,待删除的节点是第二层,parentNode则是第三层,根据图像来看一般不会为空
* 除非特殊的情况 这里最好判断一下
* (4)在第三种情况下需要判断parentNode节点是否为空
* 比如一颗树中,只有两个节点,再删除根结点的时候,parentNode.left.value会报错,空指针异常
* 因为根节点没有parentNode,则剩下的那个节点直接为根结点
*
* (5)需要注意的是数值最好不要有重复,如果退化为线性链表,则(3)肯定需要判断一下
* 后面会有平衡二叉树解决这个问题
*
* */
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
//循环的添加结点到二叉排序树
for(int i = 0; i< arr.length; i++) {
binarySortTree.add(new Node(arr[i]));
}
//中序遍历二叉排序树
System.out.println("中序遍历二叉排序树~");
binarySortTree.infixOrder(); // 1, 3, 5, 7, 9, 10, 12
Node target = binarySortTree.search(10);
System.out.println("找到"+target);
Node parentNode = binarySortTree.searchParent(10);
System.out.println("当前节点的父节点是"+parentNode);
//binarySortTree.delNode(12);//叶子节点没有问题
// binarySortTree.delNode(3);//待删除结点有两个子节点 没有问题
// binarySortTree.delNode(7);//删除根节点出现问题
//binarySortTree.delNode(1);
binarySortTree.delNode(12);
binarySortTree.delNode(5);
binarySortTree.delNode(10);
binarySortTree.delNode(2);
binarySortTree.delNode(3);
binarySortTree.delNode(9);
binarySortTree.delNode(7);
binarySortTree.delNode(1);
System.out.println("中序遍历二叉排序树~");
System.out.println(binarySortTree.getRoot());
binarySortTree.infixOrder(); // 1, 3, 5, 7, 9, 10, 12
}
}
class BinarySortTree {
private Node root;
public Node getRoot() {
return root;
}
//查找要删除的结点
public Node search(int value) {
if (root == null) {
return null;
} else {
return root.search(value);
}
}
//查找父结点
public Node searchParent(int value) {
if (root == null) {
return null;
} else {
return root.searchParent(value);
}
}
//添加结点的方法
public void add(Node node) {
if (root == null) {
root = node;
} else {
root.add(node);//add(node); 栈溢出错误
}
}
//中序遍历
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉排序树为空,不能遍历");
}
}
//编写方法:
//1. 返回的 以node 为根结点的二叉排序树的最小结点的值
//2. 删除node 为根结点的二叉排序树的最小结点
/**
* @param node 传入的结点(当做二叉排序树的根结点)
* @return 返回的 以node 为根结点的二叉排序树的最小结点的值
*/
public int delRightTreeMin(Node node) {
Node targetNode = node;
//循环的查找左子节点,就会找到最小值
while (targetNode.left != null) {
targetNode = targetNode.left;
}
int minVal = targetNode.value;
delNode(minVal);
//这时 target就指向了最小结点
//删除最小结点
return minVal;
/* delNode(targetNode.value);
return targetNode.value;*/
}
//删除结点
//bug 括号括错了
public void delNode(int value) {
if (root == null) {
return;
} else {
//1.需求先去找到要删除的结点 targetNode
Node targetNode = search(value);
如果没有找到要删除的结点
if (targetNode == null) {
return;
}
//如果我们发现当前这颗二叉排序树只有一个结点
if (root.left == null && root.right == null) {
root = null;
return;//结束
}
//去找到targetNode的父结点
Node parentNode = searchParent(value);
//如果要删除的结点是叶子结点
if (targetNode.left == null && targetNode.right == null) {
//if (parentNode != null) {//待删除的节点是叶子节点,说明肯定有parentNode节点
//判断targetNode 是父结点的左子结点,还是右子结点
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == targetNode.value) {
parentNode.right = null;
}
//}
//删除有两颗子树的节点
} else if (targetNode.left != null && targetNode.right != null) {
//选择右子树中最小的值替换待删除结点
int minVal = delRightTreeMin(targetNode.right);//int minVal = delRightTreeMin(targetNode);
targetNode.value = minVal;
}
// 删除只有一颗子树的结点
else {
//待删除的结点有左子节点
if (targetNode.left != null) {
if (parentNode != null) {
//如果 targetNode 是 parentNode 的左子结点
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.left;
} else { // targetNode 是 parentNode 的右子结点
parentNode.right = targetNode.left;
}
}else{
root = targetNode.left;//待删除的的节点为根结点,且只有一个子树,没有parentNode节点
}
}
//如果要删除的结点有右子结点
if (targetNode.right != null) {
if (parentNode != null) {
if (parentNode.left != null && parentNode.left.value == targetNode.value) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
}
}
}
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
public Node search(int value) {
if (value == this.value) {
return this;
} else if (value < this.value) {
if (this.left != null) {
return this.left.search(value);
} else {
return null;
}
} else {
if (this.right != null) {
return this.right.search(value);// this.right.search(value);记得要返回
} else {
return null;
}
}
}
//查找要删除结点的父结点
/**
* @param value 要找到的结点的值
* @return 返回的是要删除的结点的父结点,如果没有就返回null
*/
public Node searchParent(int value) {
if (this.left != null && this.left.value == value ||
this.right != null && this.right.value == value) {
return this;
} else if (value < this.value && this.left != null) {
return this.left.searchParent(value);
// searchParent( value);默认调用对象是this,所以这里并没有指向下一个节点,一直在当前节点死循环
//return (返回)结束该层递归
} else if (value >= this.value && this.right != null) {
return this.right.searchParent(value);
} else {
return null;
}
}
//添加结点的方法
//递归的形式添加结点,注意需要满足二叉排序树的要求
public void add(Node node) {
if (node == null) {//??????????? 为什么要判断这个
return;
}
//node值小于当前节点的值
if (node.value < this.value) {
if (this.left != null) {
this.left.add(node);
} else {
this.left = node;
}
} else {// 大于等于
if (this.right != null) {
this.right.add(node);
} else {
this.right = node;
}
}
}
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}