二叉搜尋樹
二叉搜尋樹也叫二叉查詢樹或者二叉排序樹,它要麼是一顆空樹,要麼滿足以下幾點:
1.若任意節點的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值。
2.若任意節點的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值。
3.任意節點的左、右子樹也分別為二叉搜尋樹。
4.沒有鍵值相等的節點。
二叉搜尋樹的實現
1.二叉搜尋樹的儲存結構
public class BinarySearchTree {
public static Node root;
public BinarySearchTree(){
this.root = null;
}
}
class Node{
int data;
Node left;
Node right;
public Node(int data){
this.data = data;
left = null;
right = null;
}
}
複製程式碼
2.二叉搜尋樹的插入
a.迴圈二分查詢到需要插入的地方。
b.假如插入的值小於當前的值,並且當前左節點為空,那麼左節點就指向新節點。
c.假如插入的值大於當前的值,並且當前右節點為空,那麼右節點就指向新節點。
public void insert(int id){
Node newNode = new Node(id);
if(root == null){
root = newNode;
return;
}
Node current = root;
Node parent = null;
while(true){
parent = current;
if(id < current.data){
current = current.left;
if(current == null){
parent.left = newNode;
return;
}
} else {
current = current.right;
if(current == null){
parent.right = newNode;
return;
}
}
}
}
複製程式碼
3.二叉搜尋樹的刪除
a.當刪除節點為葉子節點時,直接刪除節點。
b.當刪除節點只有左子樹時,重接左子樹。
c.當刪除節點只有右子樹時,重接右子樹。
d.當刪除節點既有左子樹,又有右子樹時,先找一個可以替換刪除節點的節點。由於二叉樹的性質,左子樹的值小於根節點的值,右子樹的值大於根節點的值。所以右子樹的最左的節點就是替換刪除的節點,然後在重接右子樹。
第 d 點的圖例:
public boolean delete(int id) {
Node parent = root;
Node current = root;
boolean isLeftChild = false;
while (current.data != id) {
parent = current;
if (current.data > id) {
isLeftChild = true;
current = current.left;
} else {
isLeftChild = false;
current = current.right;
}
if (current == null) {
return false;
}
}
//刪除的節點既沒左節點,也沒右節點
if (current.left == null && current.right == null) {
if (current == root) {
root = null;
}
if (isLeftChild == true) {
parent.left = null;
} else {
parent.right = null;
}
}
//刪除的節點只有左節點
else if (current.right == null) {
if (current == root) {
root = current.left;
} else if (isLeftChild) {
parent.left = current.left;
} else {
parent.right = current.left;
}
}
//刪除的節點只有右節點
else if (current.left == null) {
if (current == root) {
root = current.right;
} else if (isLeftChild) {
parent.left = current.right;
} else {
parent.right = current.right;
}
}
//刪除的節點既有左節點,又有右節點
else if (current.left != null && current.right != null) {
//找到右子樹的最左節點
Node successor = getSuccessor(current);
if (current == root) {
root = successor;
} else if (isLeftChild) {
parent.left = successor;
} else {
parent.right = successor;
}
successor.left = current.left;
}
return true;
}
public Node getSuccessor(Node deleleNode) {
Node successsor = null;
Node successsorParent = null;
Node current = deleleNode.right;
while (current != null) {
successsorParent = successsor;
successsor = current;
current = current.left;
}
if (successsor != deleleNode.right) {
successsorParent.left = successsor.right;
successsor.right = deleleNode.right;
}
return successsor;
}
複製程式碼
4.二叉搜尋樹的查詢
public boolean find(int id) {
Node current = root;
while (current != null) {
if (current.data == id) {
return true;
} else if (current.data > id) {
current = current.left;
} else {
current = current.right;
}
}
return false;
}
複製程式碼
總結
由於它是一顆有序的樹,就可以進行折半查詢,每一次查詢,假如不是匹配的值,都可以排除一半的值。所以一般的時間複雜度是 O(log n)。假如這棵樹退化為斜樹,就差不多是線性表了,它的時間複雜度就是 O(n)。
雖然二叉搜尋樹的最壞時間複雜度是 O(n),但通過一些改進可以把最壞時間複雜度降至 O(log n),比如 AVL樹、紅黑樹等。紅黑樹不需要絕對的平衡,所以插入和刪除效率上要高,在 JDK1.8 中雜湊表儲存大於等於 8 個節點的連結串列就是採用的紅黑樹。
所以二叉搜尋樹在查詢上是非常快的,在一些需要很高查詢效率上推薦使用。
PS:
清山綠水始於塵,博學多識貴於勤。
我有酒,你有故事嗎?
公眾號:「清塵閒聊」。
歡迎一起談天說地,聊程式碼。