数据结构-----AVL树的旋转操作
来源:互联网 发布:威科姆网络机顶盒 编辑:程序博客网 时间:2024/05/18 09:32
本文主要讲解AVL的旋转操作,供自己复习用,如有不对之处请指出。另外图片是从链接处的大神那复制的,感觉文章写的很好,可以去学习。
http://www.cnblogs.com/QG-whz/p/5167238.html具体内容可以参考链接处的讲解。
AVL树定义:一颗空的二叉树是AVL树;如果T是一棵非空的二叉树,TL和TR分别是其左子树和右子树,那么当T满足以下条件时,T是一棵AVL树:
1.TL和TR是AVL树;
2.|hl - hr| <= 1,其中hl和hr分别是TL和TR的高。
从定义可以知道,对于一个AVL树来说,左右节点的高度差只能取1, 0, -1三个值。而只有我们向树中插入一个节点或者从树中删除一个节点后,树的某些节点的高度差才会改变。也就是说,在插入和删除操作后,某些节点的左右子树的高度差可能不再是上述三个值中的任何一个。此时就需要改变这些节点的位置,来让其满足AVL树的定义。
完成一次插入或删除操作后,根据子树结构的不同,可以分成左旋,右旋,先左旋再右旋,先右旋再左旋四中方法来重构这个子树。本文以插入操作为例。
1.左旋,适用场景:插入操作完成后,插入的节点是某个节点的右节点的右节点。
如图所示,假设一段子树结构中,根节点为4,右节点为5。此时如果我们插入6,根据二叉树的定义,会将6插入在5的右节点。就像上图中间那样。我们称插入的节点是根节点的右节点的右节点。
但是插入之后,对于根节点4来说,它的左节点的高度为0,右节点的高度为2,高度差不满足AVL树的定义,需要调增这些节点的位置。调整的方式就是以中间的节点为轴,向左方向旋转。即在调整之后,5是这个子结构的根节点,4是5的左节点,6是5的右节点。而5原先的左节点变成如今4的右节点(图中没有展示这个)
为了实现左旋操作,我们定义一个leftRotation函数,其参数为子结构的根节点,返回值为调整后这个子结构的根节点。
好了,接下来我们可以写出左旋函数的代码了
template<class T>AVLTreeNode<T>* AVLTree<T>::leftRotation(AVLTreeNode<T>* pnode){AVLTreeNode<T>* prnode = pnode->rightChild;pnode->rightChild = prnode->leftChild;prnode->leftChild = pnode;pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1;prnode->height = max(height(prnode->leftChild), height(prnode->rightChild)) + 1;return prnode;}
代码中更新了节点高度,正如刚才提到过的,节点的位置发生改变,所以需要改变节点的高度。但是要注意先更新pnode的高度,再更新prnode的高度,因为pnode是prnode的子树。
但是为什么只需要改变节点4和节点5的高度呢?
这里说一下节点高度的概念:从该节点开始往下走,直到走到节点为NULL时停止,可以有很多条路,一条路上的节点个数为一个高度,所有的高度中最大的那个就是该节点的高度。
因为其他节点的左右子树都没有发生改变,所以高度不会发生变化。
用于左旋的插入是:插入的节点是根节点的右节点的右节点。
根节点的右节点的高度减去其左节点的高度为2,此时就需要左旋。调用的代码就像下面这样:
template<class T>AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key){...if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(pnode->rightChild->key < key)pnode = leftRotation(pnode)...}...}
因为pnode是一个引用,比如是ppnode->leftChild的引用,调用pnode = leftRotation(pnode)后,pnode改变也就导致ppnode->leftChild的改变。
2.右旋,使用场景:插入操作完成后,插入的节点是某个节点的左节点的左节点。
右旋和左旋是完全相反的两个过程。如上图,子树的根节点是4,其左节点是3,插入的2是4的左节点3的左节点。此时判断是否需要旋转只要比较4的左节点的高度减去其右节点的高度是否为2就可以了。代码和左旋类似:
template<class T>AVLTreeNode<T>* AVLTree<T>::rightRotation(AVLTreeNode<T>* pnode){AVLTreeNode<T>* plnode = pnode->leftChild;pnode->leftChild = plnode->rightChild;plnode->rightChild = pnode;pnode->height = max(height(pnode->leftChild), height(pnode->rightChild)) + 1 ;plnode->height = max(height(plnode->leftChild), height(plnode->rightChild)) + 1;return plnode;}同样也需要更新节点的高度,但是更新高度应该先更新pnode,再更新plnode,因为此时plnode才是这个子树的根节点,它的高度需要借助其左右节点来计算,会用到pnode的高度。
使用代码的情况如左旋一样:
template<class T>AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key){...if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(pnode->leftChild->key > key)pnode = rightRotation(pnode)...}...}
3.先左旋后右旋,使用场景:插入操作完成后,插入的节点是某个节点的左节点的右节点。
这种情况,应该先对以0为根节点的子结构进行左旋操作,效果如上图第二行中间,然后再去以2为根节点的子结构进行右旋操作。
因为已经定义了左旋和右旋操作,所以左右旋和右左旋都比较好实现:
template<class T>AVLTreeNode<T>* AVLTree<T>::leftRightRotation(AVLTreeNode<T>* pnode){pnode->leftChild = leftRotation(pnode->leftChild);return rightRotation(pnode);}调用的情况和右旋对应:
template<class T>AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key){...if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(pnode->leftChild->key > key)pnode = rightRotation(pnode)else if(pnode->leftChild->key < key)pnode = leftRightRotation(pnode);...}...}
4.先右旋后左旋,使用场景:插入操作完成后,插入的节点是某个节点的右节点的左节点。
这种情况,应该先对以8为根节点的子结构进行右旋操作,效果如第二行中间的图所示,再对以6为根节点的子结构进行左旋操作。
代码如下:
template<class T>AVLTreeNode<T>* AVLTree<T>::rightLeftRotation(AVLTreeNode<T>* pnode){pnode->rightChild = rightRotation(pnode->rightChild);return leftRotation(pnode);}
调用的方式和左旋对应:
template<class T>AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key){...if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(pnode->rightChild->key < key)pnode = rightRotation(pnode)else if(pnode->rightChild->key > key)pnode = rightLeftRotation(pnode);...}...}
- 数据结构-----AVL树的旋转操作
- AVL树旋转的操作
- AVL树的旋转操作
- AVL树的旋转操作
- AVL树的旋转操作
- 【数据结构复习】AVL树的旋转
- [数据结构与算法]AVL树的旋转
- 【数据结构】AVL树的旋转和插入
- 【AVL树】AVL树的插入操作以及旋转
- AVL树的旋转,插入,删除操作
- AVL树的旋转操作详解
- AVL树的旋转与插入操作
- AVL树的旋转
- AVL树的旋转
- AVL树的旋转
- AVL树的旋转
- AVL树的旋转
- AVL树的旋转
- 关于CSDN上转载别人博客的一个方法
- oracle违反唯一约束的错误
- 16.Observer-观察者模式
- 17.Iterator-迭代器模式
- mvc
- 数据结构-----AVL树的旋转操作
- html5本地数据库
- 18.ChainOfResponsibility-职责链模式
- 19.Command-命令模式
- 20.Memento-备忘录模式
- 给View设置selector无效的解决办法。
- HBASE笔记
- Kotlin+Anko 你应该尝试的新Android开发方式
- MFC基于对话框上插入MENU菜单栏并点击菜单弹出新窗口