前言
二叉搜尋樹在一般情況下它的查詢時間複雜度是 O(log n)。但在一些特殊的情況下,它會退化為斜樹變成線性結構,導致查詢效率大大降低,根本發揮不出折半查詢的優勢。因此,就需要一種平衡的二叉搜尋樹來達到我們期望查詢時間複雜度為 O(log n) 的資料結構,那就是平衡二叉搜尋樹。
平衡二叉搜尋樹
平衡二叉搜尋樹又叫 AVL樹,它具有以下性質:
1.任一節點對應的兩棵子樹的最大高度差為 1。
2.兩棵子樹都是平衡二叉樹。
通過保證上面 2 條規則,因此它也被稱為高度平衡樹。查詢、插入和刪除在平均和最壞情況下的時間複雜度都是O(log n)。因此,它是一顆相對穩定的樹。
為了保證左右 2 棵子樹的最大高度差為 1,我們需要在插入和刪除時,對樹進行旋轉操作,以保證樹的平衡性。有 2 種基本的操作,那就是左旋和右旋。
在進行左右旋轉時,會出現四種可能性:1.左左形態
當 x 是 y 的左節點,y 又是 z 的左節點時,這個時候只需要把 y 節點向右旋轉即可。如下圖: 2.左右形態
當 x 是 y 的右節點,而 y 又是 z 的左節點時,需要先把 y 左旋,變成 左左形態,然後再把 z 右旋。如下圖: 3.右右形態
當 x 是 y 的右節點, y 又是 z 的右節點時,這時候只需要把 y 節點向左旋轉即可。如下圖: 4.右左形態
當 x 是 y 的左節點,而 y 又是 z 的右節點時,需要先把 y 右旋,變成 右右形態,然後再把 z 左旋。如下圖:
平衡二叉樹的實現
public class AVLTree {
//獲取左右節點的高度差
public int getBalance(Node n) {
if (n != null) {
return (getHeight(n.left) - getHeight(n.right));
}
return 0;
}
//獲取節點的高度
public int getHeight(Node n) {
if (n != null) {
return n.height;
}
return 0;
}
//右旋
public Node rightRotate(Node y) {
Node x = y.left;
Node T2 = x.right;
// 旋轉
x.right = y;
y.left = T2;
// 更新節點的高度
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
return x;
}
//左旋
public Node leftRotate(Node x) {
Node y = x.right;
Node T2 = y.left;
// 旋轉
y.left = x;
x.right = T2;
// 更新節點的高度
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
return y;
}
public Node insert(Node node, int data) {
if (node == null) {
return (new Node(data));
}
if (node.data > data) {
node.left = insert(node.left, data);
} else {
node.right = insert(node.right, data);
}
// 更新節點的高度
node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
int balDiff = getBalance(node);
// 右旋
if (balDiff > 1 && data < node.left.data) {
return rightRotate(node);
}
// 左旋
if (balDiff < -1 && data > node.right.data) {
return leftRotate(node);
}
// 先左旋在右旋
if (balDiff > 1 && data > node.left.data) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// 先右旋在左旋
if (balDiff < -1 && data < node.right.data) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}
}
class Node {
int data;
Node left;
Node right;
int height;
public Node(int data) {
this.data = data;
height = 1;
}
}
複製程式碼
總結
二叉搜尋樹的不穩定性,就有可能退化為近似鏈或鏈,查詢時間複雜度就退化為 O(n)。當 n 很大的時候,效能就大大降低,達不到折半的效果。因此,我們需要一個平衡的二叉搜尋樹。
平衡二叉樹是通過對樹節點的左旋和右旋來保證樹的平衡性,也就是保證左右子樹的高度差不超過 1。
在對樹進行左旋和右旋時,有四種形態分別是:左左,左右,右右,右左。
因此,平衡二叉樹的查詢、插入和刪除在平均和最壞情況下的時間複雜度都是O(log n)。
PS:
清山綠水始於塵,博學多識貴於勤。
我有酒,你有故事嗎?
公眾號:「清塵閒聊」。
歡迎一起談天說地,聊程式碼。