AVL树



大概学习了一下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();		
}


你可能感兴趣的:(AVL树)