图片来自https://blog.csdn.net/isunbin/article/details/81703762
一、二叉排序树简介
二叉排序树,又叫二叉查找树(BST),它或者是一棵空树;
具有以下性质的二叉树:
- 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 它的左右子树也分别为二叉排序树;
二、二叉排序树类的定义
public class BinarySortTree {
// 二叉排序树根结点
private Node treeNode;
// 树结点
private static class Node {
private int data;
private Node leftChild;
private Node rightChild;
public Node() {
}
public Node(int data, Node leftChild, Node rightChild) {
this.data = data;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
public BinarySortTree() {}
}
三、二叉排序树的操作
注意: 在c语言中无论是递归插入结点还是删除结点都可以通过传入结点地址的方式来为该结点初始化这是引用传递,而在java中采用的值传递的方式,传入方法的是该对象的副本引用,如果需要为该对象实例化,或者改变当前对象的引用,就需要采用返回值的方式,通过父结点来设置当前结点的信息。 当然不使用递归就不会有这样的问题了。
递归调用前的代码想象成沿着树向下走:它会将给定的键和每个节点的键进行比较并根据结果向左或者向右移动到下一个结点。
递归调用后的代码想象成沿着树向上爬:对于get()方法,这对应着一系列的返回指令(return),但对于put()方法,这意味着重置搜索路径上每个父结点指向子结点的连接。
3.1 插入结点以及二叉排序树的创建
/**
* 创建二叉排序树
*/
public void creat(int[] arrays) {
for (int i = 0; i < arrays.length; i++) {
insert(arrays[i]);
}
}
/**
* 插入结点
*/
public void insert(int element) {
this.treeNode = insert(treeNode, element);
}
private Node insert(Node node, int element) {
if (node == null)
return new Node(element, null, null);
else if (node.data > element)
node.leftChild = insert(node.leftChild, element);
else
node.rightChild = insert(node.rightChild, element);
return node;
}
3.2 查找元素
从根节点一直往左走,直到无路可走就可得到最小值;从根节点一直往右走,直到无路可走,就可以得到最大值
3.2.1 查找最大元素
public int searchMaxElem() {
Node node = treeNode;
while (node.rightChild != null)
node = node.rightChild;
return node.data;
}
3.2.2 查找最小元素
public int searchMinElem() {
Node node = treeNode;
while (node.leftChild != null)
node = node.leftChild;
return node.data;
}
3.2.3 查找某个元素
递归版
private Node search_1(Node treeNode, int element) {
if (treeNode == null || treeNode.data == element)
return treeNode;
else if (treeNode.data < element)
return search_1(treeNode.leftChild, element);
else if (treeNode.data > element)
return search_1(treeNode.rightChild, element);
else
return null;
}
非递归版
private Node search_2(Node treeNode, int element) {
while (treeNode != null) {
if (treeNode.data == element)
return treeNode;
treeNode = (treeNode.data < element) ? treeNode.leftChild : treeNode.rightChild;
}
return null;
}
3.3 删除元素
删除该元素的结点可能存在四种情况:
- 删除结点为叶子结点
- 删除结点只有左子树
- 删除结点只有右子树
- 删除结点左右子树都有
这种情况较为复杂,我们采用先覆盖在删除的方法
第一步:查找删除结点右子树最小的结点,并将该结点的值赋值给删除结点
第二步:将右子树最小的结点(左子树最大结点)进行删除,由于符合前三种情况的一种,可进行删除
这样就可以间接删除既有左子树又有右子树的结点
/**
* 删除结点
*/
public void deleteBST(int element) {
treeNode = deleteBST(treeNode, element);
}
private Node deleteBST(Node node, int element) {
if (node == null)
return null;
// 当前的node为删除结点的父结点,删除结点将它的子树返回,父结点重新指向新的子树
if (node.data > element)
node.leftChild = deleteBST(node.leftChild, element);
else if (node.data < element)
node.rightChild = deleteBST(node.rightChild, element);
else {
if (node.rightChild == null)
return node.leftChild;
if (node.leftChild == null)
return node.rightChild;
// 如果该结点左右结点都有,那么找到右子树的最小结点
Node rTree = node.rightChild;
while (rTree.leftChild != null)
rTree = rTree.leftChild;
// 赋值为删除结点
node.data = rTree.data;
// 删除右子树的最小结点
node.rightChild = deleteBST(node.rightChild, rTree.data);
}
return node;
}