zoukankan      html  css  js  c++  java
  • 品味C++实现AVL树的删除操作

    最近在写数据结构课设,基于字典树,avl树,pat树(压缩字典树),哈希表写个英汉词典

    写完后会开源, 可以期待一波

    分享一些饶有趣味的感悟hhh


    AVL树的删除操作要虽比插入复杂一点,不过思想很值得揣摩

    抛开细节,如果真的找到了那个要删除的节点,问题就转化为,如何使删除完的树继续平衡呢,利用二叉排序树的特点——左子树比根小,右子树比根大  , 找到左子树中的最大值或者右子树中的最小值来替换他, 因为在局部子树的最值节点,都是在边缘地带,牵扯的鸡毛蒜皮之事都远小于拖家带口的节点

    ❓那么, 该选左子树中的最大值还是右子树中的最小值呢?随便都可以吗

    答案是否定的, 不要因小失大, 小——删除一个节点;大——整棵自平衡二叉查找树的平衡性

    我们可以将情况进一步细化

    • 若该节点同时有左右子树.  那样我们就需要比较它左右子树的高度,选择高的那一方,取其最大/最小节点进行替代, 同时向下递归, 在左子树/右子树中删除那个最大/最小节点, 很奇妙, 一次简单的选择具有"柳暗花明又一村"的效果
    • 若该节点至少一棵子树为空, 我们就可以直接用其左子树.右子树的根来替代

    另一方面,如果当前没找到那个要删除的节点, 就需要根据与目标的大小相比较,进而选择左/右子树走下去(递归实现), 当回溯回来的时候,可能已经按上面的那种情况实现了删除, 那样就需要判断是否失衡, 此刻的左右子树高度差是否为二(即失衡), 接下来的问题就回归到树的旋转上去了,上一篇博文有所提及

    C++实现AVL树的四种旋转_☆迷茫狗子的秘密基地☆-CSDN博客结构template<typename T>struct AVLNode{ T data; int height; AVLNode* lchild, *rchild; AVLNode(T dt, AVLNode* l, AVLNode* r):data(dt),lchild(l),rchild(r){}};template<typename T>class AVLTree{ public: AVLTree()..https://blog.csdn.net/qq_39391544/article/details/121688941?spm=1001.2014.3001.5501

    AVLNode<PII>* AVLTree::MaxNode(AVLNode<PII>* pRoot)
    {
    	if(pRoot == nullptr) return nullptr;
    	while(pRoot->rchild != nullptr)
    	{
    		pRoot = pRoot->rchild;
    	}
    	return pRoot;
    }
    
    AVLNode<PII>* AVLTree::MinNode(AVLNode<PII>* pRoot)
    {
    	if(pRoot == nullptr) return nullptr;
    	while(pRoot->lchild != nullptr)
    	{
    		pRoot = pRoot->lchild;
    	}
    	return pRoot;
    }
    
    void AVLTree::DeleteWord(string word)
    {
    	root = _deleteWord(root, word);
    }
    
    AVLNode<PII>* AVLTree::_deleteWord(AVLNode<PII>* pRoot, string word)
    {
    	if(pRoot == nullptr){
    		//cout << "不存在" << word << endl;
    		return nullptr;
    	}
    
    	//找到对应值 
    	if(pRoot->data.first == word)
    	{
    		// 如果同时存在左右子树,则根据高度选择更换左子树最大节点或右子树最小节点
    		if(pRoot->lchild != nullptr && pRoot->rchild != nullptr)
    		{
    			if(GetH(pRoot->lchild) > GetH(pRoot->rchild))
    			{
    				
    				AVLNode<PII>* left_max = MaxNode(pRoot->lchild);			// 用左子树的最大节点替代当前节点
    				
    				pRoot->data = left_max->data;				// 当前分支情况left_max不可能为nullptr,可以直接覆盖data 
    				
    				pRoot->lchild =  _deleteWord(pRoot->lchild, left_max->data.first);	// 转移矛盾为删除左子树的最大节点
    				
    			}else{
    				
    				AVLNode<PII>* right_min = MinNode(pRoot->rchild);			// 用右子树的最小节点替代当前节点
    				
    				pRoot->data = right_min->data;	// 当前分支情况right_min不可能为nullptr,可以直接覆盖data 
    				
    				pRoot->lchild =  _deleteWord(pRoot->lchild, right_min->data.first);	// 转移矛盾为删除左子树的最大节点
    			}
    		}
    		// 至少一个子树为空 
    		else{
    			AVLNode<PII>* p = pRoot;
    			if(pRoot->lchild != nullptr)
    				pRoot = pRoot->lchild;
    			else if(pRoot->rchild != nullptr)
    				pRoot = pRoot->rchild;
    				
    			delete p;
    			cout << "成功删除" << word << endl;
    			return nullptr;
    		}
    		
    	}
    	else if(pRoot->data.first > word)
    	{
    		pRoot->lchild = _deleteWord(pRoot->lchild, word);
    		// 若是处理左子树完后失衡,则对右子树进行旋转变换 
    		if(GetH(pRoot->rchild) - GetH(pRoot->lchild) == 2)
    		{
    			if(GetH(pRoot->rchild->lchild) > GetH(pRoot->rchild->rchild))
    				pRoot = RL_Rotation(pRoot);
    			else
    				pRoot = Left_Rotation(pRoot);
    		}
    	}
    	else if(pRoot->data.first < word)
    	{
    		pRoot->rchild = _deleteWord(pRoot->rchild, word);
    		// 若是处理右子树完后失衡,则对左子树进行旋转变换
    		if(GetH(pRoot->lchild) - GetH(pRoot->rchild) == 2)
    		{
    			if(GetH(pRoot->lchild->rchild) > GetH(pRoot->lchild->lchild))
    				pRoot = LR_Rotation(pRoot);
    			else 
    				pRoot = Right_Rotation(pRoot);
    		}
    	}
    	
    	return pRoot;
    }

  • 相关阅读:
    [Linux 004]——用户和用户组以及 Linux 权限管理(二)
    [Linux 003]——用户和用户组以及 Linux 权限管理(一)
    [Linux 002]——Linux的常用命令
    [Linux 001]——计算机和操作系统的基础知识
    给 Android 开发者的 RxJava 详解
    Mac OSX系统搭建React natvie for android 开发环境
    Java中的堆和栈的区别
    Nginx配置详解
    在博客园安家了!
    J2SE核心实战开发—— 集合类框架
  • 原文地址:https://www.cnblogs.com/Knight02/p/15799010.html
Copyright © 2011-2022 走看看