package com.zzk.data;
/**
* 二叉搜索树(BST)
*/
public class BinarySearchTree<T extends Comparable> {
Node root;
public BinarySearchTree() {
this.root = null;
}
static class Node<T extends Comparable> {
public T key;
public Node<T> left;
public Node<T> right;
public Node<T> parent;
public Node(T key, Node left, Node right, Node parent) {
this.key = key;
this.left = left;
this.right = right;
this.parent = parent;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
'}';
}
}
/**
* 插入到树中
*/
public void insert(T key) {
if (null == root) {
root = new Node(key, null, null, null);
} else {
insert(root, key);
}
}
private void insert(Node node, T key) {
Node<T> targetNode = new Node<>(key, null, null, node);
if (key.compareTo(node.key) < 0) {
if (null == node.left) {
node.left = targetNode;
} else {
insert(node.left, key);
}
} else {
if (null == node.right) {
node.right = targetNode;
} else {
insert(node.right, key);
}
}
}
/**
* 前序遍历
*/
public void preOrder() {
preOrder(root);
}
private void preOrder(Node node) {
if (null == node) {
return;
}
System.out.print(node.key);
if (null != node.left) {
preOrder(node.left);
}
if (null != node.right) {
preOrder(node.right);
}
}
/**
* 中序遍历
*/
public void inOrder() {
inOrder(root);
}
private void inOrder(Node node) {
if (null == node) {
return;
}
if (null != node.left) {
inOrder(node.left);
}
System.out.print(node.key);
if (null != node.right) {
inOrder(node.right);
}
}
/**
* 后序遍历
*/
public void postOrder() {
postOrder(root);
}
private void postOrder(Node node) {
if (null == node) {
return;
}
if (null != node.left) {
postOrder(node.left);
}
if (null != node.right) {
postOrder(node.right);
}
System.out.print(node.key);
}
/**
* 查找
*/
public Node<T> search(T key) {
return search(root, key);
}
private Node<T> search(Node<T> node, T key) {
//没找到
if (null == node) {
return null;
}
if (node.key.compareTo(key) == 0) {
return node;
}
if (node.key.compareTo(key) > 0) {
return search(node.left, key);
} else {
return search(node.right, key);
}
}
/**
* 非递归查找
*/
public Node<T> search2(T key) {
if (null == root) {
return null;
}
Node cur = root;
while (cur != null) {
if (cur.key.compareTo(key) == 0) {
return cur;
} else if (cur.key.compareTo(key) > 0) {
cur = cur.left;
} else {
cur = cur.right;
}
}
return null;
}
/**
* 获取最大的节点,主要为了删除节点功能中 可以获取 前驱节点和后驱节点做准备
* (目标节点的 前驱节点 : 是左子树的最大节点, 也是中序遍历中 "目标节点"的 前一个节点)
* (目标节点的 后驱节点 : 是右子树的最小节点, 也是中序遍历中 "目标节点"的 后一个节点)
*/
public Node maxNode() {
return maxNode(root);
}
private Node maxNode(Node node) {
if (node == null) {
return null;
}
Node cur = node;
while (cur != null) {
if (cur.right != null) {
cur = cur.right;
} else {
return cur;
}
}
return null;
}
public Node minNode() {
return minNode(root);
}
private Node minNode(Node node) {
if (node == null) {
return null;
}
Node cur = node;
while (cur != null) {
if (cur.left != null) {
cur = cur.left;
} else {
return cur;
}
}
return null;
}
/**
* 查找一个节点的前驱节点:
* 1. 如果查找节点有左子树,那么他的前驱节点就是左子树的最大节点
* 2. 如果查找节点没有左子树,那么分两种情况
* a: 查找节点是右节点,那么查找节点的父节点就是前驱节点
* b: 查找节点是左节点,那么顺着查找节点往上查, 如果 "被查节点" 是 "被查节点父节点" 的 右孩子,那么 "被查节点的父节点" 就是前驱
*/
private Node pre(Node node) {
if (node.left != null) {
return maxNode(node.left);
} else {
if (node.parent == null) {
return null;
}
if (node.parent.right == node) {
return node.parent;
} else {
Node p = node.parent;
while (p != null && p.left == node) {
node = p;
p = p.parent;
}
return p;
}
}
}
/**
* 查找一个几点的后驱节点:
* 1. 如果查找节点有右子树,那么他的前驱节点就是右子树的最小节点
* 2. 如果查找节点没有右子树,那么分两种情况
* a: 查找节点是左节点,那么查找节点的父节点就是后驱节点
* b: 查找节点是右节点,那么顺着查找节点往上查, 如果 "被查节点" 是 "被查节点父节点" 的 左孩子,那么 "被查节点的父节点" 就是后驱
*/
private Node after(Node node) {
if (node.right != null) {
return minNode(node.right);
} else {
if (node.parent == null) {
return null;
}
if (node.parent.left == node) {
return node.parent;
} else {
Node p = node.parent;
while (p != null && p.right == node) {
node = p;
p = p.parent;
}
return p;
}
}
}
/**
* 删除节点 (不考虑根节点的删除)
*
* 删除代码未优化,只根据规则进行对应实现.
*
* 1. 被删除节点是叶子节点, 直接将该节点的父节点的 "左节点或右节点" 置为null
* 2. 被删除节点只有左子树,或者只有右子树,那么将孙节点直接交给祖父节点, 然后将被删除节点置为null
* 3. 被删除节点既有左子树, 又有右子树, 那么选择将左子树的 "前驱节点" 的值 赋值给待删除节点, 然后 根据第2点,删除节点即可
*
* 具体的规则可参考 https://www.bilibili.com/video/BV1sv411j7hZ
*/
public void remove(T key) {
Node node = remove(root, key);
if (node != null) {
node = null;
}
}
private Node remove(Node node, T key) {
Node<T> deleteTargetNode = search2(key);
//不存在要删除的节点
if (null == deleteTargetNode) {
return null;
}
//是叶子节点
if (deleteTargetNode.left == null && deleteTargetNode.right == null) {
if (deleteTargetNode.parent.left == deleteTargetNode) {
deleteTargetNode.parent.left = null;
}
if (deleteTargetNode.parent.right == deleteTargetNode) {
deleteTargetNode.parent.right = null;
}
return deleteTargetNode;
}
//要被刪除的节点只有左子树
if (deleteTargetNode.left != null && deleteTargetNode.right == null) {
if (deleteTargetNode.parent.left == deleteTargetNode) {
deleteTargetNode.parent.left = deleteTargetNode.left;
}
if (deleteTargetNode.parent.right == deleteTargetNode) {
deleteTargetNode.parent.right = deleteTargetNode.left;
}
return deleteTargetNode;
}
//要被刪除的节点只有右子树
if (deleteTargetNode.left == null && deleteTargetNode.right != null) {
if (deleteTargetNode.parent.left == deleteTargetNode) {
deleteTargetNode.parent.left = deleteTargetNode.right;
}
if (deleteTargetNode.parent.right == deleteTargetNode) {
deleteTargetNode.parent.right = deleteTargetNode.right;
}
return deleteTargetNode;
}
//要被刪除的节点有左子树和右子树
if (deleteTargetNode.left != null && deleteTargetNode.right != null) {
//找到左子树的前驱节点
Node pre = pre(deleteTargetNode);
//前驱节点赋值 给 要删除节点
deleteTargetNode.key = (T) pre.key;
//前驱节点肯定没有右子树,所以只判断是否有左子树即可 (因为中序遍历 左中右的顺序, 要是有右节点, 那么右节点肯定要比当前选中的前驱节点大,不可能)
//前驱节点没有左子树
if (pre.left == null) {
pre.parent = null;
return pre;
}else{
//前驱节点有左子树
if (pre.parent.left == pre) {
pre.parent.left = pre.left;
}
if (pre.parent.right == pre) {
pre.parent.right = pre.left;
}
return deleteTargetNode;
}
}
return null;
}
public static void main(String[] args) {
BinarySearchTree<Integer> tree = new BinarySearchTree<>();
tree.insert(1);
tree.insert(5);
tree.insert(4);
tree.insert(3);
tree.insert(2);
tree.insert(6);
// System.out.println(tree.search(1));
// System.out.println(tree.search2(1));
// tree.preOrder();
// System.out.println();
// tree.inOrder();
// System.out.println();
// tree.postOrder();
// System.out.println(tree.maxNode());
// System.out.println(tree.minNode());
// Node search1 = tree.search(1);
// Node search2 = tree.search(7);
// Node search3 = tree.search(4);
// Node search4 = tree.search(3);
// Node search5 = tree.search(8);
// Node search6 = tree.search(2);
// System.out.println(tree.pre(search1));
// System.out.println(tree.after(search1));
// System.out.println(" ");
// System.out.println(tree.pre(search2));
// System.out.println(tree.after(search2));
// System.out.println(" ");
// System.out.println(tree.pre(search3));
// System.out.println(tree.after(search3));
// System.out.println(" ");
// System.out.println(tree.pre(search4));
// System.out.println(tree.after(search4));
// System.out.println(" ");
// System.out.println(tree.pre(search5));
// System.out.println(tree.after(search5));
// System.out.println(" ");
// System.out.println(tree.pre(search6));
// System.out.println(tree.after(search6));
// System.out.println(" ");
tree.inOrder();
System.out.println("");
tree.remove(2);
tree.inOrder();
}
}
- 代码中的删除节点功能,并没有考虑根节点的删除
- 通过BST的代码实现,强化了二叉搜索树的基本原理,复习了树的基本概念, 其中较为困难的是节点的删除功能, 对于理论知识的遗忘和欠缺,导致我必须在视频教程的基础上,重学BST的删除, 对于删除节点的代码,只按照规则进行实现, 没有重构,看起来有点别扭…
- 该视频对于节点的删除,原理讲的还是很好,代码讲解有点绕,可以参考.