AVL樹
一棵AVL樹是每個節點的左子樹和右子樹的高度最多相差1的二叉查找樹(空樹的高度定義爲-1)。
插入一個節點可能會破壞AVL樹的平衡性。如果發生這種情況就需要把平衡性質恢復了之後才認爲插入這步完成。
在插入之後,只有那些從插入點到根節點的路徑上的節點的平衡可能會被改變,因爲只有這些節點的子樹可能會發生變化。當我們沿着這條路徑上行到根並更新平衡信息時,我們可以找到一個節點,它的新平衡破壞了AVL條件。
重新平衡AVL樹的方法根據插入位置的不同分爲單旋轉和雙旋轉。插入發生在外邊(左-左或者右-右)時使用單旋轉,插入發生在內部(左-右或者右-左)時使用雙旋轉。
現在分別給出單旋轉和雙旋轉的圖和實現代碼。根據圖來實現代碼非常簡單。
單旋轉:
Position SingleRotateWithLeft(Position k2)
{
Position k1;
k1 = k2->Left;
k2->Left = k1->Right;
k2->Height = max(GetHeight(k2->Left), GetHeight(k2->Right)) + 1;
k1->Height = max(GetHeight(k1->Left), k2->Height) + 1;
return k1;//new root
}
雙旋轉
Position DoubleRotateWithLeft(Position k3)
{
k3->Left = SingleRotateWithRight(k3->Left);
return SingleRotateWithLeft(k3);
}
Position DoubleRotateWithRight(Position k3)
{
k3->Right = SingleRotateWithLeft(k3->Right);
return SingleRotateWithRight(k3);
}
AVL樹的插入操作:
AVLTree Insert(ElementType X, AVLTree T) {
if (T==NULL) {
T = new struct AVLNode;
if (T==NULL) {
cout<<"out of space"<<endl;
return NULL;
} else {
T->Element = X;
T->Left = T->Right = NULL;
T->Height = 0;
}
} else if (T->Element>X) {
T->Left = Insert(X, T->Left);
if (GetHeight(T->Left)-GetHeight(T->Right)==2) {
if (X<T->Left->Element)
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);
}
} else if (T->Element>X) {
T->Right = Insert(X, T->Right);
if (GetHeight(T->Right)-GetHeight(T->Left)==2) {
if (X>T->Right->Element)
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T);
}
}
T->Height = max(GetHeight(T->Left), GetHeight(T->Right)) + 1;
return T;
}
AVL樹的刪除可以使用懶惰刪除。
伸展樹
它保證從空樹開始任意連續M次對樹的操作最多花費O(MlogN)時間。雖然這種保證並不排除任意一次操作花費O(N)時間的可能。
一棵伸展樹每次操作的攤還代價是O(logN)。
伸展樹的展開操作:
令X是在訪問路徑上的一個(非根)節點,我們將在這個路徑上實施旋轉操作。
1. 如果X的父節點是樹根,我們只要旋轉X和樹根。否則X就存在父親P和祖父G。
2. G-P-X呈之字形,則對G執行雙旋轉。
3. G-P-X呈一字型,則逆轉樹。
遞歸展開工作,直到X變成樹根。
展開工作不僅將訪問的節點移動到根處,而且還會把訪問路徑上的大部分節點的深度大致減少一半(某些淺的節點最多向下推後兩個層次)。
可以通過訪問要被刪除的節點來實現刪除工作。這種操作將節點上推到根處。如果刪除該節點,則得到兩棵子樹TL和TR。如果我們找到TL中最大元素,那麼這個元素就被旋轉到TL的根下,而此時TL將有一個沒有右兒子的根。可以使TR成爲右兒子從而結束刪除。
樹的遍歷
層序遍歷與其他類型的遍歷不同的地方在於它不是遞歸的實施的,它用到隊列而不適用遞歸默認的棧。