大概学习了一下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 使用递归的方法,参考自之前总结的BST void 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右子树)父结点指向a a->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; //rebalance rebalance(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(); }