一、概念
二叉查找树也叫二叉搜索树(BST),是为了实现快 速查找而生的。不仅支持快速查找一个数据,还支持快速插入、删除一个数据。
二叉查找树要求,在树中的任意一个节点,其左子树中的每 个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。
平衡二叉查找树的高度接近 logn,所以插入、删除、查找操作的时间复杂度也比较稳定,是 O(logn)。
二、操作
1、创建
2、节点定义
节点有顺序,所以实现 Comparable 接口
csharp
public class BinaryNode<T> implements Comparable<T> {
/**
* 数据
*/
T data;
/**
* 左节点
*/
private BinaryNode left;
/***
* 右节点
*/
private BinaryNode right;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public BinaryNode getLeft() {
return left;
}
public void setLeft(BinaryNode left) {
this.left = left;
}
public BinaryNode getRight() {
return right;
}
public void setRight(BinaryNode right) {
this.right = right;
}
@Override
public int compareTo(T o) {
return this.compareTo(o);
}
}
2、树的定义
scala
public class BinarySearchTree<T extends Comparable> {
// 根节点
private BinaryNode<T> root;
// 树的节点数
private int size = 0;
public BinaryNode<T> getRoot() {
return root;
}
public int getSize() {
return size;
}
}
3、添加节点
- 从根节点开始,依次比较要插入的数据和节点的大小关系;
- 插入的数据比节点数值大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;不为空,就再递归遍历右子树,查找插入位置;
- 插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;不为空,就再递归遍历左子树,查找插入位置。
scss
/**
* 添加节点
* @param data
*/
public void insert(T data) {
BinaryNode<T> childNode = new BinaryNode<>();
childNode.setData(data);
// 根节点为空,直接作为根
if (root == null) {
root = childNode;
} else {
// 后续递归处理
insert(root,childNode);
}
}
/**
* 实际的插入
* @param pNode
* @param childNode
*/
private void insert(BinaryNode<T> pNode, BinaryNode<T> childNode) {
if (pNode == null) {
pNode = childNode;
}
T pData = pNode.getData();
T cData = childNode.getData();
int compare = pData.compareTo(cData);
// 父节点大于子节点
if (compare > 0) {
// 父节点的左节点为空
if (pNode.getLeft() == null) {
pNode.setLeft(childNode);
} else {
// 向下递归
insert(pNode.getLeft(),childNode);
}
} else if (compare < 0) {
if (pNode.getRight() == null) {
pNode.setRight(childNode);
} else {
insert(pNode.getRight(),childNode);
}
}
}
// 中序遍历进行测试
public List<T> midTraversal() {
ArrayList<T> ts = new ArrayList<>();
midTraversal(root,ts);
return ts;
}
private void midTraversal(BinaryNode<T> binaryNode,List<T> result) {
if (binaryNode != null) {
if (binaryNode.getLeft() != null) {
midTraversal(binaryNode.getLeft(),result);
}
result.add(binaryNode.getData());
if (binaryNode.getRight() != null) {
midTraversal(binaryNode.getRight(),result);
}
}
}
测试方法
ini
BinarySearchTree<Integer> binarySearchTree = new BinarySearchTree<>();
binarySearchTree.insert(6);
binarySearchTree.insert(4);
binarySearchTree.insert(8);
binarySearchTree.insert(5);
binarySearchTree.insert(3);
binarySearchTree.insert(7);
binarySearchTree.insert(9);
List<Integer> integers = binarySearchTree.midTraversal();
System.out.println(integers);
输出如下:
csharp
[3, 4, 5, 6, 7, 8, 9]
2、查找
查找一个节点
- 找根节点,等于就返回;
- 比根节点小,左子树中递归查找;
- 比根节点大,右子树中递归查找
kotlin
public Boolean find (T data) {
return find(data,root);
}
private Boolean find(T data, BinaryNode<T> node) {
while (node != null) {
T data1 = node.getData();
int compare = data1.compareTo(data);
// 父节点大
if (compare > 0) {
node = node.getLeft();
} else if (compare < 0) {
node = node.getRight();
} else {
return true;
}
}
return false;
}
3、删除
要删除节点的子节点个数的不同,分三种情况来处理。
- 要删除的节点没有子节点,只需要直接将父节点中,指向要删除节点的指 针置为 null。
- 要删除的节点只有一个子节点(只有左子节点或者右子节点),只需要更 新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
- 要删除的节点有两个子节点。我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上,然后再删除掉这个最小节点。
ini
public void del(T data) {
BinaryNode<T> node = root;
BinaryNode<T> parent = root;
while (node != null) {
T data1 = node.getData();
int compare = data1.compareTo(data);
// 父节点大
if (compare > 0) {
parent = node;
node = node.getLeft();
} else if (compare < 0) {
parent = node;
node = node.getRight();
} else {
// 找到了node节点,
break;
}
}
// 节点在树中不存在
if (node == null) {
return;
}
// 删除没有右节点的
if (node.getRight() == null) {
// 没有左节点
if (node.getLeft() == null) {
//删除父节点指向即可
if (parent.getLeft().equals(node)) {
parent.setLeft(null);
}
if (parent.getRight().equals(node)) {
parent.setRight(null);
}
return;
}
// 获取左边节点的最大节点
BinaryNode<T> maxLeft = node.getLeft();
BinaryNode<T> PMaxLeft = node.getLeft();
while (maxLeft.getRight() != null) {
PMaxLeft = maxLeft;
maxLeft = maxLeft.getRight();
}
// 删除这个节点
if (PMaxLeft.getLeft().equals(maxLeft)) {
PMaxLeft.setLeft(null);
}
if (PMaxLeft.getRight().equals(maxLeft)) {
PMaxLeft.setRight(null);
}
// 值替换
node.setData(maxLeft.getData());
} else {
// 获取右边节点最大值
BinaryNode<T> maxRight = node.getRight();
BinaryNode<T> PMaxRight = node.getRight();
while (maxRight.getLeft() != null) {
PMaxRight = maxRight;
maxRight = maxRight.getLeft();
}
// 删除这个节点,主要是不知道是那边
if (PMaxRight.getLeft().equals(maxRight)) {
PMaxRight.setLeft(null);
}
if (PMaxRight.getRight().equals(maxRight)) {
PMaxRight.setRight(null);
}
// 值替换
node.setData(maxRight.getData());
}
}
三、深度
ini
public int depth() {
return depth(root);
}
private int depth(BinaryNode<T> node) {
int dep1 = 0, dep2 = 0;
if (node == null) {
return 0;
}
dep1 = depth( node.getLeft());
dep2 = depth( node.getRight());
if ( dep1 > dep2 ) {
return dep1 + 1;
}
else {
return dep2 + 1;
}
}