对于AVL的插入和删除,主要利用的就是上篇文章所述的四种旋转操作,根据插入后不同的结构选用不同的方式复原平衡。

再次声明一下,http://www.cnblogs.com/QG-whz/p/5167238.html这篇文章讲解的比较好,我也是从链接处的大神那学习的,这里只用来复习。

首先对于插入操作,有以下几个步骤:

步骤1:根据二叉树的性质:大的向右找,小的向左找。逐个比较要插入的值和节点的值的大小关系,找到应该插入的位置。

步骤2:在插入的位置新申请一个节点,并返回该节点。

步骤3:判断是否出现不平衡的情况,通过旋转操作恢复平衡。

其次明确插入函数的实现方式:利用递归,假设当前结点为pnode,要插入的值为key。

步骤1:比较权值

如果key > pnode->key。那么执行insert(pnode->rightChild);

如果key < pnode->key。那么执行insert(pnode->leftChild);

步骤2:处理不平衡

如果在右子树中插入,则可能会需要左旋操作或者先右旋后左旋操作。

如果在左子树种插入,则可能会需要右旋操作或者先左旋后右旋操作。

然后应该明确插入操作的参数和其返回值:

既然使用了递归,很明显参数应该是当前节点pnode和要插入的值key。理解为在以节点pnode为根节点的子树中插入新节点,该节点的权值为key。

返回值是当前子树的根节点。

所以插入操作的大体结构应该长这个样子:

template<class T>
AVLTreeNode<T>* AVLTree<T>::insert(AVLTreeNode<T>* &pnode, const T& key)
{if(pnode == NULL){pnode = new AVLTreeNode<T>(key);}else{if(key > pnode->key){pnode->rightChild = insert(pnode->rightChild, key);if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(key > pnode->rightChild->key)pnode = leftRotation(pnode);else if(key < pnode->rightChild->key)pnode = rightLeftRotation(pnode);}}else if(key < pnode->key){pnode->leftChild = insert(pnode->leftChild, key);if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(key < pnode->leftChild->key)pnode = rightRotation(pnode);else if(key > pnode->leftChild->key)pnode = leftRightRotation(pnode);}}}return pnode;
}

接下来是删除操作,删除操作和插入都是利用了递归的思想。

首先思路如下:

步骤1:根据二叉树的定义,找到要删除的节点。该节点的key值和参数key相等。

步骤2:比较待删除的节点的左右子树的高度。

如果左子树的高度大,则将左子树中key值最大的节点移动到待删除的节点处。

如果右子树的高度大,则将右子树种key值最小的节点移动到待删除的节点处。

其次明确删除操作的实现方式:利用递归,假设当前结点为pnode,要删除的节点权值应为key。

步骤1:比较权值。

如果key > pnode->key,那么执行remove(pnode->rightChild, key);

如果key < pnode->key,那么执行remove(pnode->leftChild, key);

如果key == pnode->key,那么删除该节点。

删除操作

步骤1:

如果height(pnode->leftChild) > height(pnode->rightChild),那么寻找其左子树中key值最大的那个节点plnode。

如果height(pnode->leftChild) < height(pnode->rightChild),那么寻找其右子树中key值最小的那个节点prnode。

步骤2:

若在左子树中查找,则令pnode->key = plnode->key,随后利用递归在pnode的左子树中删除plnode节点,即remove(pnode->leftChild, plnode->key)。

若在右子树中查找,则令pnode->key = prnode->key,随后利用递归在pnode的右子树中删除prnode节点,即remove(pnode->rightChild, prnode->key)。

然后应该明确删除操作的参数和其返回值。

参数应为当前结点pnode和要删除的节点的值key。理解为在以节点pnode为根节点的子树中删除权值为key的节点。

返回值为当前子树的根节点pnode。

所以删除操作大体上长这个样子:

