AVL树

来源:互联网 发布:香港中文大学人工智能 编辑:程序博客网 时间:2024/06/05 02:31


大概学习了一下AVL树,基本思路还是比较清楚,但是发现还是很难自己写一个AVL树来。
下面记录下自己学习的过程(网上资料很多,就不解释AVL了):
1.参考的代码: http://rosettacode.org/wiki/AVL_tree#C.2B.2B
2.图形化参考工具:  https://www.cs.usfca.edu/~galles/visualization/AVLtree.html
3.各种递归、指针,各种云里雾里;
4.相对于BST(二叉搜索树),多了“父结点指针”,需要调整“父结点指针”的指向;
5.AVL最重要的是为了保证左右子树平衡度绝对差值为1,必须进行旋转,旋转可以总结为下面4种:
  根->左1->左0  -->>             (根)右旋
  根->左1->右0  -->>  (左1)左旋,(根)右旋
  根->右1->右0  -->>             (根)左旋
  根->右1->左0  -->>  (右1)右旋,(根)左旋
  我最开始担心:数据很多的情况下,插入一个数据,引起“多米诺骨牌”效应,引起多次旋转;实际上插入一个数据最多旋转2次,即使是头结点被旋转;数据越多,只是会加大对树高度(平衡)的判断。
6.还是要多画图、多调试代码、多思考才能理清楚,还需要反复来学习。

下面代码是在上述“1”的基础上修改的代码与注释。

