树系列:
一、前言
最近在深入了解MySQL索引,发现MySQL索引主要用B+树实现的。本人对树这种数据结构,一直以来掌握都是模棱两可的状态,现在希望通过写一个关于树的专题系列,来掌握树这种数据结构,为深入了解MySQL索引打下基础,做到心中有"树"。
二、定义
二叉排序树(Binary Sort(Search) Tree) 也叫二叉搜索树
- 若左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 左、右子树也分别为二叉排序树
- 二叉排序树也可以是一个空树
三、类结构
public class BinarySortTree<K extends Comparable<? super K>, V> {
private Node root;
private class Node {
private K key;
private V value;
private Node left;
private Node right;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Node{" + "value=" + value + '}';
}
}
}
四、插入
思路:
- 若二叉树为空,则首先单独生成根结点。
- 首先执行查找算法,找出被插结点的父亲结点。
- 判断被插结点是其父亲结点的左结点或者右结点,将被插结点作为叶子结点插入。
注意:新插入的结点总是叶子结点。
public void add(K key, V value) {
Node node = new Node(key, value);
if (this.root == null) {
this.root = node;
return;
}
this.add(this.root, node);
}
private void add(Node root, Node node) {
if (node.key.compareTo(root.key) < 0) {
if (root.left == null) {
root.left = node;
} else {
add(root.left, node);
}
} else {
if (root.right == null) {
root.right = node;
} else {
add(root.right, node);
}
}
}
五、查找
思路:
- 如果二叉排序树为空,返回null
- 如果根结点的键等于要查找的键,返回根结点的值
- 否则,递归的在子树中查找,如果要查找的键小于根结点的键在左子树中查找,反之在右子树中查找
效率:
查找效率和二叉树的高度有关,最好的情况是,即由n个结点构成的高度为
⌊log2n⌋+1的二叉排序树,效率为O(log2n)。
最坏的情况是单向生长的二叉排序树,即n个结点高度为n的二叉排序树,其效率为O(n)
public V get(K key) {
if (this.root == null) {
return null;
}
Node node = getNode(this.root, key);
if (node == null) {
return null;
}
return node.value;
}
private Node getNode(Node root, K key) {
if (root == null) {
return null;
}
if (root.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) > 0) {
return getNode(root.left, key);
} else {
return getNode(root.right, key);
}
}
六、删除
思路:
二叉排序树的删除相对麻烦点,分为三种情况
- 删除的是叶子结点
- 删除的结点只有左子树/右子树
- 删除结点左右子树都存在
public boolean delete(K key) {
if (this.root == null) {
return false;
}
Node targetNode = getNode(this.root, key);
if (targetNode == null) {
return false;
}
Node parentNode = getParentNode(this.root, key);
//删除的是叶子结点
if (targetNode.left == null && targetNode.right == null) {
//只有根结点
if (parentNode == null) {
this.root = null;
return true;
}
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = null;
return true;
}
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = null;
return true;
}
}
//删除的结点只有左子树
if (targetNode.left != null && targetNode.right == null) {
if (parentNode == null) {
this.root = targetNode.left;
return true;
}
//删除结点是父结点的左结点
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = targetNode.left;
return true;
}
//删除结点是父结点的右结点
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = targetNode.left;
return true;
}
}
//删除的结点只有右子树
if (targetNode.right != null && targetNode.left == null) {
if (parentNode == null) {
this.root = targetNode.right;
return true;
}
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = targetNode.right;
return true;
}
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = targetNode.right;
return true;
}
}
//删除结点左右子树都存在
if (targetNode.right != null && targetNode.left != null) {
//从右子树早到最小的结点
Node minNode = getMinNode(targetNode.right);
//删除minNode
delete(minNode.key);
targetNode.key = minNode.key;
targetNode.value = minNode.value;
}
return false;
}
//查找当前结点
private Node getNode(Node root, K key) {
if (root == null) {
return null;
}
if (root.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) > 0) {
return getNode(root.left, key);
} else {
return getNode(root.right, key);
}
}
//查找父结点
private Node getParentNode(Node root, K key) {
if (root == null) {
return null;
}
if (root.left != null) {
if (root.left.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) > 0) {
return getParentNode(root.left, key);
}
}
if (root.right != null) {
if (root.right.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) < 0) {
return getParentNode(root.right, key);
}
}
return null;
}
//查找左子树值最小的结点,也就是叶子结点
private Node getMinNode(Node node) {
Node targetNode = node;
while (targetNode.left != null) {
targetNode = targetNode.left;
}
return targetNode;
}
七、全部代码
public class BinarySortTree<K extends Comparable<? super K>, V> {
private Node root;
private class Node {
private K key;
private V value;
private Node left;
private Node right;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
'}';
}
}
/**
* 添加结点
* @param key
* @param value
*/
public void add(K key, V value) {
Node node = new Node(key, value);
if (this.root == null) {
this.root = node;
return;
}
this.add(this.root, node);
}
private void add(Node root, Node node) {
if (node.key.compareTo(root.key) < 0) {
if (root.left == null) {
root.left = node;
} else {
add(root.left, node);
}
} else {
if (root.right == null) {
root.right = node;
} else {
add(root.right, node);
}
}
}
/**
* 获取值
* @param key
* @return
*/
public V get(K key) {
if (this.root == null) {
return null;
}
Node node = getNode(this.root, key);
if (node == null) {
return null;
}
return node.value;
}
/**
* 获取当前结点
* @param root
* @param key
* @return
*/
private Node getNode(Node root, K key) {
if (root == null) {
return null;
}
if (root.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) > 0) {
return getNode(root.left, key);
} else {
return getNode(root.right, key);
}
}
/**
* 获取父结点
* @param root
* @param key
* @return
*/
private Node getParentNode(Node root, K key) {
if (root == null) {
return null;
}
if (root.left != null) {
if (root.left.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) > 0) {
return getParentNode(root.left, key);
}
}
if (root.right != null) {
if (root.right.key.compareTo(key) == 0) {
return root;
}
if (root.key.compareTo(key) < 0) {
return getParentNode(root.right, key);
}
}
return null;
}
/**
* 查找左子树值最小的结点,也就是叶子结点
* @param node
* @return
*/
private Node getMinNode(Node node) {
Node targetNode = node;
while (targetNode.left != null) {
targetNode = targetNode.left;
}
return targetNode;
}
/**
* 删除结点
* @param key
* @return
*/
public boolean delete(K key) {
if (this.root == null) {
return false;
}
Node targetNode = getNode(this.root, key);
if (targetNode == null) {
return false;
}
Node parentNode = getParentNode(this.root, key);
//删除的是叶子结点
if (targetNode.left == null && targetNode.right == null) {
//只有根结点
if (parentNode == null) {
this.root = null;
return true;
}
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = null;
return true;
}
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = null;
return true;
}
}
//删除的结点只有左子树
if (targetNode.left != null && targetNode.right == null) {
if (parentNode == null) {
this.root = targetNode.left;
return true;
}
//删除结点是父结点的左结点
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = targetNode.left;
return true;
}
//删除结点是父结点的右结点
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = targetNode.left;
return true;
}
}
//删除的结点只有右子树
if (targetNode.right != null && targetNode.left == null) {
if (parentNode == null) {
this.root = targetNode.right;
return true;
}
if (parentNode.left != null && parentNode.left.key.compareTo(targetNode.key) == 0) {
parentNode.left = targetNode.right;
return true;
}
if (parentNode.right != null && parentNode.right.key.compareTo(targetNode.key) == 0) {
parentNode.right = targetNode.right;
return true;
}
}
//删除结点左右子树都存在
if (targetNode.right != null && targetNode.left != null) {
//从右子树早到最小的结点
Node minNode = getMinNode(targetNode.right);
//删除minNode
delete(minNode.key);
targetNode.key = minNode.key;
targetNode.value = minNode.value;
}
return false;
}
/**
* 前序遍历
*/
public void preOrder() {
if (this.root == null) {
return;
}
preOrder(this.root);
}
private void preOrder(Node node) {
System.out.println(node);
if (node.left != null) {
preOrder(node.left);
}
if (node.right != null) {
preOrder(node.right);
}
}
}