1. 什么是二叉搜索树?
二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST,又被称为:二叉查找树、二叉排序树。
• 任意一个节点的值都大于其左子树所有节点的值
• 任意一个节点的值都小于其右子树所有节点的值
• 它的左右子树也是一棵二叉搜索树
2. 如何构建二叉搜索树?
具体在到文件中看,放在了idea->JavaCode中
3. 如何比较元素?
比较重要的一些点,比较元素有多重方式,二叉搜索树默认存放的元素都是具有可比较性的,所以必须定义比较方法。
- 基本类型的比较,如果的Integer、String等类型的比较,直接比较即可
- Object类型的比较,例如Person类,而且有多个变量时。
第一种:二叉搜索树类的泛型 E extend Comparable,这样Person类就必须要实现接口的方法,实现具体的方法
第二种:定义一个Comparator comparator 对象,在二叉搜索树构造方法中添加Comparator comparator 参数,这样用户在 new BinarySearchTree 的时候,就可以自己new一个比较器来进行个性化比较
第三种:在第二种的基础上再人性化,用户可以自己定义比较器,也可以不定义,如果没有传递比较器,则强制转换为Comparable类来进行比较
4. 二叉树遍历
• 前序遍历 - 递归或迭代
• 中序遍历 - 递归或迭代 - 二叉搜索树的中序遍历可能是升序或降序的
• 后序遍历 - 递归或迭代
• 层序遍历 - 利用队列实现,取出对头进行访问,并将其左右子节点入列,然后继续对头出队,循环操作
5. 如何求二叉树的高度?
利用队列进行求解,levelSize为每一层的元素数量,每遍历一层,就将其左右子节点入队,而队列的个数就是该层结点的数量,然后每出队一个,levelSize–;
6. 判断是否是一棵完全二叉树?
public boolean isComplete() {
if (root == null) return false;
//队列
Queue<Node<E>> queue = new LinkedList<>();
queue.offer(root);
boolean leaf = false; //叶子结点标记
while (!queue.isEmpty()) {
Node<E> node = queue.poll();
if (leaf && !node.isLeaf()) return false; //判断是否是叶子结点
if (node.left != null) {
// 左子节点不为空则将左子节点入队
queue.offer(node.left);
} else if (node.right != null) { // node.left == null && node.right != null
return false;
}
if (node.right != null) {
// 右子节点不为空则将右子节点入队
queue.offer(node.right);
} else { // node.right == null
leaf = true;
}
}
return true;
}
7.前驱节点
private Node<E> predecessor(Node<E> node) {
if (node == null) return null;
// 前驱节点在左子树当中(left.right.right.right....)
Node<E> p = node.left;
if (p != null) {
while (p.right != null) {
p = p.right;
}
return p;
}
// 从父节点、祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.left) {
node = node.parent;
}
// node.parent == null
// node == node.parent.right
return node.parent;
}
8.后继结点
private Node<E> successor(Node<E> node) {
if (node == null) return null;
// 后驱节点在右子树当中(right.left.left.left....)
Node<E> p = node.right;
if (p != null) {
while (p.left != null) {
p = p.left;
}
return p;
}
// 从父节点、祖父节点中寻找后继节点
while (node.parent != null && node == node.parent.right) {
node = node.parent;
}
return node.parent;
}
9.删除节点
删除度为1的节点
删除度为1的节点时,只需要用子节点替代原节点的位置即可,分三种情况:
- 删除左子节点
- 删除右子节点
- 删除根节点
删除度为2的节点
删除度为2的节点,就需要找到被删除节点的前驱节点或者后继结点来替换。
private Node<E> node(E element) {
Node<E> node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
} else { // cmp < 0
node = node.left;
}
}
return null;
}
private void remove(Node<E> node) {
if (node == null) return;
size--;
if (node.hasTwoChildren()) { // 度为2的节点
// 找到后继节点
Node<E> s = successor(node);
// 用后继节点的值覆盖度为2的节点的值
node.element = s.element;
// 删除后继节点
node = s;
}
// 删除node节点(node的度必然是1或者0)
Node<E> replacement = node.left != null ? node.left : node.right;
if (replacement != null) { // node是度为1的节点
// 更改parent
replacement.parent = node.parent;
// 更改parent的left、right的指向
if (node.parent == null) { // node是度为1的节点并且是根节点
root = replacement;
} else if (node == node.parent.left) {
node.parent.left = replacement;
} else { // node == node.parent.right
node.parent.right = replacement;
}
} else if (node.parent == null) { // node是叶子节点并且是根节点
root = null;
} else { // node是叶子节点,但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { // node == node.parent.right
node.parent.right = null;
}
}
}