本篇隨筆主要從以下三個方面介紹樹的平衡:
1):BST不平衡問題
2):BST 旋轉
3):AVL Tree
一:BST不平衡問題的解析
之前有提過普通BST的一些一些缺點,例如BST的高度是介於lgN和N之間的,如果是N的的話,顯然效率很低,不是我們需要的;但是在實際情況中,BST的高度h = N的情況卻經常出現,例如下圖所示。在BST中search,insert的running time都等於BST的高度h,我們肯定希望高度h越小越好,best case就是lgN。下圖的example 2的情況,我們會稱之為這個BST是不平衡的。 所以如果遇到這種不平衡的BST,我們如何解決呢?如何將不平衡的BST轉化成平衡的BST呢?如何將BST的高度h從N轉化成lgN呢?
二:樹的平衡
下面我們就來介紹一下樹的旋轉 rotation。BST是可以經過一些旋轉操作后,仍然保持BST的結構不變的,即對於每一個node,該node的left child的值總是小於這個node的值,而該node的right child的值總是大於這個node的值。經過總結,這個旋轉主要可以分為4中模式,這四種模式如下面的兩圖所示:
這四種rotation的方式是由BST的特性所決定的,至於為什么這樣旋轉是是正確的,也是由BST的特點所確定的,我在這就不證明了。只要大家記住BST可以有這4中旋轉模式即可。其實上面所示的rotation過程,都是為了平衡樹的目的。 那么現在有一個問題來了,我們說了這么多平衡,不平衡的概念,前面我們都是通過直觀的感受來體味平衡或者不平衡,那么到底有什么明確的指標可以指明一個BST到底是平衡還是不平衡的呢???這個指標到底是什么呢?那么下面就要解釋AVL Tree的概念了。
三:AVL Tree
首先AVL Tree要滿足以下2個條件:
1. AVL Tree遵循BST的結構;即left child 小於當前node, right child大於當前node。
2.每一個node的2個 child nodes的高度相差不大於1。
根據上面的條件,我們可以看出AVL Tree其本質是一種特殊的BST。所以我們現在有一個定性的指標來判斷一個BST是不是平衡的了,這個指標就是上面2個條件。當然了BST中有很多指標來判讀一個BST是不是平衡的,我們這里只是用AVL Tree作為其中之一個指標,你也可以用其他的指標方法。
所以AVL Tree是平衡的,其高度是h=lgN;
在AVL Tree中,每一個node的高度等於取其2個child node 的較大的高度值加1,即max[left child height, right child height]+1; 若node==NULL,則其高度默認為-1.
當在構建AVL Tree的過程中,向其中insert node的時候,首先第一步跟BST insert一樣,然后第二步是要檢查insert后node的2個children之間的高度差,然后根據相應的高度差來判斷相應的rotation的pattern,經過旋轉后,使整個Tree仍然保持AVL Tree的特性,即滿足上面的2個條件,所以仍然是平衡的。由於insert,search的操作的時間復雜度在BST中都是等於樹的高度,AVL Tree作為一種特殊的BST,insert, search的操作的時間復雜度自然也是等於AVL的高度h=lgN. 這樣的時間復雜度還是可以讓我們滿意的,其效率也要遠遠高於O(N)。AVL Tree的C++ 實現過程如下面的代碼所示,以下代碼實現了AVL Tree的insertion, sorting, rotation等功能。代碼僅供學習交流等非盈利使用,不能用於商業目的,作者保留追溯的權利。
#include "AVLTree.hpp" using namespace std; AVL_Tree::AVL_Tree(){ this->root = NULL; } void AVL_Tree::setRoot(Node *root){ this->root = root; } Node *AVL_Tree::getRoot(){ return this->root; } /* * height of tree or subtree * * a node's height equals the max of node's left child's height and node's right child's height plus 1 * *parameters: 1, node;//the node that we want to measure with * *return: the height of the node */ int AVL_Tree::height(Node *node){ int h = -1; if (node != NULL) { int l_height = height(node->getLeft()); int r_height = height(node->getRight()); h = std::max(l_height,r_height) + 1; } return h; } /* * the height difference of two children nodes * *parameters: * 1, node;//the node which we want to know the differences of its two children * *return: int; the height difference of the two children nodes */ int AVL_Tree::heightDiff(Node *node){ int l_height = height(node->getLeft()); int r_height = height(node->getRight()); return l_height-r_height; } /* * *4 types of rotations * *1)left left pattern *2)left right pattern *3)right right pattern *4)right left pattern * */ void AVL_Tree::ll_rotation(Node *node){ int value = node->getData(); Node *temp = node->getLeft(); node->setData(temp->getData()); node->setLeft(temp->getLeft()); temp->setData(value); temp->setLeft(temp->getRight()); temp->setRight(node->getRight()); node->setRight(temp); } void AVL_Tree::lr_rotation(Node *node){ Node *temp = node->getLeft(); node->setLeft(temp->getRight()); temp->setRight(temp->getRight()->getLeft()); node->getLeft()->setLeft(temp); ll_rotation(node); } void AVL_Tree::rr_rotation(Node *node){ int value = node->getData(); Node *temp = node->getRight(); node->setData(temp->getData()); node->setRight(temp->getRight()); temp->setData(value); temp->setRight(temp->getLeft()); temp->setLeft(node->getLeft()); node->setLeft(temp); } void AVL_Tree::rl_rotation(Node *node){ Node *temp = node->getRight(); node->setRight(temp->getLeft()); temp->setLeft(node->getRight()->getRight()); node->getRight()->setRight(temp); rr_rotation(node); } /* *Description: balancing the node whoes two children nodes' height difference is greater than 1 or smaller than -1 * *parameters: * 1, node;//the node which we want to rotate with, it is the polar point of the rotation * * *return: void * * * */ void AVL_Tree::balance(Node *node){ int balance_factor = heightDiff(node);//differences of the node's two sub nodes. if (balance_factor>1) {//left side is heavy if (heightDiff(node->getLeft())>0) {//left left case ll_rotation(node); }else{//left right case lr_rotation(node); } }else if (balance_factor<-1){//right side heavy if (heightDiff(node->getRight())<0) {//right right case rr_rotation(node); }else{//right left case rl_rotation(node); } } } /* * Description: insert a node into the AVL tree and keep the whole structure balanced after inserting * *Parameters: * 1, Node *node;//the node which needs to be inserted * 2, Node *root;//the root of the tree or subtree; * *Return: Node *;//the parent node of the inserted node; */ Node *AVL_Tree::insert(Node *node, Node *root){ if (this->root == NULL) { Node *root = new Node(); root->setLeft(NULL); root->setRight(NULL); root->setData(node->getData()); this->root = root; return root; } if (root == NULL) { return node; }else if(node->getData() < root->getData()){ root->setLeft(insert(node, root->getLeft())); balance(root); }else if (node->getData()>=root->getData()){ root->setRight(insert(node, root->getRight())); balance(root); } return root; } /* *Description: print out the sorted nodes of the AVL tree of AVL subtree * *parameters: * 1, Node *node;//the root of the AVL tree of AVL subtree * * */ void AVL_Tree::inorderSort(Node *node){ if (node == NULL) { return; } inorderSort(node->getLeft()); std::cout<<node->getData()<<" "; inorderSort(node->getRight()); }