算法--AVL树的删除

AVL 树删除

定义

AVL树是一棵二叉查找树,平衡因子为-110.若平衡因子为2就必须进行平衡化。平衡化的过程就是左旋或者右旋,分LL,LR,RR,RL4种情况。

性能:

高度为:log(n),在最坏的情况下要比较O(logn)次。与其它的平衡树不同,当一个结点出现不平衡,可能会影响整棵树。

过程,比如一个数组为5,6,8,3,2,4,7。长度为7。那么插入的情况是

《算法--AVL树的删除》

定义的头部为:

#define NULL    0
template<class T>
class AVL_Node//AVL树中节点结构
{
public:
	T key;
	AVL_Node<T>* leftChild;
	AVL_Node<T> *rightChild;
	AVL_Node<T> *parent;//为了平衡AVL树我们不得不加入父结点便于算法
	AVL_Node(T key)
	{
		this->key=key;
		leftChild=NULL;
		rightChild=NULL;
		parent=NULL;
	}
};

template<class T>
class AVL_Tree//AVL树的数据结构
{
public:
	int nodeNum;
	AVL_Node<T> *root;
	AVL_Tree()
	{
		nodeNum=0;
		root=NULL;
	}
	void AVL_TREE_INSERT(AVL_Tree<T>*avlTree,T key);//插入
	void AVL_TREE_DELETE(AVL_Tree<T>*avlTree,T key);//删除
	bool RotateRight(AVL_Tree<T>*avlTree,AVL_Node<T>*node);//右旋
	bool RotateLeft(AVL_Tree<T>*avlTree,AVL_Node<T>*node);//左旋
	int AVL_TREE_HIGH(AVL_Node<T>*node);//计算树的高度
	AVL_Node<T>* AVL_TREE_SEARCH(T key);
	void AVL_TREE_BALANCE(AVL_Tree<T>*avlTree,AVL_Node<T>* node);
};

删除的代码:

最关键的是查找到待删除的结点。这个结点可能是原来的结点,也可能是前驱结点

找到待删除结点的孩子结点,将指针正确的复制。

具体代码:

template<class T>//删除结点
void AVL_Tree<T>::AVL_TREE_DELETE(AVL_Tree<T>*avlTree,T key)
{
	//思想:1、找到这个结点
	//2、通过拷贝删除法。进行值的替换。
	//3、进行平衡化
	AVL_Node<T>*index=avlTree->root;
	AVL_Node<T>*delNodeChild=NULL;

	while (index)//找到这个结点,并且获得该结点的父结点
	{
		if (key==index->key)
		{
			break;
		}
		else if (index->leftChild&&key<index->key)
		{
			index=index->leftChild;
		}
		else if(index->rightChild&&key>index->key)
		{
			index=index->rightChild;
		}
	}
	AVL_Node<T>*delNode=index;//有可能这个就是叶子结点,进行直接删除。
	if (index->leftChild==NULL||index->rightChild==NULL)
	{
		delNode=index;
	}
	else  //若两棵子树都存在。
	{   //利用左子树中最大的结点进行替换,复制删除
		delNode=index->leftChild;
		while(delNode->rightChild)//找到待删除的结点,利用二叉树的中序的前驱。
		{
			delNode=delNode->rightChild;
		}
	}
   //下面主要是找的哦啊待删除结点的孩子结点,若是采用前驱找那么势必不会有右子树,但是如果整棵树只剩下右子树,
	//这时就必须用右子树来替换
	if (delNode->leftChild)
	{
		delNodeChild=delNode->leftChild;
	}
	if(delNode->rightChild)
	{
		delNodeChild=delNode->rightChild;
	}
	if (delNodeChild)//若deleteNode的父结点存在,将孩子结点父指针指向这个指针。
	{
		delNodeChild->parent=delNode->parent;
	}
	if (delNode->parent==NULL)//如果这个结点已经是跟结点,那么孩子结点也是没有赋值的,就等于将根结点设置为NULL
	{
		avlTree->root=delNodeChild;
	}
	else//设置删除结点孩子结点的父结点。若是这个孩子结点的值为空,那么父结点的孩子也是为空的,
	{   //这样在后面删除delNode时,虽然delete,但是指针值还是存在的,这是一个野指针,如此就可以设置值为空
		if (delNode==delNode->parent->leftChild)
		{
			delNode->parent->leftChild=delNodeChild;
		}
		else if (delNode==delNode->parent->rightChild)
		{
			delNode->parent->rightChild=delNodeChild;
		}
	}
	if (index!=delNode)//若是这个结点就是删除结点不需要进行交换
	{
		index->key=delNode->key;//对值进行交换。
	}
	if (nodeNum>2)//只有当数目大于2的时候才会有平衡化步骤
	{
		AVL_TREE_BALANCE(avlTree,delNode->parent);//待平衡化的结点为待删除节点的父结点
	}
	//delete delNode;
	nodeNum--;	
}


int main()
{
	AVL_Tree<int>*avlTree=new AVL_Tree<int>();
	int testArray[7]={5,6,8,3,2,4,7};
	for (int i=0;i<7;i++)
	{
		avlTree->AVL_TREE_INSERT(avlTree,testArray[i]);

	}
	for (int i=0;i<7;i++)
	{
		avlTree->AVL_TREE_DELETE(avlTree,testArray[i]);
	}
	return 0;
}

平衡化的代码见前面的插入代码中

小结:

1、考虑的必须有可能待删除结点的孩子结点为空,这个时候父亲结点的孩子结点就是为NULL

2、待删除结点的父亲结点为空。那么孩子结点为根结点

3、平衡结点为父结点依旧要考虑当前只有一个结点的情况

4、虽然使用的前驱作为待删除结点,但是会出现只有右子树的情况,所以待删除结点中若左子树不存在,它指向的应该是右子树


最开始的时候使用一个prev的指针,并没有使用delNode,发现要考虑的情况更多

首当其冲的是野指针,没有delNode,指针指向的空间已经被删除,但是指针的值是存在的,如下面的代码中

          int * pp = new int;
	   int *ppp=pp;
           *pp = 16;
	   int *&p=pp;
	   delete p;
	   p=NULL;

这个时候将p设置为空,不存在野指针,但是pp呢?所以,*&ppp=pp,才能正确的删除。其次还有设置为Null

这就是只有指针与指针引用的不同。但是在我们的程序中,因为我们只是得到指针A中的值,虽然可以将所指向空间的内容清空,但是A中的值并没有为空。这个时候A就是野指针。

    原文作者:AVL树
    原文地址: https://blog.csdn.net/tang_jin2015/article/details/8616873
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