使用场景
-
用作系统中的多级索引,实现高效的查找、插入、删除操作。
-
作为某些搜索算法的底层数据结构。
-
用于存储数据流,以保持其有序状态。
特点
1.对于根节点满足:任意左子树节点<跟节点<任意右子树节点
2.对于任意节点也满足1
查找操作
和二分查找法的工作原理一致,循环次数最多为树的高度,当树是平衡的话,它的查找时间复杂度为O(logn)
1.让一个指针root指向根节点
2.让查找袁术和根节点比大小
3.若比根节点大在右子树找,若比根节点小则在左子树找
/* 查找节点 */
TreeNode search(int num) {
TreeNode cur = root;
// 循环查找,越过叶节点后跳出
while (cur != null) {
// 目标节点在 cur 的右子树中
if (cur.val < num)
cur = cur.right;
// 目标节点在 cur 的左子树中
else if (cur.val > num)
cur = cur.left;
// 找到目标节点,跳出循环
else
break;
}
// 返回目标节点
return cur;
}
插入操作
1.查询插入位置,从根节点出发,根据当前节点和插入num的大小判断在左右子树,直到越过叶子节点跳出循环,(查询到None)
2.在该位置插入新初始化节点
在这里我们用到pre来存储当前位置的父节点,方便初始化
当插入节点的值与当前节点的值相同时,直接返回,不进行插入操作(二叉搜索树不允许右相同值的节点出现)
时间复杂度O(logn)
/* 插入节点 */
void insert(int num) {
// 若树为空,则初始化根节点
if (root == null) {
root = new TreeNode(num);
return;
}
TreeNode cur = root, pre = null;
// 循环查找,越过叶节点后跳出
while (cur != null) {
// 找到重复节点,直接返回
if (cur.val == num)
return;
pre = cur;
// 插入位置在 cur 的右子树中
if (cur.val < num)
cur = cur.right;
// 插入位置在 cur 的左子树中
else
cur = cur.left;
}
// 插入节点
TreeNode node = new TreeNode(num);
if (pre.val < num)
pre.right = node;
else
pre.left = node;
}
删除操作
当要删除的节点的度为0的时候,我们可以直接将其删除,不影响二叉搜索树的性质
当要删除的节点的度为1的时候,说明有一个子节点,将其子节点代替该节点即可
当要删除的节点的度为2的时候,可以把它替换为左子树的最大值,也可以替换成右子树的最小值来实现左子树<该节点<右子树
因为中序遍历在遍历完该节点后要编列右子树最小节点,所以我们选择使用右子树最小节点替换
1.找到要删除节点的右子树最小节点tmp,也就是中序遍历的下一个节点
2用tmp覆盖要删除节点,递归删除节点tmp
代码实现
/* 删除节点 */
void remove(int num) {
// 若树为空,直接提前返回
if (root == null)
return;
TreeNode cur = root, pre = null;
// 循环查找,越过叶节点后跳出
while (cur != null) {
// 找到待删除节点,跳出循环
if (cur.val == num)
break;
pre = cur;
// 待删除节点在 cur 的右子树中
if (cur.val < num)
cur = cur.right;
// 待删除节点在 cur 的左子树中
else
cur = cur.left;
}
// 若无待删除节点,则直接返回
if (cur == null)
return;
// 子节点数量 = 0 or 1
if (cur.left == null || cur.right == null) {
// 当子节点数量 = 0 / 1 时, child = null / 该子节点
TreeNode child = cur.left != null ? cur.left : cur.right;
// 删除节点 cur
if (cur != root) {
if (pre.left == cur)
pre.left = child;
else
pre.right = child;
} else {
// 若删除节点为根节点,则重新指定根节点
root = child;
}
}
// 子节点数量 = 2
else {
// 获取中序遍历中 cur 的下一个节点
TreeNode tmp = cur.right;
while (tmp.left != null) {
tmp = tmp.left;
}
// 递归删除节点 tmp
remove(tmp.val);
// 用 tmp 覆盖 cur
cur.val = tmp.val;
}
}
中序遍历有序
利用二叉搜索树的性质左<中<右,以及中序遍历左-->中-->右的性质,在二叉搜索树中只用O(n)的时间复杂度就可以得到有序数据。