算法整理:查找篇-二叉排序树&&AVL树

二叉排序树

满足一下三个性质的二叉树称为二叉排序树:

  1. 若左子树不空,左子树上所有节点的值均小于根节点的值;
  2. 若右子树不空,右子树上所有节点的值均大于根节点的值;
  3. 左子树和右子树也为二叉排序树

在这里插入图片描述
二叉排序树上的查找
在二叉排序树上进行查找非常简单,从根节点开始,进行比较,如果关键字相等,则查找成功,反之根据带查找元素与更节点元素的大小关系继续向下比较。
另外,二叉排序树上进行查找的代价与 l o g n logn logn成正比。

二叉排序树上的插入
在二叉排序树上,每次插入的节点都是新的叶子节点,所以在进行插入操作时不需要移动其他节点。
在这里插入图片描述

中序遍历二叉排序树可得到一个关键字的有序序列。也就是说一个无序序列可通过构造二叉排序树变成一个有序序列。构造树的过程即对无序序列进行排序的过程。

二叉排序树上的删除
二叉排序树上节点的删除相对来说复杂了一些,需要分成3种情况:

  1. 如果被删除节点是叶子节点,直接删除即可
  2. 如果被删除节点只有左子树或右子树,则需要使双亲结点指向被删除节点的左子树或右子树
  3. 如果被删除节点的左右子树都不为空,则有两种方式:第一种是使双亲结点指向被删除节点的左子树,并令被删除节点的右子树成为被删除节点的左子树中最大节点的右子树。第二种是用被删除节点左子树中最大的节点的值替换被删除节点的值,然后删掉左子树中最大的节点。

C++代码

bool SearchBST(BiTree* tree, int value, BiTree** parent = NULL);
BiTree* InsertBST(BiTree* tree, int value);

struct BiTree {
	int data;
	BiTree* left;
	BiTree* right;
	BiTree() :data(0), left(NULL), right(NULL) {};
	~BiTree() {
		if (left != NULL)delete left;
		if (right != NULL)delete right;
	}
};

//创建
BiTree* CreateBST(vector<int> list) {
	BiTree* tree = NULL;
	for (auto item : list) {
		tree = InsertBST(tree, item);
	}
	return tree;
}

//插入
BiTree* InsertBST(BiTree* tree, int value) {
	if (tree == NULL) {
		tree = new BiTree();
		tree->data = value;
	}
	else {
		BiTree* parent = NULL;
		BiTree** pointerToParent = &parent;
		if (!SearchBST(tree, value, pointerToParent)) {
			if (parent->data > value) {
				parent->left = new BiTree();
				parent->left->data = value;
			}
			else {
				parent->right = new BiTree();
				parent->right->data = value;
			}
		}
		else {
			cout << "节点已插入" << endl;
		}
	}
	return tree;
}

//搜索
bool SearchBST(BiTree* tree, int value, BiTree** pointerToParent) {
	if (tree == NULL)return false;
	if (tree->data == value)return true;
	if (pointerToParent != NULL)*pointerToParent = tree;
	if (tree->data > value)
	{
		return SearchBST(tree->left, value, pointerToParent);
	}
	else
	{
		return SearchBST(tree->right, value, pointerToParent);
	}
}

