柚子快報(bào)激活碼778899分享:算法 C++:AVL樹
柚子快報(bào)激活碼778899分享:算法 C++:AVL樹
目錄
AVL樹概念
AVL樹的實(shí)現(xiàn)
AVL樹的節(jié)點(diǎn)
AVL樹的插入
AVL樹的平衡調(diào)整
右單旋
左單旋
左右雙旋
右左雙旋
完整的插入函數(shù)
AVL樹的查找
AVL樹的驗(yàn)證
驗(yàn)證有序
驗(yàn)證平衡
完整代碼
AVL樹概念
AVL樹是一種具有特殊性質(zhì)的二叉搜索樹,AVL樹的左右子樹也都是AVL樹,且左右子樹的高度差的絕對(duì)值不超過1,超過1就說明AVL樹失衡,需要調(diào)整。AVl樹是一顆高度平衡的二叉搜索樹,通過高度控制高度差去控制平衡。
AVL樹對(duì)比二叉搜索樹多引入了一個(gè)平衡因子的概念,每個(gè)節(jié)點(diǎn)都有一個(gè)平衡因子,任何節(jié)點(diǎn)的平衡因子等于右子樹的高度減去左子樹的高度,所以說平衡因子的取值是0、1和-1。
AVL樹整體節(jié)點(diǎn)和分布與完全二叉樹類似,高度控制在logN范圍內(nèi),那么增刪查改的效率也可以控制在O(logN),相比二叉搜索樹有了本質(zhì)提升。
以下這棵樹就是AVL樹。
? ?????????
AVL樹的實(shí)現(xiàn)
AVL樹的節(jié)點(diǎn)
由于在調(diào)整失衡的AVL樹時(shí),需要頻繁訪問父節(jié)點(diǎn),所以我們?cè)诙x節(jié)點(diǎn)時(shí)也需要引入parent指針。
template
struct AVLTreeNode
{
pair
AVLTreeNode
AVLTreeNode
AVLTreeNode
int _bf;
AVLTreeNode(const pair
:_kv(kv)
, _left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
AVL樹的插入
向AVL樹插入新節(jié)點(diǎn)的過程與二叉搜索樹的插入類似,但有區(qū)別的是,插入新節(jié)點(diǎn)后需要更新平衡因子,然后根據(jù)平衡因子的情況判斷AVL樹是否失衡,失衡就對(duì)AVL樹進(jìn)行調(diào)整。
按照平衡因子的計(jì)算公式,如果新節(jié)點(diǎn)插入到了父親節(jié)點(diǎn)的左邊,那么父親的平衡因子-1,反之,如果插入到右邊,父親的平衡因子+1。
更新后的平衡因子的情況,可以分為兩種:1.平衡因子(從1/-1)變成0。?2.平衡因子(從0)變成1/-1。
情況1就說明已經(jīng)完成平衡因子的更新了,情況2就還得繼續(xù)更新,因?yàn)楫?dāng)父親的平衡因子為1/-1時(shí),說明以父親為根節(jié)點(diǎn)的子樹高度發(fā)生了變化,這樣會(huì)影響父親的父親的平衡因子,所以需要繼續(xù)往上更新平衡因子。
template
class AVLTree
{
typedef AVLTreeNode Node;
public:
bool Insert(const pair
{
//空樹的情況特殊處理
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//不是空樹的情況
Node* parent = nullptr;
Node* cur = _root;
//查找新節(jié)點(diǎn)的插入位置
while (cur)
{
parent = cur;
if (kv.first > cur->_kv.first)
cur = cur->_right;
else if (kv.first < cur->_kv.first)
cur = cur->_left;
else
return false;
}
cur = new Node(kv);
//將父節(jié)點(diǎn)與新節(jié)點(diǎn)鏈接
//判斷新節(jié)點(diǎn)插入到parent的左邊還是右邊
if (kv.first > parent->_kv.first) //新節(jié)點(diǎn)插入到parent的右邊
parent->_right = cur;
else
parent->_left = cur;
//更改新節(jié)點(diǎn)的父親指針
cur->_parent = parent;
//更新平衡因子
while (parent)
{
//插入節(jié)點(diǎn)后除了對(duì)父節(jié)點(diǎn)造成影響還可能對(duì)祖宗節(jié)點(diǎn)造成影響
//隨著循環(huán)進(jìn)行,cur不一定為新節(jié)點(diǎn)
//更新父節(jié)點(diǎn)的平衡因子
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
//檢查更新后父親的平衡因子
//_bf為0,說明樹還是AVL樹,退出循環(huán)
if (parent->_bf == 0)
break;
//_bf == 1/-1,說明需要繼續(xù)向上更新平衡因子
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
//_bf == 2/-2,說明AVL樹失衡,需要進(jìn)行旋轉(zhuǎn)操作
else if (parent->_bf == 2 || parent->_bf == -2)
{
//插入后AVl樹失衡,需要進(jìn)行平衡調(diào)整
}
}
return true;
}
private:
Node* _root = nullptr;;
};
AVL樹的平衡調(diào)整
AVL樹的調(diào)整其實(shí)就是對(duì)失衡的AVl樹進(jìn)行旋轉(zhuǎn)操作,旋轉(zhuǎn)操作必須保持搜索樹的規(guī)則,讓失衡的AVL樹變平衡。
旋轉(zhuǎn)操作可以分為4種:右單旋、左單旋、左右雙旋和右左雙旋。
右單旋
當(dāng)parent->_bf == -2 && cur->_bf == -1時(shí)說明AVL樹往左傾斜,需要進(jìn)行右單旋操作。
右單旋其實(shí)就是從樹的右邊將樹順時(shí)針旋轉(zhuǎn)。?? ??
?
void RotataR(Node* parent)
{
//subL為parent的左孩子節(jié)點(diǎn)
Node* subL = parent->_left;
//subLR為subL的右子節(jié)點(diǎn)
Node* SubLR = subL->_right;
// 將parent與subLR節(jié)點(diǎn)進(jìn)行鏈接
parent->_left = SubLR;
//在SubLR的情況下更改,讓其指向正確的父親
if (SubLR)
SubLR->_parent = parent;
//提前記錄祖父節(jié)點(diǎn)
Node* pParent = parent->_parent;
//鏈接subL與parent
subL->_right = parent;
parent->_parent = subL;
//根據(jù)parent是否是根節(jié)點(diǎn)進(jìn)行不同處理
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
//將pParent和subL鏈接
//但得先判斷parent是pParent的左節(jié)點(diǎn)還是右節(jié)點(diǎn)
if (pParent->_left == parent)
pParent->_left = subL;
else
pParent->_right = subL;
//修改subL的parent指針,讓其指向正確的父親
subL->_parent = pParent;
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
左單旋
當(dāng)parent->_bf == 2 && cur->_bf == 1時(shí)說明AVL樹往右傾斜,需要進(jìn)行左單旋操作。
左單旋其實(shí)就是從樹的左邊將樹逆時(shí)針旋轉(zhuǎn)。
?
?
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* pParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
pParent->_left = subR;
else
pParent->_right = subR;
subR->_parent = pParent;
}
subR->_bf = parent->_bf = 0;
}
左右雙旋
當(dāng)parent->_bf == -2 && cur->_bf == 1時(shí),即新節(jié)點(diǎn)插入到較高左子樹的右側(cè):先左單旋然后再右單旋。
左右雙旋情況比較復(fù)雜,如下圖。
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//記錄subLR的bf,根據(jù)其取值更新左右雙旋后各個(gè)節(jié)點(diǎn)的bf
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
subLR->_bf = 0;
if (bf == -1)
{
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0)
{
subL->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
右左雙旋
當(dāng)parent->_bf == 2 && cur->_bf == -1時(shí),即新節(jié)點(diǎn)插入到較高右子樹的左側(cè):先右單旋然后再左單旋。
右左雙旋其實(shí)就是左右雙旋的另一種情況,我們直接上圖解。
?
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
subRL->_bf = 0;
if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
完整的插入函數(shù)
bool Insert(const pair
{
//空樹的情況特殊處理
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//不是空樹的情況
Node* parent = nullptr;
Node* cur = _root;
//查找新節(jié)點(diǎn)的插入位置
while (cur)
{
parent = cur;
if (kv.first > cur->_kv.first)
cur = cur->_right;
else if (kv.first < cur->_kv.first)
cur = cur->_left;
else
return false;
}
cur = new Node(kv);
//將父節(jié)點(diǎn)與新節(jié)點(diǎn)鏈接
//判斷新節(jié)點(diǎn)插入到parent的左邊還是右邊
if (kv.first > parent->_kv.first) //新節(jié)點(diǎn)插入到parent的右邊
parent->_right = cur;
else
parent->_left = cur;
//更改新節(jié)點(diǎn)的父親指針
cur->_parent = parent;
//更新平衡因子
while (parent)
{
//插入節(jié)點(diǎn)后除了對(duì)父節(jié)點(diǎn)造成影響還可能對(duì)祖宗節(jié)點(diǎn)造成影響
//隨著循環(huán)進(jìn)行,cur不一定為新節(jié)點(diǎn)
//更新父節(jié)點(diǎn)的平衡因子
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
//檢查更新后父親的平衡因子
//_bf為0,說明樹還是AVL樹,退出循環(huán)
if (parent->_bf == 0)
break;
//_bf == 1/-1,說明需要繼續(xù)向上更新平衡因子
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
//_bf == 2/-2,說明AVL樹失衡,需要進(jìn)行旋轉(zhuǎn)操作
else if (parent->_bf == 2 || parent->_bf == -2)
{
//插入后AVl樹失衡,需要進(jìn)行平衡調(diào)整
if (parent->_bf == -2 && cur->_bf == -1)
//右單旋
RotateR(parent);
else if (parent->_bf == 2 && cur->_bf == 1)
//左單旋
RotateL(parent);
else if (parent->_bf == -2 && cur->_bf == 1)
//左右雙旋
RotateLR(parent);
else if (parent->_bf == 2 && cur->_bf == -1)
//右左雙旋
RotateRL(parent);
else
assert(false);
break;
}
else
assert(false);
}
return true;
}
AVL樹的查找
AVL樹的查找與二叉搜索樹是一摸一樣的。
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_kv.first)
cur = cur->_right;
else if (key > cur->_kv.first)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
AVL樹的驗(yàn)證
驗(yàn)證有序
我們首先需要寫一個(gè)中序遍歷來看看插入的數(shù)據(jù)是否符合預(yù)期。
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
我們來跑個(gè)測(cè)試用例。
void test_AVLTree1()
{
AVLTree
AVLTree
int a1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int a2[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a1)
{
avlT1.Insert({ e, e });
}
for (auto e : a2)
{
avlT2.Insert({ e, e });
}
avlT1.InOrder();
avlT2.InOrder();
}
可以看到結(jié)果是沒問題的。
驗(yàn)證平衡
int Height()
{
return _Height(_root);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
return (LeftHeight > RightHeight ? LeftHeight : RightHeight) + 1;
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
bool _IsBalanceTree(Node* root)
{
// 空樹也是AVL樹
if (root == nullptr)
return true;
// 計(jì)算pRoot結(jié)點(diǎn)的平衡因子:即Root左右子樹的高度差
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
int diff = RightHeight - LeftHeight;
// 如果計(jì)算出的平衡因子與Root的平衡因子不相等,或者
// pRoot平衡因子的絕對(duì)值超過1,則一定不是AVL樹
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度異常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子異常" << endl;
return false;
}
// pRoot的左和右如果都是AVL樹,則該樹一定是AVL樹
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
我們用大量數(shù)據(jù)來測(cè)試下。
void test_AVLTree2()
{
const int N = 1000000;
vector
v.reserve(N);
srand((unsigned int)time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
}
AVLTree
for (auto e : v)
{
t.Insert(make_pair(e, e));
}
if (t.IsBalanceTree())
cout << "是AVL樹" << endl;
else
cout << "不是AVL樹" << endl;
cout << "Height:" << t.Height() << endl;
cout << "Size:" << t.Size() << endl;
}
完整代碼
#include
#include
using namespace std;
template
struct AVLTreeNode
{
pair
AVLTreeNode
AVLTreeNode
AVLTreeNode
int _bf;
AVLTreeNode(const pair
:_kv(kv)
, _left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template
class AVLTree
{
typedef AVLTreeNode
public:
bool Insert(const pair
{
//空樹的情況特殊處理
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//不是空樹的情況
Node* parent = nullptr;
Node* cur = _root;
//查找新節(jié)點(diǎn)的插入位置
while (cur)
{
parent = cur;
if (kv.first > cur->_kv.first)
cur = cur->_right;
else if (kv.first < cur->_kv.first)
cur = cur->_left;
else
return false;
}
cur = new Node(kv);
//將父節(jié)點(diǎn)與新節(jié)點(diǎn)鏈接
//判斷新節(jié)點(diǎn)插入到parent的左邊還是右邊
if (kv.first > parent->_kv.first) //新節(jié)點(diǎn)插入到parent的右邊
parent->_right = cur;
else
parent->_left = cur;
//更改新節(jié)點(diǎn)的父親指針
cur->_parent = parent;
//更新平衡因子
while (parent)
{
//插入節(jié)點(diǎn)后除了對(duì)父節(jié)點(diǎn)造成影響還可能對(duì)祖宗節(jié)點(diǎn)造成影響
//隨著循環(huán)進(jìn)行,cur不一定為新節(jié)點(diǎn)
//更新父節(jié)點(diǎn)的平衡因子
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
//檢查更新后父親的平衡因子
//_bf為0,說明樹還是AVL樹,退出循環(huán)
if (parent->_bf == 0)
break;
//_bf == 1/-1,說明需要繼續(xù)向上更新平衡因子
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
//_bf == 2/-2,說明AVL樹失衡,需要進(jìn)行旋轉(zhuǎn)操作
else if (parent->_bf == 2 || parent->_bf == -2)
{
//插入后AVl樹失衡,需要進(jìn)行平衡調(diào)整
if (parent->_bf == -2 && cur->_bf == -1)
//右單旋
RotateR(parent);
else if (parent->_bf == 2 && cur->_bf == 1)
//左單旋
RotateL(parent);
else if (parent->_bf == -2 && cur->_bf == 1)
//左右雙旋
RotateLR(parent);
else if (parent->_bf == 2 && cur->_bf == -1)
//右左雙旋
RotateRL(parent);
else
assert(false);
break;
}
else
assert(false);
}
return true;
}
void RotateR(Node* parent)
{
//subL為parent的左孩子節(jié)點(diǎn)
Node* subL = parent->_left;
//subLR為subL的右子節(jié)點(diǎn)
Node* subLR = subL->_right;
// 將parent與subLR節(jié)點(diǎn)進(jìn)行鏈接
parent->_left = subLR;
//在SubLR的情況下更改,讓其指向正確的父親
if (subLR)
subLR->_parent = parent;
//提前記錄祖父節(jié)點(diǎn)
Node* pParent = parent->_parent;
//鏈接subL與parent
subL->_right = parent;
parent->_parent = subL;
//根據(jù)parent是否是根節(jié)點(diǎn)進(jìn)行不同處理
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
//將pParent和subL鏈接
//但得先判斷parent是pParent的左節(jié)點(diǎn)還是右節(jié)點(diǎn)
if (pParent->_left == parent)
pParent->_left = subL;
else
pParent->_right = subL;
//修改subL的parent指針,讓其指向正確的父親
subL->_parent = pParent;
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* pParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
pParent->_left = subR;
else
pParent->_right = subR;
subR->_parent = pParent;
}
subR->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//記錄subLR的bf,根據(jù)其取值更新左右雙旋后各個(gè)節(jié)點(diǎn)的bf
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
subLR->_bf = 0;
if (bf == -1)
{
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0)
{
subL->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
subRL->_bf = 0;
if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
else
assert(false);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_kv.first)
cur = cur->_right;
else if (key > cur->_kv.first)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
return (LeftHeight > RightHeight ? LeftHeight : RightHeight) + 1;
}
int _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
bool _IsBalanceTree(Node* root)
{
// 空樹也是AVL樹
if (root == nullptr)
return true;
// 計(jì)算pRoot結(jié)點(diǎn)的平衡因子:即Root左右子樹的高度差
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
int diff = RightHeight - LeftHeight;
// 如果計(jì)算出的平衡因子與Root的平衡因子不相等,或者
// pRoot平衡因子的絕對(duì)值超過1,則一定不是AVL樹
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度異常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子異常" << endl;
return false;
}
// pRoot的左和右如果都是AVL樹,則該樹一定是AVL樹
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
private:
Node* _root = nullptr;
};
拜拜,下期再見?
摸魚ing???
柚子快報(bào)激活碼778899分享:算法 C++:AVL樹
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。