AVL樹
概念
搜索二叉樹雖然降低了查找的效率,但是如果數據有序或者接近有序,那麼搜索二叉樹就會退化成單枝樹,搜索的時間複雜度就會變成O(n),極大的降低了效率,因此我們引入了AVL樹,在向樹中插入元素的時候,能保證每一個節點的左右子樹的高度不會相差1,從而降低了樹的高度,提高了查找的效率。
2.我們把具有一下性質的樹稱之爲AVL樹:
1. 首先它是二叉搜索樹。
2. 二叉樹每個節點的左子樹和右子樹都是AVL樹。
3. 每一個節點的平衡因子絕對值不超過1(平衡因子:當前節點右子樹高度減去左子樹高度)
這樣就可以降低樹的高度,使得AVL樹的查找時間複雜度在O(log(n));
AVL樹的插入操作
插入操作很有可能導致AVL樹不滿足性質,所以我們需要旋轉操作來使得樹滿足性質。
具體插入過程:
- 當樹是空樹的時候,直接創建節點,此節點爲根節點,滿足性質
- 當樹非空時,先查找當前節點是否在樹中存在,若存在,插入失敗,直接返回,如果不存在,找到插入位置,插入節點,更新平衡因子,調整樹。
新節點pcur的平衡因子爲0,它的雙親的平衡因子有三種情況:
1.雙親parent的平衡因子爲0
這種情況就是在parent的較矮的子樹上插入,那麼parent的平衡因子爲0,parent平衡,從parent到根節點的路徑上的節點的平衡因子都沒有發生改變,因此樹滿足AVL樹性質,不需調整。
2.雙親的parent的平衡因子絕對值爲1
parent節點平衡因子在插入之前爲0,插入之後以parent爲根的樹平衡沒有被破壞,但是子樹的高度增加了,向上回溯查看節點平衡因子。
3.在上述2更新完成後,如果parent平衡因子的絕對值爲2,新節點在較高子樹上進行插入,那麼就必須要進行平衡處理:
如果parent->_buf是2,說明右子樹高,設parent的右子樹爲subR,那麼
當subR->_buf爲1時候,執行左單旋轉:
當subR->buf爲-1的時候,執行先右後左雙旋;
如果pparent->_buf是-2,說明左子樹高,折parent的左子樹是subL,那麼
當subL->buf是-1的時候,執行右單旋;
當subR->buf是1的時候,執行先左後右雙旋;
旋轉之後parent爲跟的子樹高度降低,不需要向上進行回溯。
左單旋
插入節點位於較高右子樹的右側
void RotateL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = pParent->_pRight->_pLeft;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
subR->_pLeft = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subR;
subR->_pParent = pparent;
if (pParent == _pRoot)
{
_pRoot = subR;
}
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subR;
else
pparent->_pRight = subR;
}
pParent->_buf = subR->_buf = 0;
}
右單旋
插入節點位於較高左子樹的左側
void RotateR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = pParent->_pLeft->_pRight;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subL;
subL->_pParent = pparent;
if (pParent == _pRoot)
_pRoot = subL;
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subL;
else
pparent->_pRight = subL;
}
pParent->_buf = subL->_buf = 0;
}
右左雙旋
插入節點位於較高右子樹的左側
void RotateRL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = NULL;
if (subR != NULL)
subRL = subR->_pLeft;
RotateR(subR);
RotateL(pParent);
if (subRL->_buf == 1)
pParent->_buf = -1;
else if (subRL->_buf == -1)
pParent->_buf = 0;
}
左右雙旋
插入節點位於較高左子樹的右側
void RotateLR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = NULL;
if (subL != NULL)
subLR = subL->_pRight;
RotateL(subL);
RotateR(pParent);
if (subLR->_buf == 1)
pParent->_buf = 0;
else if (subLR->_buf == -1)
pParent->_buf = 1;
}
AVL樹的刪除
- 從AVL樹中刪除一個節點,首先要判斷這個節點是否在樹中,若存在,刪除就可能導致AVL樹不滿足性質的情況,就要進行旋轉調整。
- 被刪除節點存在一下幾種情況:
1.刪除節點只有左孩子。
2.刪除節點只有右孩子。
3.刪除節點左右孩子都存在。—->變爲刪除中序遍歷下的第一個節點。 - 更新parent的平衡因子,如果刪除節點是parent 的左孩子,那麼平衡因子+1,相反,如果是右孩子,平衡因子-1,根據修改後的parent的平衡因子修改到根節點的所有節點。
1.如果parent的平衡因子爲1或者-1,那麼parent的高度不變,以parent爲子樹的所有節點平衡因子都沒有發生變化,不需要調整。
2.如果parent的平衡因子變成0,雖然以parent爲根的樹平衡了,但是其高度減1了,這時候就要去檢查parent的雙親的平衡因子的情況。
3.parent的平衡因子爲2或者-2,則較矮的子樹被縮短,需要進行平衡話旋轉。
令parent較高子樹的根爲q,根據q的平衡因子,分一下三種情況 :
如果q的平衡因子爲0,執行單旋轉恢復parent
如果q的平衡因子與parent平衡因子(正負)號相同,則執行一個單旋轉恢復 parent
如果q的平衡因子與parent平衡因子(正負)號相反,則執行一個雙旋轉恢復 parent
AVL樹的代碼:
#pragma once
#include<iostream>
#include<math.h>
using namespace std;
template<class k, class v>
struct AVLTreeNode
{
AVLTreeNode(const k& key, const v& value)
:_key(key)
,_value(value)
,_buf(0)
,_pLeft(NULL)
,_pRight(NULL)
,_pParent(NULL)
{}
AVLTreeNode<k, v>* _pLeft;
AVLTreeNode<k, v>* _pRight;
AVLTreeNode<k, v>* _pParent;
k _key;
v _value;
int _buf;
};
template<class k,class v>
class AVLTree
{
typedef AVLTreeNode<k, v> Node;
typedef Node* pNode;
public:
AVLTree()
:_pRoot(NULL)
{}
bool Insert(const k& key, const v&value)
{
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
pNode pCur = _pRoot;
pNode pParent = NULL;
while (pCur)
{
if (pCur->_key > key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (pCur->_key < key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
{
cout << "元素已存在,插入失敗" << endl;
return false;
}
}
pCur = new Node(key, value);
if (pParent->_key > key)
{
pParent->_pLeft =pCur;
pParent->_buf--;
}
else
{
pParent->_pRight = pCur;
pParent->_buf++;
}
pCur->_pParent = pParent;
//插入成功,接下來就是進行調整。
//情況一,如果pParent的平衡因子是0
while (pParent)
{
if (pParent->_pRight == pCur)
pParent->_buf++;
else
pParent->_buf--;
if (pParent->_buf == 0)
break;
else if (pParent->_buf == 1 || pParent->_buf == -1)
{
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
if (pParent->_buf == 2)
{
if (pCur->_buf == 1)
RotateL(pParent);
else if (pCur->_buf == -1)
RotateRL(pParent);
}
else if (pParent->_buf == -2)
{
if (pCur->_buf == -1)
RotateR(pParent);
else if (pCur->_buf == 1)
RotateLR(pParent);
}
break;
}
}
return true;
}
void InOrder()
{
_InOrder(_pRoot);
}
bool IsBalance()
{
return _IsBalance(_pRoot);
}
private:
bool _IsBalance(pNode pRoot)
{
if (pRoot == NULL)
return true;
int lefttree = _IsBalance(pRoot->_pLeft);
int righttree = _IsBalance(pRoot->_pRight);
if (abs(righttree - lefttree) > 1)
return false;
return _IsBalance(pRoot->_pLeft) && _IsBalance(pRoot->_pRight);
}
int _Height(pNode pRoot)
{
if (pRoot == NULL)
return 0;
int left = _Height(pRoot->_pLeft);
int right = _Height(pRoot->_pRight);
return left > right ? left + 1 : right + 1;
}
void RotateL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = pParent->_pRight->_pLeft;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
subR->_pLeft = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subR;
subR->_pParent = pparent;
if (pParent == _pRoot)
{
_pRoot = subR;
}
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subR;
else
pparent->_pRight = subR;
}
pParent->_buf = subR->_buf = 0;
}
void RotateR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = pParent->_pLeft->_pRight;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subL;
subL->_pParent = pparent;
if (pParent == _pRoot)
_pRoot = subL;
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subL;
else
pparent->_pRight = subL;
}
pParent->_buf = subL->_buf = 0;
}
void RotateRL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = NULL;
if (subR != NULL)
subRL = subR->_pLeft;
RotateR(subR);
RotateL(pParent);
if (subRL->_buf == 1)
pParent->_buf = -1;
else if (subRL->_buf == -1)
pParent->_buf = 0;
}
void RotateLR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = NULL;
if (subL != NULL)
subLR = subL->_pRight;
RotateL(subL);
RotateR(pParent);
if (subLR->_buf == 1)
pParent->_buf = 0;
else if (subLR->_buf == -1)
pParent->_buf = 1;
}
void _InOrder(pNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
_InOrder(pRoot->_pRight);
}
}
private:
pNode _pRoot;
};
void TestAVLTreee()
{
int a[] = { 16,3,7,11,9,26,18,14,15 };
AVLTree<int, int> avl;
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
avl.Insert(a[i], i);
}
avl.InOrder();
}