#include <algorithm>#include <iostream>#include <deque>using namespace std; // AVL 结点 class AVLnode {public:    int key;    int balance;    AVLnode *left, *right, *parent;    AVLnode(int k, AVLnode *p) : key(k), balance(0), parent(p), left(NULL), right(NULL) {}     ~AVLnode()     {        delete left;        delete right;    }}; // AVL 树  class AVLtree {public:AVLtree(void) : root(NULL) {}  ~AVLtree(void) {delete root;}    //插入bool insert(int key) {if (root == NULL) {root = new AVLnode(key, NULL);}else {    //n为遍历结点    //parent是n的父结点,后面n结点的状态会变(n会指向下一个结点)AVLnode *n = root, *parent; while (true) {if (n->key == key)return false;            parent = n; bool goLeft = n->key > key;n = goLeft ? n->left : n->right;                //上面两句循环判断key值加在左子树还是右子树,然后指针下移                //下面这句,找到位置为NULL后才进入插入 if (n == NULL) {if (goLeft) {parent->left = new AVLnode(key, parent);}else {parent->right = new AVLnode(key, parent);} rebalance(parent);break;}}} return true;}//删除                                                                                                                                      //1.如果删除结点没有子结点,直接删除    //2.如果删除结点有1个子结点,将其父结点指向其子结点,然后删除    //3.如果删除结点有2个子结点,将其用左子树最大结点值取代(或用右子树最小结点值取代),然后删除    //4.重置平衡void deleteKey(const int delKey) {if (root == NULL)return; AVLnode    //删除结点的右侧最小值结点//n为删除结点的替补值,parent为n的父结点,child为n的子树,delNode为删除结点,tmp为临时结点*n       = root,*parent  = root,*delNode = NULL,*child   = root,*tmp     = NULL;    //即使找到删除结点后,依旧继续遍历,然后找到删除结点右侧的最小值/左侧最大值while (child != NULL) {parent = n;n = child;//取右侧最小值child = delKey >= n->key ? n->right : n->left;//取左侧最大值//child = delKey <= n->key ? n->left : n->right;if (delKey == n->key)              delNode = tmp = n;     } if (delNode != NULL) {    //直接修改(删除结点的值)为替补值,其左右指针不变delNode->key = n->key; child = n->left != NULL ? n->left : n->right; if (root->key == delKey) {root = child;}else {    //parent为删除结点n的父结点if (parent->left == n) {parent->left = child;}else                 {parent->right = child;}                //delete tmp;rebalance(parent);}}}//deleteElem 使用递归的方法,参考自之前总结的BSTvoid deleteElem(int elem){deleteElem(root,elem);cout<<endl;}  void printBalance() {printBalance(root);cout << endl;}void LDR_order(){  LDR_order(root);  cout << endl;}     //层序遍历二叉树,即广度优先。      //从根结点开始遍历,存入队列;出队列,遍历此结点左子树,有值,存入队列,再遍历此结点右子树,有值,存入队列;    //递归:出队列,遍历此结点左子树,有值,存入队列,再遍历此结点右子树,有值,存入队列;void level_traverse()  {  if(root == NULL)  {  return;  }    deque<AVLnode *> dequesTree;  dequesTree.push_back(root);    while(dequesTree.size())  {  AVLnode *p = dequesTree.front();  dequesTree.pop_front();    cout << (p->key)<<" ";  if(p->left)  {  dequesTree.push_back(p->left);  }  if(p->right)  {  dequesTree.push_back(p->right);  }  }  cout<<endl;}   private:    AVLnode *root;    //自平衡void rebalance(AVLnode *n) {setBalance(n); if (n->balance == -2) {if (height(n->left->left) >= height(n->left->right))    //根左左,直接右旋根n = rotateRight(n);else    //根左右n = rotateLeftThenRight(n);}else if (n->balance == 2) {if (height(n->right->right) >= height(n->right->left))//判断左旋还是*旋?    //根右右,直接左旋根n = rotateLeft(n);else    //根右左n = rotateRightThenLeft(n);} if (n->parent != NULL) {rebalance(n->parent);//从下向上(子结点到父结点)依次来检查平衡}else {root = n;}} //左旋 AVLnode* rotateLeft(AVLnode *a) {    //原来a为b父结点(b为a的右子树),在这里做了交换(a变为b的左子树)AVLnode *b = a->right;b->parent = a->parent;//a的右子树将指向原b的左子树a->right = b->left; // if (a->right != NULL)    //将原b左子树(现a右子树)父结点指向aa->right->parent = a; b->left = a;a->parent = b;    //改变原a(现b)父结点的指向if (b->parent != NULL) {if (b->parent->right == a) {b->parent->right = b;}else {b->parent->left = b;}} setBalance(a);setBalance(b);return b;} //右旋 AVLnode* rotateRight(AVLnode *a) {AVLnode *b = a->left;b->parent = a->parent;a->left = b->right; if (a->left != NULL)a->left->parent = a; b->right = a;a->parent = b; if (b->parent != NULL) {if (b->parent->right == a) {b->parent->right = b;}else {b->parent->left = b;}} setBalance(a);setBalance(b);return b;} //左旋后右旋,适用于左右 root->left->right情况 AVLnode* rotateLeftThenRight(AVLnode *n) {n->left = rotateLeft(n->left);return rotateRight(n);} //右旋后左旋,适用于右左 root->right->left情况  AVLnode* rotateRightThenLeft(AVLnode *n) {n->right = rotateRight(n->right);return rotateLeft(n);} //求高度 int height(AVLnode *n) {if (n == NULL)return -1;//递归遍历所有结点求得高度(一个顶点的左、右子树都会被递归到,然后取最大值)return 1 + max(height(n->left), height(n->right));} //设置平衡因子balance void setBalance(AVLnode *n) {n->balance = height(n->right) - height(n->left);}     //打印平衡因子 void printBalance(AVLnode *n) {if (n != NULL) {printBalance(n->left);cout << n->balance << " ";printBalance(n->right);}}//LDR遍历void LDR_order(AVLnode *n){if (n!=NULL){LDR_order(n->left);cout<<n->key<<" ";LDR_order(n->right);}}//递归的方式来删除,没有求平衡struct AVLnode * deleteElem(AVLnode *root, int elem){if(root == NULL)return root;//定位删除结点elem的位置else if(elem < root->key)root->left = deleteElem(root->left, elem);else if(elem > root->key)root->right = deleteElem(root->right, elem);else {    //如果删除结点elem没有子树,直接删除if(root->left == NULL && root->right == NULL){delete root;root = NULL;}//如果删除结点elem左子树为NULL(只有右子树),则原elem指针指向下一(右子树)结点,删除原elem元素else if(root->left == NULL){struct AVLnode *tmp = root;//删除结点elem被它的右子树取代root = root->right;//rebalancerebalance(root->parent);delete tmp;}//如果删除结点elem右子树为NULL(只有左子树),则原elem指针指向下一(左子树)结点,删除原elem元素else if(root->right == NULL){struct AVLnode *tmp = root;root = root->left;rebalance(root->parent);delete tmp;}//删除结点elem有左、右子树else {    //查找右子树的最小值struct AVLnode *tmp = findMin(root->right);//struct bstNode *tmp = findMax(root->left);root->key = tmp->key;//这里“很巧妙”,递归查找该删除的元素tmp->data,此时的tmp->data是没有子树的结点,将会被直接删除root->right = deleteElem(root->right, tmp->key);}}return root;}     //找最小值,最小值一定在左子树,且它的左子树为空AVLnode* findMin(AVLnode* root) {static AVLnode* pre_node;if(root == NULL) {return pre_node;}else {pre_node = root;return findMin(root->left);}}  }; int main(void){     AVLtree  tree;    int array[] = {16,3,7,11,9,26,18,14,15,17};    for (int i = 0; i <10; ++i)        tree.insert(array[i]);     tree.printBalance();    tree.LDR_order();    tree.level_traverse();    tree.deleteKey(11);    tree.LDR_order();    tree.level_traverse();    tree.deleteElem(9);    tree.LDR_order();    tree.level_traverse();}


0 0