樹系列:
- 【資料結構&算法】樹的基本概念
- 【資料結構&算法】二叉樹的概念和多種周遊方式
- 【資料結構&算法】二叉排序樹
文章目錄
-
- 一、前言
- 二、定義
- 三、類結構
- 四、插入
- 五、查找
- 六、删除
- 七、全部代碼
一、前言
最近在深入了解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);
}
}
}