//删除
bool DeleteBST(BiTree** tree, int value) {
	BiTree* parent = NULL;
	BiTree** pointerToParent = tree;
	BiTree** pointerToChild;
	BiTree* node;
	if (SearchBST(*tree, value, pointerToParent)) {
		if (parent == NULL) {
			pointerToChild = tree;
			node = *tree;
		}
		else if (parent->left != NULL && parent->left->data == value) {
			pointerToChild = &(parent->left);
			node = parent->left;
		}
		else {
			pointerToChild = &(parent->right);
			node = parent->right;
		}

		if (node->left == NULL && node->right == NULL) {
			*pointerToChild = NULL;
			delete node;
		}
		else if (node->left == NULL && node->right != NULL) {
			*pointerToChild = node->right;
			node->right = NULL;
			delete node;
		}
		else if (node->left != NULL && node->right == NULL) {
			*pointerToChild = node->left;
			node->left = NULL;
			delete node;
		}
		else {
			BiTree* temp = node->left;
			pointerToChild = &(node->left);
			while (temp->right != NULL)
			{
				pointerToChild = &(temp->right);
				temp = temp->right;
			}
			node->data = temp->data;
			*pointerToChild = temp->left;
			temp->left = NULL;
			delete temp;
		}
		return true;
	}
	else {
		cout << "不存在这个节点" << endl;
		return false;
	}
}

平衡二叉树

对于相同的数据,可以构造不同的二叉排序树,而不同的二叉排序树的深度也不同,如下图所示。
在这里插入图片描述
图a是一棵比较平衡的二叉树,其平均查找长度与折半查找相同;而图b则是一棵极度不平衡的单支树,其平均查找长度与顺序查找相同。
事实上,当原数据基本有序的情况下,构造的二叉排序树会很不平衡,十分影响查找效率,因此人们提出了高度平衡的二叉树——平衡二叉树

平衡二叉树又称为AVL树,它要么是一棵空树,要么左子树和右子树的深度差不大于1,且左子树和右子树都是平衡二叉树。

通常将平衡二叉树左子树和右子树的深度差定义为平衡因子,二叉树上所有节点的平衡因子只能为-1、0、1,如果某一个节点的平衡因子大于1,则该二叉树就是不平衡的。

下图中a为平衡二叉树,b则不是平衡二叉树,其节点内为平衡因子的值。
在这里插入图片描述

AVL树上的查找
AVL树的查找与二叉排序树排序树相同,在此不再赘述。但是由于AVL树是高度平衡的,所以它的平均查找时间复杂度为 O ( l o g n ) O(logn) O(logn)

AVL树上的插入
在AVL树上插入节点并不像二叉排序树上那么简单,在插入一个新的叶子节点后,AVL树的平衡可能会被打破,此时需要对树的结构进行调整。调整的步骤如下:

  • 首先找到因插入新节点而失去平衡的最小子树的根节点,将其称之为A
  • 然后根据插入节点的位置,分为四种情况调整:
    1. 单向右旋:设A的左子树的根节点为B,当新插入的节点在B的左子树上时,需要对A、B进行一次顺时针的旋转操作:在这里插入图片描述

    2. 单向左旋:设A的右子树的根节点为B,当新插入的节点在B的右子树上时,需要对A、B进行一次逆时针的旋转操作:在这里插入图片描述

    3. 双向旋转-先左后右:设A的左子树的根节点为B,B的右子树的根节点为C,如果新插入的节点在B的右子树上,则需要先对B、C进行一次逆时针旋转,然后对C、A进行一次顺时针旋转:在这里插入图片描述

    4. 双向旋转-先右后左:设A的右子树的根节点为B,B的左子树的根节点为C,如果新插入的节点在B的左子树上,则需要先对B、C进行一次順时针旋转,然后对C、A进行一次逆时针旋转:在这里插入图片描述

