前言
今天要介紹幾種高級數據結構AVL樹,介紹之前AVL,會先說明平衡二叉樹,並將樹的學習路線進行總結,並介紹維持平衡的方法:右旋轉、左旋轉。
一、樹學習路線
1、路線總結
總結了一下樹的學習路線,如下圖:
2、說明
上面這個圖要從上往下進行一步一步學習;首先,從二叉樹開始學習,要對樹的一些概念有一些基本了解,如樹的左孩子和右孩子等,然后對樹的遍歷方法:先序、中序和后序遍歷都熟練掌握,有精力再把層序遍歷掌握;
接下來,大部分的樹,都是在二叉樹的基礎上加了許多特性而形成的,所以二叉樹是基礎,如二叉搜索樹,任意一個節點都比左子樹大,都比右子樹小,主要用於解決查找問題,對二分查找法有一個基本了解,還有一個特性,二分搜索樹的中序遍歷:數據就從從小到大進行排序了。
AVL樹,是今天要講的話題,下面會詳細進行講解。
紅黑樹應該是大名鼎鼎了,都應該聽過了,之后我會專門介紹。
trie樹就不再是二叉樹了,是多叉樹了,之后也會講解。
二、平衡二叉樹
1、定義
平衡二叉樹,首先是二叉樹,並且對於任意一個節點,左子樹和右子樹的高度差不能超過1。
2、意義
有了二分查找樹為什么還要平衡二叉樹呢?這篇對二分查找樹進行了詳細介紹,並對先序、中序和后序進行了明確說明,可以參考:https://www.cnblogs.com/liudw-0215/p/9835691.html,因為二叉樹有一個弊端就是會退化為鏈表,就是只有左子樹或右子樹有節點,這樣查詢效率就會變低了。所以,就需要“平衡”這個概念了。
3、平衡因子
先畫個圖,進行說明,不是平衡二叉樹,只是為了說明問題,如下圖:
說明:如上圖,樹的高度從葉子節點開始,並且葉子節點高度是1;平衡因子就是用左子樹高度減去右子樹高度,例如:4這個節點,左子樹2的高度為1,右子樹沒有則為0,所以4這個節點的平衡因子為1。
三、AVL樹
1、定義
AVL樹是自平衡二分搜索樹,既具有平衡性和二分性。
2、構建AVL樹類
是在二分搜索樹的基礎上進行修改並維持“平衡”這個特性的,首先,來看下AVL樹的類,如下:

