目录
二叉排序树(Binary Sort Tree)
又称二叉查找数(Binary Search Tree),亦称二叉搜索树。
定义
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
-
若左子树不空,则左子树上所有结点的值均小于它的根节点的值;
-
若右子树不空,则右子树上所有结点的值均大于它的根节点的值;
-
左、右子树也分别为二叉排序树;
-
没有键值相等的节点。
二叉排序树的建立
先建立一个节点类
public class Node {
public int data;
public Node left;
public Node right;
public Node(int data) {
this.data = data;
}
public Node() {
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
思路:按照二叉排序树的性质,左子树的节点都小于它父节点的值,右子树的节点都大于它父节点的值。当需要插入一个节点时,假设树为空,则新建一颗树,假设树不为空,则按照二叉排序树的性质,找到当前值应该在这树那个位置。以下图二叉排序树为例,插入一个40的值,40小于根节点的50,走左孩子到20,40大于20,则去20的右孩子30,30小于40,则走30的右孩子,但右孩子为null,为30的右孩子为40。
public class BinarySearchTree {
private Node root;
public void add(int val) {
if (root == null) {
//当树为空时,新建一个根节点
root = new Node(val);
} else {
//当树不为空时
Node next = root;
//进行遍历二叉树,搜索当前值放那个位置
while (next != null) {
if (val > next.data) {
//如果next的右孩子节点为空,那么新增一个节点作为next的右孩子节点
//不为空则继续遍历
if (next.right == null) {
next.right = new Node(val);
break;
} else {
next = next.right;
}
} else {
if (next.left == null) {
next.left = new Node(val);
break;
} else {
next = next.left;
}
}
}
}
}
}
二叉排序树的遍历
根据二叉排序树的性质,按照树的中序遍历即可遍历出二叉排序树从小到大排序的结果。
public void show() {
show(root);
}
private void show(Node next) {
if (next.left != null) {
show(next.left);
}
System.out.print(next.data + " ");
if (next.right != null) {
show(next.right);
}
}
二叉排序树的查找
二叉排序树查找依靠它本身的性质,与二分查找类似,假设二叉树的节点数为n,树的高度为log2n + 1,则查找的效率为O(log2n),近似于二分查找,但最坏的情况为O(n)。
public boolean index(int val) {
if (root == null) {
return false;
} else {
Node next = root;
while (next != null) {
if (next.data == val) {
return true;
} else if (next.data > val) {
if (next.right == null) {
return false;
}
next = next.right;
} else {
if (next.left == null) {
return false;
}
next = next.left;
}
}
return false;
}
}
二叉树的最小值,最大值
二叉树的最小值为最左边的节点的值。二叉树的最小值为最右边的节点的值。
public Integer min() {
if (root == null) {
return null;
} else {
Node next = root;
while (next.left != null) {
next = next.left;
}
return next.data;
}
}
public Integer max() {
if (root == null) {
return null;
} else {
Node next = root;
while (next.right != null) {
next = next.right;
}
return next.data;
}
}
二叉树的删除
1.若删除的节点为叶子节点,直接删除,比如下图删除55这个值。
2.若删除的节点有一个孩子节点,则孩子节点替换被删除的节点,下图中,二叉排序树删除60这个值。
3.若删除的节点有2个孩子节点,则找右子树最小的节点替代被删除的节点,若删除50这个值,则找到他右孩子中最小的值55,替代该节点。
public Integer delete(int val) {
if (root == null) {
return -1;
} else {
Node next = root;
Node parent = null;
while (next != null) {
if (next.data > val) {
if (next.left != null) {
parent = next;
next = next.left;
} else {
//没找到该删除的值
return -1;
}
} else if (next.data == val) {
//没有孩子节点,被删除的节点的父节点直接删除该节点
if (next.left == null && next.right == null) {
if (next.data > parent.data) {
parent.right = null;
} else {
parent.left = null;
}
}
//有一个节点,则替换
else if (next.left == null && next.right != null) {
if (next.data > parent.data) {
parent.right = next.right;
} else {
parent.left = next.right;
}
} else if (next.left != null && next.right == null) {
if (next.data > parent.data) {
parent.right = next.left;
} else {
parent.left = next.left;
}
} else {
//找到右子树中最小的节点
Node temp = next.right;
Node tparent = next;
if (temp.left != null) {
while (temp.left != null) {
tparent = temp;
temp = temp.left;
}
tparent.left = null;
}
//该节点保留右子树最小的节点的右孩子
Node houxu = null;
if (temp.right != null) {
houxu = temp.right;
temp.right = null;
}
//被删除节点的左孩子
Node nextleft = null;
if (next.left != null) {
nextleft = next.left;
}
//被删除节点的右孩子
Node nextright = null;
if (next.right != null) {
nextright = next.right;
}
//被删除节点的父节点,替换被删除的节点
if (parent != null) {
if (next.data > parent.data) {
parent.right = temp;
} else {
parent.left = temp;
}
} else {
root = temp;
}
//右子树最小的节点的右孩子,作为右子树最小的节点的的父亲节点的左孩子
if (houxu != null) {
tparent.left = houxu;
}
//判断是不是右子树的最小值为 被删除节点的右孩子,是则不将被删除节点的右孩子作为替代节点的右孩子,不然形成了闭环
if (nextright != null && next.right.data != temp.data) {
temp.right = nextright;
} else {
temp.right = houxu;
}
//被删除节点的左孩子作为替代节点的左孩子
if (nextleft != null) {
temp.left = nextleft;
}
}
return val;
} else {
if (next.right != null) {
parent = next;
next = next.right;
} else {
return -1;
}
}
}
}
return -1;
}
总结:
二叉查找树类似二分查找,查找速度快,但避免出现树不平衡的情况,树完全不平衡的情况下,查找效率变成线性查找,效率较低。优化考虑,避免二叉排序树出现不平衡的状态,所以考虑红黑树,以及平衡树。