数据结构——AVL树

AVL树的性质

BST树虽然可以缩短查找的效率,但如果数据有序或接近有序,BST树就会退化为接近线性的结构,查找元素相当于线性查找,效率低下;
AVL树本质上是一颗二叉搜索树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。下面是非平衡二叉搜索树和平衡二叉搜索树对比的例图:
左边是非平衡二叉搜索树,右边是平衡二叉搜索树

实现AVL树

AVL树数据结构

结点的值以int型为例子,一个AVL树的结点有5个属性,分别是当前结点的值val,左孩子结点left,右孩子结点right,父结点parent,平衡因子bf;
平衡因子bf的值为左子树的高度减去右子树的高度,AVL树结点的平衡因子只能是-1、0、1。

struct node {
	int val;
	node* parent;
	node* left;
	node* right;
	int bf;
	node(int x = 0) :val(x), parent(nullptr), left(nullptr), right(nullptr),bf(0) {}
	void show() { //中序遍历
		if (this != nullptr) {
			this->left->show();
			cout << this->val << " ";
			this->right->show();
		}
	}
};
class AVL {
	node* root;
public:
	AVL():root(nullptr){}
	bool insert(int x);
	void show() {
		this->root->show();
		cout << endl;
	}
private:
	void leftOne(node* p);   //左单旋
	void rightOne(node* p);  //右单旋
	void leftRight(node* p); //左右双旋
	void rightLeft(node* p); //右左双旋
};

插入新结点

1)找到插入新节点的位置并连接
2)修改平衡因子并调整成AVL树

当AVL树插入新结点后,需要修改平衡因子,当插入节点是父节点的左子树时,父节点的平衡因子-1
当插入节点是父节点的右子树时,父节点的平衡因子1
修改完成后父节点的平衡因子有三种情况:
1)平衡因子等于0,插入完成;
2)平衡因子绝对值等于1,把插入节点更新为父节点,父节点跟新为父节点的父节点,继续往上调整平衡因子;
3)平衡因子绝对值等于2,树结构调整成AVL树结构,插入成功。

void insert(int x) {
		if(root == nullptr){
			root = new node(x);
			return ;
		}
		node* newnode = new node(x);
		node* p = this->root;
		while (p) {
			if (x < p->val) {
				if (p->left == nullptr) {
					p->left = newnode;
					newnode->parent = p;
					break;
				}
				p = p->left;
			}
			else {
				if (p->right == nullptr) {
					p->right = newnode;
					newnode->parent = p;
					break;
				}
				p = p->right;
			}
		}
		node* _parent = p;
		node* _child = newnode;
		while (_parent) {
			if (_parent->left == _child) {
				_parent->bf--;
			}
			else {
				_parent->bf++;
			}
			if (_parent->bf == 0) {
				break; //平衡因子为0,插入完成
			}
			else if (_parent->bf == 1 || _parent->bf == -1) {
				_child = _parent;//往上调整平衡因子
				_parent = _parent->parent;
			}
			else if (_parent->bf == 2) {
				//调整树结构
			}
			else if (_parent->bf == -2) {
				//调整树结构
			}
		}
	}

插入调整

调整树结构分很多种情况,具体用图说明。

右单旋和左单旋

右单旋
如下图,P、C结点为AVL树结点,ABD的结构是AVL树结构,h为数的高度,h>=0,当在子树A插入一个结点使A的高度增加1,结点P的平衡因子就变为-2,只需一次右单旋即可使其平衡。
在这里插入图片描述

对P结点进行右单旋过程:
1)保存C结点的地址,把P的左结点指向C的子树B,如果B不是空树,B的父结点指向P;
2)C的右结点指向P、C的父节点指向P的父结点,如果P的父结点是空,则把根结点指向C;如果不空,需要把原来指向P的结点指针指向C;
3)P的父节点指向C,更新平衡因子,子树A、B、D的结构并没有改变,不需要更改,C和P的左右子树高度一致,平衡因子都更新为0

void rightOne(node* p) {
		node* c = p->left;
		p->left = c->right;
		if (p->left != nullptr) {
			p->left->parent = p;
		}
		c->right = p;
		c->parent = p->parent;
		if (p->parent != nullptr) {
			if (p->parent->left == p) {
				p->parent->left = c;
			}
			else {
				p->parent->right = c;
			}
		}
		else {
			root = c;
		}
		p->parent = c;
		c->bf = 0;
		p->bf = 0;
	}

调整完成后如下图:
在这里插入图片描述

左单旋
如下图,P、C结点为AVL树结点,ABD的结构是AVL树结构,h为数的高度,h>=0,当在子树A插入一个结点使A的高度增加1,结点P的平衡因子就变为2,只需一次左单旋即可使其平衡。
在这里插入图片描述

左单旋跟右单旋步骤几乎一致,把代码中的left变right、right变left即可

void leftOne(node* p) {
		node* c = p->right;
		p->right = c->left;
		if (p->right != nullptr) {
			p->right->parent = p;
		}
		c->left = p;
		c->parent = p->parent;
		if (p->parent != nullptr) {
			if (p->parent->left == p) {
				p->parent->left = c;
			}
			else {
				p->parent->right = c;
			}
		}
		else {
			root = c;
		}
		p->parent = c;
		c->bf = 0;
		p->bf = 0;
	}
左右双旋和右左双旋