template<class T>
AVLTreeNode<T>* AVLTree<T>::remove(AVLTreeNode<T>* &pnode, const T& key)
{if(pnode != NULL){if(pnode->key == key){if(pnode->leftChild != NULL && pnode->rightChild != NULL){           if(height(pnode->leftChild) > height(pnode->rightChild)){AVLTreeNode<T>* plnode = maximun(pnode->leftChild);pnode->key = plnode->key;pnode->leftChild = remove(pnode->leftChild, plnode->key);}else{AVLTreeNode<T>* prnode = minimun(pnode->rightChild);pnode->key = prnode->key;pnode->rightChild = remove(pnode->rightChild, prnode->key);}}else{AVLTreeNode<T>* temp = pnode;if(pnode->leftChild != NULL)pnode = pnode->leftChild;else if(pnode->rightChild != NULL)pnode = pnode->rightChild;delete temp;return pnode;}}else if(key > pnode->key){pnode->rightChild = remove(pnode->rightChild, key);if(height(pnode->leftChild) - height(pnode->rightChild) == 2){if(height(pnode->leftChild->leftChild) > height(pnode->leftChild->rightChild))pnode = rightRotation(pnode);elsepnode = leftRightRotation(pnode);}}else{pnode->leftChild = remove(pnode->leftChild, key);if(height(pnode->rightChild) - height(pnode->leftChild) == 2){if(height(pnode->rightChild->rightChild) > height(pnode->rightChild->leftChild))pnode = leftRotation(pnode);elsepnode = rightLeftRotation(pnode);}}return pnode;}return NULL;
}

在没有找到要删除的节点的时候,需要利用递归再次调用删除函数。

又因为以待删除的那个节点为根节点的子树中,替换掉的节点是高度大的一方,所以这个子树的平衡不会被破坏。

但是因为确实少了一个节点,高度很有可能会改变,所以其父节点的平衡很有可能就被破坏了,需要重新改变树的结构。

不过在判断需要什么样的旋转操作时与插入操作有点不同,它是比较当前节点的左节点的左节点和当前节点的左节点的右节点的高度差,或者是当前节点的右节点的左节点和当前节点的右节点的右节点的高度差。具体的实现可以参考上述代码。

下面说一下为什么插入操作和删除操作利用的是递归而不是迭代。

考虑一下,在插入或删除操作后需要进行左旋或者右旋来重塑树的结构,具体向哪个方向旋转是根据它在父节点的左边还是右边。同时在插入删除后,改变树的结构是从插入位置开始,或者从删除位置开始逐步向上进行重塑的。即应该先考虑当前父节点开始的子树结构是否需要重塑,完成后再考虑以父节点的父节点开始的子树结构是否需要重塑。

简单来说,就是从发生改变的那个节点到整个树的根节点的这条路上经过的所有节点都需要判断一下是否需要重塑。

所以如果利用迭代的话,那么每一个节点都需要记录,而且每一个节点是其父节点的左节点还是其右节点也需要记录。

但是如果利用递归的话,在递归的调用空间中,我们默认就记录下了这些节点,也记录下了每一个节点在其父节点的左边还是右边,所以利用递归实现起来是比较方便的。