AVL树上的删除
在AVL树上删除节点,设被删除的节点为M,步骤如下:

  1. 首先执行上面提过的二叉排序树的删除操作
  2. 检查AVL树是否失衡,如果失衡则需要从M开始,向上移动找到第一个不平衡的节点A(即失去平衡的最小子树的根节点)
  3. 如果未找到A,说明AVL树的平衡没有改变,算法结束,否则,A的平衡因子应为2或-2。
    如果A的平衡因子为2,设B是A的左孩子,则有以下三种情况:
    (1) B的平衡因子为1,则执行类似于插入操作中单向右旋的操作,需要对A、B进行一次顺时针的旋转操作,旋转完成之后,B和A的平衡因子都为0。
    (2) B的平衡因子为0,则执行类似于插入操作中单向右旋的操作,需要对A、B进行一次顺时针的旋转操作,旋转完成之后,B的平衡因子为-1,A的平衡因子为1。
    (3) B的平衡因子为-1,C是B的右孩子,则执行类似于插入操作中先左后右旋转的操作,需要对B、C进行一次逆时针的旋转操作,然后对C、A进行一次顺时针的旋转操作,旋转完成之后,C的平衡因子为0,A、B的平衡因子由旋转前C的子树的结构决定。
    同样,如果A的平衡因子为-2,设B是A的右孩子,也有三种情况:
    (1) B的平衡因子为1,C是B的左孩子,则执行类似于插入操作中先右后左旋转的操作,需要对B、C进行一次順时针的旋转操作,然后对C、A进行一次逆时针的旋转操作,旋转完成之后,C的平衡因子为0,A、B的平衡因子由旋转前C的子树的结构决定。
    (2) B的平衡因子为0,则执行类似于插入操作中单向左旋的操作,需要对A、B进行一次逆时针的旋转操作,旋转完成之后,B的平衡因子为1,A的平衡因子为-1。
    (3) B的平衡因子为-1,则执行类似于插入操作中单向左旋的操作,需要对A、B进行一次逆时针的旋转操作,旋转完成之后,B和A的平衡因子都为0。
    (此处懒得画图了,详情参照博客AVL树的插入与删除(详解)

C++代码

//各种函数
void R_Rotate(AVLTree** p);//左旋
void L_Rotate(AVLTree** p);//右旋
void Left_Balance(AVLTree** p);//插入节点后调整 适用于LL LR型
void Right_Balance(AVLTree** p);//插入节点后调整 适用于RR RL型
bool InsertAVL(AVLTree** tree, int value, bool& taller);//插入节点
bool SearchAVL(AVLTree* tree, int value, AVLTree** pointerToParent = NULL);//查找
int getTreeHeight(AVLTree* tree);//得到树高
int getTreeHeight_R(AVLTree* tree, int height);//得到树高,递归时使用
void Right_Balance_Delete(AVLTree** p);//删除节点后调整  最小不平衡子树的平衡因子为2时使用
void Left_Balance_Delete(AVLTree** p);//删除节点后调整  最小不平衡子树的平衡因子为-2时使用
bool DeleteAVL(AVLTree** tree, int value, bool &lower);//删除节点


//AVL树结构
struct AVLTree {
	int data;
	int factor;
	AVLTree* left;
	AVLTree* right;

	AVLTree() :data(0), factor(0), left(NULL), right(NULL) {};
	~AVLTree() {
		if (left != NULL)delete left;
		if (right != NULL)delete right;
	}
};

//结构调整
void R_Rotate(AVLTree** p) {
	AVLTree* temp = (*p)->left;
	(*p)->left = temp->right;
	temp->right = (*p);
	(*p) = temp;
}

void L_Rotate(AVLTree** p) {
	AVLTree* temp = (*p)->right;
	(*p)->right = temp->left;
	temp->left = (*p);
	(*p) = temp;
}

void Left_Balance(AVLTree** p) {
	AVLTree* temp = (*p)->left;
	if (temp->factor == 1) {
		(*p)->factor = 0;
		temp->factor = 0;
		R_Rotate(p);
	}
	else if (temp->factor == -1) {
		AVLTree* temp_r = temp->right;
		if (temp_r->factor == -1) {
			(*p)->factor = 0;
			temp->factor = 1;
		}
		else if (temp_r->factor == 1) {
			(*p)->factor = -1;
			temp->factor = 0;
		}
		else {
			(*p)->factor = 0;
			temp->factor = 0;
		}
		temp_r->factor = 0;
		L_Rotate(&((*p)->left));
		R_Rotate(p);
	}
}

void Right_Balance(AVLTree** p) {
	AVLTree* temp = (*p)->right;
	if (temp->factor == -1) {
		(*p)->factor = 0;
		temp->factor = 0;
		L_Rotate(p);
	}
	else if (temp->factor == 1) {
		AVLTree* temp_l = temp->left;
		if (temp_l->factor == 1) {
			(*p)->factor = 0;
			temp->factor = -1;
		}
		else if (temp_l->factor == -1) {
			(*p)->factor = 1;
			temp->factor = 0;
		}
		else {
			(*p)->factor = 0;
			temp->factor = 0;
		}
		temp_l->factor = 0;
		R_Rotate(&((*p)->right));
		L_Rotate(p);
	}
}

void Left_Balance_Delete(AVLTree** p) {
	AVLTree* temp = (*p)->right;
	if (temp->factor == 1) {
		AVLTree* temp_r = temp->left;
		if (temp_r->factor == -1) {
			(*p)->factor = 0;
			temp->factor = 1;
		}
		else if (temp_r->factor == 1) {
			(*p)->factor = -1;
			temp->factor = 0;
		}
		else {
			(*p)->factor = 0;
			temp->factor = 0;
		}
		temp_r->factor = 0;

		R_Rotate(&((*p)->right));
		L_Rotate(p);
	}
	else if (temp->factor == 0){
		(*p)->factor = -1;
		temp->factor = 1;
		L_Rotate(p);
	}
	else {
		(*p)->factor = 0;
		temp->factor = 0;
		L_Rotate(p);
	}
}

void Right_Balance_Delete(AVLTree** p) {
	AVLTree* temp = (*p)->left;
	if (temp->factor == 1) {
		(*p)->factor = 0;
		temp->factor = 0;
		R_Rotate(p);
	}
	else if (temp->factor == 0) {
		(*p)->factor = -1;
		temp->factor = 1;
		R_Rotate(p);
	}
	else {
		AVLTree* temp_r = temp->right;
		if (temp_r->factor == -1) {
			(*p)->factor = 1;
			temp->factor = 0;
		}
		else if (temp_r->factor == 1) {
			(*p)->factor = 0;
			temp->factor = -1;
		}
		else {
			(*p)->factor = 0;
			temp->factor = 0;
		}
		temp_r->factor = 0;

		L_Rotate(&((*p)->left));
		R_Rotate(p);
	}
}

//获得树高
int getTreeHeight_R(AVLTree* tree, int height);
int getTreeHeight(AVLTree* tree) {
	int height = 0;
	if (tree == NULL) return height;
	height++;
	int temp1 = 0, temp2 = 0;
	temp1 = getTreeHeight_R(tree->left, height);
	temp2 = getTreeHeight_R(tree->right, height);
	height = temp1 > temp2 ? temp1 : temp2;
	return height;
}

int getTreeHeight_R(AVLTree* tree, int height) {
	if (tree == NULL) return height;
	height++;
	int temp1 = 0, temp2 = 0;
	temp1 = getTreeHeight_R(tree->left, height);
	temp2 = getTreeHeight_R(tree->right, height);
	height = temp1 > temp2 ? temp1 : temp2;
	return height;
}

//插入
bool InsertAVL(AVLTree** tree, int value, bool& taller) {
	if (*tree == NULL) {
		*tree = new AVLTree();
		(*tree)->data = value;
		taller = true;
	}
	else {
		if ((*tree)->data > value) {
			if (InsertAVL(&((*tree)->left), value, taller)) {
				if (taller) {
					if ((*tree)->factor == 0) {
						(*tree)->factor = 1;
						taller = true;
					}
					else if ((*tree)->factor == 1) {
						Left_Balance(tree);
						taller = false;
					}
					else {
						(*tree)->factor = 0;
						taller = false;
					}
				}
			}
			else {
				return false;
			}
		}
		else if ((*tree)->data < value)
		{
			if (InsertAVL(&((*tree)->right), value, taller)) {
				if (taller) {
					if ((*tree)->factor == 0) {
						(*tree)->factor = -1;
						taller = true;
					}
					else if ((*tree)->factor == -1) {
						Right_Balance(tree);
						taller = false;
					}
					else {
						(*tree)->factor = 0;
						taller = false;
					}
				}
			}
			else {
				return false;
			}
		}
		else {
			cout << "节点已插入" << endl;
			return false;
		}

	}
	return true;
}

//查找
bool SearchAVL(AVLTree* tree, int value, AVLTree** p
ointerToParent = NULL) {
	if (tree == NULL)return false;
	if (tree->data == value)return true;
	if (pointerToParent != NULL)*pointerToParent = tree;
	if (tree->data > value)
	{
		return SearchAVL(tree->left, value, pointerToParent);
	}
	else
	{
		return SearchAVL(tree->right, value, pointerToParent);
	}
}

//删除
bool DeleteAVL(AVLTree** tree, int value, bool &lower) {
	if (*tree == NULL) {
		cout << "未找到删除对象" << endl;
		return false;
	}
	if ((*tree)->data == value) {
		AVLTree* node;
		node = *tree;

		if (node->left == NULL && node->right == NULL) {
			*tree = NULL;
			delete node;
		}
		else if (node->left == NULL && node->right != NULL) {
			*tree = node->right;
			node->right = NULL;
			delete node;
		}
		else if (node->left != NULL && node->right == NULL) {
			*tree = node->left;
			node->left = NULL;
			delete node;
		}
		else {
			AVLTree* temp = node->left;
			AVLTree** pointToLeft = &(node->left);
			while (temp->right != NULL)
			{
				temp->factor++;
				pointToLeft = &(temp->right);
				temp = temp->right;
			}
			node->data = temp->data;
			node->factor--;
			*pointToLeft = temp->left;
			temp->left = NULL;
			delete temp;

			if (node->factor == -2) {
				Left_Balance_Delete(tree);
			}
		}
		lower = true;
		return true;
	}
	else if ((*tree)->data > value) {
		if (DeleteAVL(&((*tree)->left), value, lower)) {
			if (lower) {
				if ((*tree)->factor == 0) {
					(*tree)->factor = -1;
					lower = false;
				}
				else if ((*tree)->factor == 1) {
					(*tree)->factor = 0;
					lower = true;
				}
				else {
					(*tree)->factor = -2;
					Left_Balance_Delete(tree);
				}
			}
		}
		else {
			return false;
		}
	}
	else {
		if (DeleteAVL(&((*tree)->right), value, lower)) {
			if (lower) {
				if ((*tree)->factor == 0) {
					(*tree)->factor = 1;
					lower = false;
				}
				else if ((*tree)->factor == 1) {
					(*tree)->factor = 2;
					Right_Balance_Delete(tree);
				}
				else {
					(*tree)->factor = 0;
					lower = true;
				}
			}
		}
		else {
			return false;
		}
	}

	return true;
}

运行测试

//省略头文件
int main()
{
	AVLTree** pointToAVL;
	AVLTree* tree = NULL;
	pointToAVL = &tree;
	bool taller = false;
	bool lower = false;
	for (int i = 1; i < 30; i++) {
		InsertAVL(pointToAVL,i, taller);
	}
	TreeVisualization(tree);//打印树结构的函数,不是重点所以没有给出
	cout << endl << "删除11" << endl;
	DeleteAVL(pointToAVL, 11, lower);
	TreeVisualization(tree);
	cout << endl << "删除10" << endl;
	DeleteAVL(pointToAVL, 10, lower);
	TreeVisualization(tree);
	cout << endl << "删除12" << endl;
	DeleteAVL(pointToAVL, 12, lower);
	TreeVisualization(tree);
}

结果如下:
原始树
在这里插入图片描述
删除11
在这里插入图片描述

删除10
在这里插入图片描述
删除12
在这里插入图片描述

参考文献:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值