左右双旋
如下图中的左半部分,P、C、A是AVL结点,B、D子树平衡,高度为h,E、F子树平衡,高度为h-1,当在红色位置其中之一插入一个结点时,P的C的平衡因子为1,P的平衡因子为-2,需要两次旋转才能使其平衡;
右半部分插入节点A是左半部分的特例,平衡操作一致,但在更新指向时需要判空;
在这里插入图片描述
1)先保存旋转前A结点的平衡因子,为后面调整平衡因子做准备
2)对结点C左旋
在这里插入图片描述

3)再对结点P右旋
在这里插入图片描述
4)调整平衡因子
两次旋转过程中,子树B、E、F、D的结构并没有改变,平衡因子不变;对C结点左旋的时候,A、C结点的平衡因子修改为0,对P右旋的时候,P、C结点平衡因子修改为0;如果新节点的位置再E子树下,那么旋转前A结点的平衡因子为-1,调整后C的平衡因子为0,P结点的平衡因子为1;如果新节点的位置再F子树下,那么旋转前A结点的平衡因子为1,调整后P的平衡因子为0,C结点的平衡因子为-1;如果旋转前A的平衡因子为0,则不需要调整,因为两次旋转已经把他们的平衡因子置0了。

void leftRight(node* p) {
		node* c = p->left;
		int bf = c->right->bf;
		leftOne(c);
		rightOne(p);
		if (bf == -1) {
			p->bf = 1;
		}
		if (bf == 1) {
			c->bf = -1;
		}
	}

右左双旋跟左右双旋思路是一样的,这里就不再赘述,直接上代码

void rightLeft(node* p) {
		node* c = p->right;
		int bf = c->left->bf;
		rightOne(c);
		leftOne(p);
		if (bf == -1) {
			p->bf = 1;
		}
		if (bf == 1) {
			c->bf = -1;
		}
	}

AVL树的验证

#include <iostream>
#include <vector>
#include <time.h>
using namespace std;
struct node {
	int val;
	node* parent;
	node* left;
	node* right;
	int bf;
	node(int x = 0) :val(x), parent(nullptr), left(nullptr), right(nullptr),bf(0) {}
	void show() {
		if (this != nullptr) {
			this->left->show();
			cout << this->val << " ";
			this->right->show();
		}
	}
};
class AVL {
	node* root;
public:
	AVL():root(nullptr){}
	void insert(int x) {
		if(root == nullptr){
			root = new node(x);
			return ;
		}
		node* newnode = new node(x);
		node* p = this->root;
		while (p) {
			if (x < p->val) {
				if (p->left == nullptr) {
					p->left = newnode;
					newnode->parent = p;
					break;
				}
				p = p->left;
			}
			else {
				if (p->right == nullptr) {
					p->right = newnode;
					newnode->parent = p;
					break;
				}
				p = p->right;
			}
		}
		node* _parent = p;
		node* _child = newnode;
		while (_parent) {
			if (_parent->left == _child) {
				_parent->bf--;
			}
			else {
				_parent->bf++;
			}
			if (_parent->bf == 0) {
				break;
			}
			else if (_parent->bf == 1 || _parent->bf == -1) {
				_child = _parent;
				_parent = _parent->parent;
			}
			else if (_parent->bf == 2) {
				if (_parent->right->bf == 1) {
					leftOne(_parent);
				}
				else if (_parent->right->bf == -1) {
					rightLeft(_parent);
				}
				break;
			}
			else if (_parent->bf == -2) {
				if (_parent->left->bf == -1) {
					rightOne(_parent);
				}
				else if (_parent->left->bf == 1) {
					leftRight(_parent);
				}
				break;
			}
		}
	}
	void show() {
		this->root->show();
		cout << endl;
	}
private:
	void leftOne(node* p) {
		node* c = p->right;
		p->right = c->left;
		if (p->right != nullptr) {
			p->right->parent = p;
		}
		c->left = p;
		c->parent = p->parent;
		if (p->parent != nullptr) {
			if (p->parent->left == p) {
				p->parent->left = c;
			}
			else {
				p->parent->right = c;
			}
		}
		else {
			root = c;
		}
		p->parent = c;
		c->bf = 0;
		p->bf = 0;
	}
	void rightOne(node* p) {
		node* c = p->left;
		p->left = c->right;
		if (p->left != nullptr) {
			p->left->parent = p;
		}
		c->right = p;
		c->parent = p->parent;
		if (p->parent != nullptr) {
			if (p->parent->left == p) {
				p->parent->left = c;
			}
			else {
				p->parent->right = c;
			}
		}
		else {
			root = c;
		}
		p->parent = c;
		c->bf = 0;
		p->bf = 0;
	}
	void leftRight(node* p) {
		node* c = p->left;
		int bf = c->right->bf;
		leftOne(c);
		rightOne(p);
		if (bf == -1) {
			p->bf = 1;
		}
		if (bf == 1) {
			c->bf = -1;
		}
	}
	void rightLeft(node* p) {
		node* c = p->right;
		int bf = c->left->bf;
		rightOne(c);
		leftOne(p);
		if (bf == -1) {
			p->bf = 1;
		}
		if (bf == 1) {
			c->bf = -1;
		}
	}
};
void test(vector<int>& v) {
	time_t t;
	srand((unsigned)time(&t));
	for (int i = 0; i < 100; i++) {
		v.push_back(rand() % 100);
	}
}
int main() {
	AVL* t = new AVL;
	vector<int> v;
	test(v);
	for (int i = 0; i < 100; i++) {
		t->insert(v[i]);
	}
	t->show();
}

中序遍历结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_200_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值