#ifndef AVLTREE_AVLTREE_H #define AVLTREE_AVLTREE_H #include <algorithm> #include <iostream> #include <vector> template<typename Key, typename Value> class AVLTree { private: struct Node { Key key; Value value; Node *left; Node *right; int height; //用於標注高度,計算平衡因子 Node(Key key, Value value) { this->key = key; this->value = value; this->left = this->right = nullptr; height = 1; } Node(Node *node) { this->key = node->key; this->value = node->value; this->left = node->left; this->right = node->right; this->height = node->height; } }; Node *root; int size; public: AVLTree() { root = nullptr; size = 0; } ~AVLTree() { destroy(root); } int getSize() { return size; } int isEmpty() { return size == 0; } int getHeight(Node *node) { //獲取高度 if (node == nullptr) { return 0; } return node->height; } int getBalanceFactor(Node *node) { //獲取平衡因子 if (node == nullptr) { return 0; } return getHeight(node->left) - getHeight(node->right); } bool isBST() { std::vector<Key> keys; inOrder(root, keys); for (int i = 1; i < keys.size(); ++i) { if (keys.at(i - 1) < keys.at(i)) { return false; } } return true; } bool isBalanced() { return isBalanced(root); } void add(Key key, Value value) { root = add(root, key, value); } bool contains(Key key) { return getNode(root, key) != nullptr; } Value *get(Key key) { Node *node = getNode(root, key); return node == nullptr ? nullptr : &(node->value); } void set(Key key, Value newValue) { Node *node = getNode(root, key); if (node != nullptr) { node->value = newValue; } } // 從二叉樹中刪除鍵值為key的節點 Value *remove(Key key) { Node *node = getNode(root, key); if (node != nullptr) { root = remove(root, key); return &(node->value); } return nullptr; } private: // 向以node為根的二叉搜索樹中,插入節點(key, value) // 返回插入新節點后的二叉搜索樹的根 Node *add(Node *node, Key key, Value value) { if (node == nullptr) { size++; return new Node(key, value); } if (key == node->key) { node->value = value; } else if (key < node->key) { node->left = add(node->left, key, value); } else { node->right = add(node->right, key, value); } node->height = 1 + std::max(getHeight(node->left), getHeight(node->right)); int balanceFactor = getBalanceFactor(node); if (std::abs(balanceFactor) > 1) { std::cout << "unbalanced : " << balanceFactor; } return node; } // 在以node為根的二叉搜索樹中查找key所對應的Node Node *getNode(Node *node, Key key) { if (node == nullptr) { return nullptr; } if (key == node->key) { return node; } else if (key < node->key) { return getNode(node->left, key); } else { return getNode(node->right, key); } } void destroy(Node *node) { if (node != nullptr) { destroy(node->left); destroy(node->right); delete node; size--; } } // 在以node為根的二叉搜索樹中,返回最小鍵值的節點 Node *minimum(Node *node) { if (node->left == nullptr) return node; return minimum(node->left); } // 在以node為根的二叉搜索樹中,返回最大鍵值的節點 Node *maximum(Node *node) { if (node->right == nullptr) return node; return maximum(node->right); } // 刪除掉以node為根的二分搜索樹中的最小節點 // 返回刪除節點后新的二分搜索樹的根 Node *removeMin(Node *node) { if (node->left == nullptr) { Node *rightNode = node->right; delete node; size--; return rightNode; } node->left = removeMin(node->left); return node; } // 刪除掉以node為根的二分搜索樹中的最大節點 // 返回刪除節點后新的二分搜索樹的根 Node *removeMax(Node *node) { if (node->right == nullptr) { Node *leftNode = node->left; delete node; size--; return leftNode; } node->right = removeMax(node->right); return node; } // 刪除掉以node為根的二分搜索樹中鍵值為key的節點 // 返回刪除節點后新的二分搜索樹的根 Node *remove(Node *node, Key key) { if (node == nullptr) { return nullptr; } if (key < node->key) { node->left = remove(node->left, key); return node; } else if (key > node->key) { node->right = remove(node->right, key); return node; } else { if (node->left == nullptr) { Node *rightNode = node->right; delete node; size--; return rightNode; } if (node->right == nullptr) { Node *leftNode = node->left; delete node; size--; return leftNode; } Node *successor = new Node(minimum(node->right)); //Node *precursor = new Node(maximum(node->right)); size++; successor->right = removeMin(node->right); successor->left = node->left; //precursor->left = removeMax(node->left); //precursor->right = node->right; delete node; size--; return successor; //return precursor; } } void inOrder(Node *node, std::vector<Key> keys) { if (node == nullptr) { return; } inOrder(node->left, keys); keys.push_back(node->key); inOrder(node->right, keys); } bool isBalanced(Node *node) { if (node == nullptr) { return true; } int balanceFactor = getBalanceFactor(node); if (std::abs(balanceFactor) > 1) { return false; } return isBalanced(node->left) && isBalanced(node->right); } }; #endif //AVLTREE_AVLTREE_H
增加height屬性,用於記錄每個節點的高度,並計算平衡因子;
3、獲取節點高度
把height屬性返回就可以了:
int getHeight(Node *node) { //獲取高度 if (node == nullptr) { return 0; } return node->height; }
4、獲取平衡因子
將左子樹高度減去右子樹高度即可,但注意:不要區絕對值,因為之后的旋轉要判斷左子樹還是右子樹的高度高,代碼如下:
int getBalanceFactor(Node *node) { //獲取平衡因子 if (node == nullptr) { return 0; } return getHeight(node->left) - getHeight(node->right); }
5、判斷是不是平衡二叉樹
平衡因子大於1就不是平衡二叉樹了,代碼如下:
bool isBalanced(Node *node) { if (node == nullptr) { return true; } int balanceFactor = getBalanceFactor(node); if (std::abs(balanceFactor) > 1) { return false; } return isBalanced(node->left) && isBalanced(node->right); } bool isBalanced() { return isBalanced(root); }
四、AVL樹的旋轉
1、什么時維護平衡?
如下圖,假如原來沒有2這個節點,那么樹是平衡二叉樹,但插入2之后,就不再平衡了,這時就需要維護平衡了,大體上有4種情況需要維護平衡,來說明這一種。
2、右旋轉 LL
將其中的部分節點抽離出來,如下圖:
說明:主要分為兩步:
第一步:將T3保存,然后將y以及孩子節點旋轉到x的右孩子位置,相對於x,y是順時針向右旋轉的,所以叫右旋轉;
第二步:將T3移到y的左孩子位置
最后,形成的二叉樹符合二分和平衡兩個性質,所以還是平衡二叉樹。
3、右旋轉代碼實現
上圖應該已經講解的很明白了吧,代碼如下:
Node *rightRotate(Node *y) { Node *x = y->left; //存x Node *tmp = x->right; //將x的右孩子備份 x->right = y; //將y右旋轉到x的右孩子 y->left = tmp; //將x的右孩子移到y的左側 y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1; //修改y高度,注意要先修改y的高度 x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1; //修改x的高度 return x; }
4、左旋轉 RR
左旋轉和右旋轉很相似,只是方向不同,如下圖:
說明:相對於x,y是逆時針向左旋轉,所以是左旋轉
5、左旋轉代碼實現
左旋轉代碼跟右旋轉很相似,代碼如下:
Node *leftRotate(Node *y){ Node *x = y->right; Node *tmp = x->left; x->left = y; y->right = tmp; y->height = std::max(getHeight(y->left), getHeight(y->right)) + 1; x->height = std::max(getHeight(x->left), getHeight(x->right)) + 1; return x; }
6、LR
還有兩種情況需要討論,LL代表“左左”,LR代表“左右”,如下圖:
說明:借助左旋轉將LR轉為LL,再對LL進行右旋轉就OK了,所以理解左、右旋轉是基礎!
7、LR代碼實現
代碼如下:
if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) { //LR node->left = leftRotate(node->left); return rightRotate(node); }
8、RL
最后一種情況RL,如下圖:
9、RL代碼實現
代碼如下:
if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) { //RL node->right = rightRotate(node->right); return leftRotate(node); }
總結
AVL樹和平衡二叉樹就比較難了,主要理解右旋轉和左旋轉,對之后理解紅黑樹有巨大作用!