C语言实现平衡二叉树

C语言实现平衡二叉搜索树
这篇博客介绍了如何使用C语言实现平衡二叉树,包括定义结构体、插入结点的过程,以及在插入过程中如何调整平衡因子。通过插入结点的分析,详细阐述了如何设置父结点,并借助栈来维护平衡。当平衡因子不为0时,会进行右单旋、左单旋、先左后右双旋和先右后左双旋等旋转操作,以保持树的平衡。删除结点的策略和旋转函数也进行了说明。

定义结构体

#define Type int
typedef struct AVLNode {
	Type data;
	AVLNode* leftchild;
	AVLNode* rightchild;
	int bf;//平衡因子
}AVLNode;

typedef struct AVLTree {
	AVLNode* root;
}AVLTree;

插入结点

void InitAVLTree(AVLTree* avl) {
	avl->root = NULL;
}

bool Insert(AVLTree* avl, Type x) {
	return Insert(avl->root, x);
}
bool Insert(AVLNode* &t, Type x) {
	AVLNode* parent = NULL;
	AVLNode* p = t;
	LinkStack st;
	InitStack(&st);

	while (p != NULL) {//如果存在根节点,就从根的左右子树找
		if (x == p->data)
			return 0;

		parent = p;
		Push(&st, parent);

		if (x < p->data)
			p = p->leftchild;
		else
			p = p->rightchild;
	}
	//1.首先判断根节点是否为空,如果为空,为新插入的结点开辟空间
	p = CreateNode(x);
	if (parent==NULL) {
		t = p;
		return 1;
	}
	else
	{
		if (x < parent->data) {
			parent->leftchild = p;
		}
		else {
			parent->rightchild = p;
		}
	}
	

	//2.设置平衡因子bf
	while (!IsEmpty(&st))
	{
		parent = GetTop(&st);
		Pop(&st);
		if (parent->leftchild == p) {
			parent->bf--;
		}
		else {
			parent->bf++;
		}

		if (parent->bf == 0) break;
		if (parent->bf == 1 || parent->bf == -1) {
			p = parent;
		}
		else {
			//3.开始旋转操作
			int flag = (parent->bf < 0) ? -1 : 1;
			if (p->bf == flag) {//单旋
				if (flag == -1)
					RotateR(parent);
				else 
					RotateL(parent);
			}
			else {//双旋
				if (flag == 1)
					RotateRL(parent);
				else
					RotateLR(parent);
			}
			break;
		}
	}

	//4.设置父结点的位置
	if (IsEmpty(&st)) {//栈为空,根就是父节点
		t = parent;
	}
	else {
		AVLNode* q = GetTop(&st);//q是调整那个失衡节点之后的新子树根节点的父节点
		if (q->data > parent->data)
			q->leftchild = parent;
		else
			q->rightchild = parent;
	}
	return 1;
}


/*测试*/
void main() {
	AVLTree avl;
	InitAVLTree(&avl);
	//int arr[] = { 50,40,60,10,45,70,5,30,20 };
	int arr[] = {30,20,50,10,40,70,60,80,65 };
	for (int i = 0; i < sizeof(arr)/sizeof(int); i++)
	{
		Insert(&avl, arr[i]);
	}
	Insert(&avl, 100);
}

分析

插入结点并设置父结点

插入第一个结点时,指针p都是指向新插入的节点。一开始,p=NULL,于是创建结点
在这里插入图片描述
插入第二个节点:p!=NULL,确定插入p的左子树还是右子树,p再次=NULL,于是创建结点。此时有父节点,让p根据大小指向父节点的左孩子
在这里插入图片描述
插入第三个节点,同样的创建结点,让p指向这个新结点之前,让parent指向p这个节点,成为新插入这个节点的父节点
在这里插入图片描述

调整平衡因子(借助栈)

让父结点入栈(30先入栈,其次是24),如果插入的节点是在这个结点的父节点的右边,父结点的bf++,反之bf–。
在这里插入图片描述
当父结点的bf=1或-1时,还要上溯到父节点的父节点。如24的bf为1,这时
在这里插入图片描述
此时栈中还有一个父节点30,所以这时让parent指向30,并把30出栈,栈空,然后parent(即30)的bf再次–等于-2
这时parent的bf既不是1,-1,也不是0。即找到了那一个使树不平衡的结点,所以就要开始旋转操作,使树恢复平衡。

旋转之后,找到调整那个失衡节点之后的新子树根节点的父节点,把旋转之后的新子树放在根节点的左右树。至此插入完成。

删除结点

旋转函数

针对的是第一个不平衡的节点进行旋转

右单旋

void RotateR(AVLNode* &ptr) {
	AVLNode* subR = ptr;
	ptr = subR->leftchild;
	//1.ptr的右孩子赋值给根节点的的左孩子
	subR->leftchild = ptr->rightchild;
	//2.原来的根节点变成ptr的右孩子,ptr成为根节点
	ptr->rightchild = subR;

	ptr->bf = subR->bf = 0;

}

在这里插入图片描述
在这里插入图片描述

左单旋

void RotateL(AVLNode* &ptr) {
	AVLNode* subR = ptr;
	ptr = subR->rightchild;
	subR->rightchild = ptr->leftchild;
	ptr->leftchild = subR;

	ptr->bf = subR->bf = 0;
}

在这里插入图片描述

先左后右双旋

void RotateLR(AVLNode* &ptr) {
	AVLNode* subR = ptr;
	AVLNode* subL = ptr->leftchild;
	ptr=subL->rightchild ;

	subL->rightchild = ptr->leftchild;
	ptr->leftchild = subL;
	//左旋之后调整平衡因子
	if (ptr->bf <= 0) {
		subL->bf = 0;
	}
	else {//说明ptr没有左树
		subL->bf = -1;
	}

	subR->leftchild = ptr->rightchild;
	ptr->rightchild = subR;
	if (ptr->bf == -1) {//说明ptr有没有右树
		subR->bf = 1;
	}
	else {
		subR->bf = 0;
	}
	ptr->bf = 0;//ptr最后是根节点
}

在这里插入图片描述
在这里插入图片描述

先右后左双旋

void RotateRL(AVLNode* &ptr) {
	AVLNode* subL = ptr;
	AVLNode* subR = ptr->rightchild;
	ptr = subR->leftchild;

	//开始右旋
	subR->leftchild = ptr->rightchild;
	ptr->rightchild = subR;
	if (ptr->bf >= 0)
		subR->bf = 0;
	else
		subR->bf = 1;

	//开始左旋
	subL->rightchild = ptr->leftchild;
	ptr->leftchild = subL;
	if (ptr->bf == 1)
		subL->bf = -1;
	else
		subL->bf = 0;
	ptr->bf = 0;
}

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值