数据结构-----AVL树的插入删除操作相关推荐

  1. 数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

    AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1). 也就是说,AVL树,本质上是带了平 ...

  2. c++《AVL树的概念》《AVL树的插入》《AVL树的旋转》《AVL树的验证》《AVL树的删除》《AVL树的性能》

    4.1 AVL树 4.1.1 AVL树的概念 二叉搜索树虽可以缩短查找的效率,**但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当 于在顺序表中搜索元素,效率低下.**因此,两位俄罗斯的 ...

  3. AVL树的 插入 和 删除

    AVL树的 插入 和 删除 文章目录 AVL树的 插入 和 删除 AVL的定义 AVL树节点的创建 AVL的插入 调整 插入的左旋 插入右旋 左右旋 右左旋 AVL的删除 前面写的 二叉搜索树(BST ...

  4. AVL树的插入与删除(详解)

    AVL树的插入与删除(详解) 平衡二叉树的定义就不在这里赘述了,平衡二叉树的插入与删除都是基于平衡二叉树的查找进行的.平衡二叉树的查找和二叉树的查找又是一样的. 插入的话,我们从平衡二叉树的根结点出发 ...

  5. AVL树的插入(C++实现)

    1. 概念 AVL树(Adelson-Velsky and Landis Tree)于1962年被提出,是计算机科学中最早被发明的平衡二叉查找树.AVL树得名于它的发明者G. M. Adelson-V ...

  6. 顺序表和单链表的插入删除操作时间复杂度的区别

    顺序表和单链表的插入删除操作时间复杂度的区别 最近在学习数据结构,看到如果需要用到大量的插入和删除操作,单链表的效率会高于顺序表.看到这里时内有有个疑惑,这两种数据结构的插入和删除操作的时间复杂度不都 ...

  7. 08_Python算法+数据结构笔记-二叉搜索树查询/删除-AVL树旋转/插入/应用-贪心算法

    b站视频:路飞IT学城 清华计算机博士带你学习Python算法+数据结构_哔哩哔哩_bilibili #71 二叉搜索树:查询 import randomclass BiTreeNode:def __ ...

  8. AVL树的插入_删除操作实现~

    AVL的插入操作:找到所插位置之后,往上寻找可能发生不平衡之处x,若找到x则调整平衡.顶多调整平衡1次. AVL的删除操作:找到删除结点x,类似于二叉查找树一样找到其后继或前驱y,若为x->ri ...

  9. 数据结构-----AVL树的旋转操作

    本文主要讲解AVL的旋转操作,供自己复习用,如有不对之处请指出.另外图片是从链接处的大神那复制的,感觉文章写的很好,可以去学习. http://www.cnblogs.com/QG-whz/p/516 ...

最新文章

  1. SOJ 4543 4542
  2. php证券k线图,php画K线图的一个工具
  3. 北京内推 | ​阿里达摩院智能计算实验室认知智能组招收多模态算法实习生
  4. java中mongodb中dao通用_Spring配置MongoDB及 构建通用Dao
  5. 九九乘法表编程上三角python_java语言打印上三角和下三角,进一步得到九九乘法表...
  6. 前端学习(2320):typeScript的概述和安装
  7. TypeError: 'numpy.ndarray' object is not callable
  8. 使用jq的toggle函数实现全选功能遇到的问题
  9. docker 基础操作
  10. 227 用栈模拟汉诺塔问题
  11. webpack配置路径问题 1
  12. python爬图mzitu_小白学 Python 爬虫(16):urllib 实战之爬取妹子图
  13. 阿里云盘 Mac客户端(附福利码)
  14. 澳洲CE毕业意向FullStackDeveloper
  15. JavaScript分号使用指南
  16. html css字体最小,网页字体大小的设计技巧
  17. Cannot add foreign key constraint 错误解决办法
  18. 广东工业大学化学工程考研情况
  19. 滥用exchage远程调用域管理员API接口
  20. 技术负责人如何搞定老板之我所见

热门文章

  1. url能访问但new file()找不到文件_Go Web编程给自己写的服务器添加错误和访问日志...
  2. mysql got signal 11_mysql bug : mysqld got signal 11
  3. c语言5的阶乘流程图_2020年,5种将死的编程语言!
  4. 程序员面试100题之十二:求数组中最长递增子序列
  5. Composer切换到Laravel-China 镜像
  6. 使用Amazon Simple Queue Service(SQS) 实现简单的消息服务
  7. 数据结构(十八)树的定义与存储结构
  8. win10系统下载-靠谱推荐
  9. Oracle中DUMP转储方法
  10. 深入分析Flex [Bindable] 【转】