Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++】AVL树

【C++】AVL树

作者头像
zxctscl
发布于 2024-12-04 00:13:34
发布于 2024-12-04 00:13:34
15100
代码可运行
举报
文章被收录于专栏:zxctscl个人专栏zxctscl个人专栏
运行总次数:0
代码可运行

个人主页zxctscl 如有转载请先通知

1. 底层结构

前面对map/multimap/set/multiset进行了简单的介绍: 【C++】map和set ,在其文档介绍中发现,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

2. AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

因为有些节点的数量,像2和4,做不到高度差相等,所以规则就退而求其次,左右高度差不超过1。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树: 它的左右子树都是AVL树 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1) 平衡因子并不是必须的,只是它的一种实现方式。

平衡因子=右子树高度-左子树高度

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在

O(log_2 n)

,搜索时间复杂度O(

log_2 n

)。

3. AVL树节点的定义

AVL树节点的定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data)
		: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
		, _data(data), _bf(0)
	{}
	AVLTreeNode<T>* _pLeft; // 该节点的左孩子
	AVLTreeNode<T>* _pRight; // 该节点的右孩子
	AVLTreeNode<T>* _pParent; // 该节点的双亲
	T _data;
	int _bf; // 该节点的平衡因子
};

4. AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么 AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子(更新插入节点的祖先节点的平衡因子) (1)插入到父亲的左边,父亲平衡因子减减 (2)插入到父亲的右边,父亲平衡因子加加 (3)如果父亲节点的平衡因子等于0,父亲所在子树高度不变,不再继续往上更新,插入结束。 (4)如果父亲的平衡因子是1或者-1,父亲所在子树高度变了,继续往上更新 (5)如果父亲的平衡因子是2或者-2,父亲所在子树已经不平衡了,需要旋转处理更新中不可能出现其它值,插入之前树是AVL树,平衡因子是-1 0 1,加加减减最多出现(3)(4)(5)情况。

插入一个节点,先判断,如果root为空,就先插入第一个节点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

用cur指向root,插入比root大往右走,是比较pair的first,比root小往左走:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);

		if (parent->_kv.first <kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

但是这里是三叉链,这里得和父亲链接起来:cur->_parent = parent; 这里得更新平衡因子:插入到父亲的左边,父亲平衡因子减减;插入到父亲的右边,父亲平衡因子加加;平衡因子等于0,父亲所在子树高度不变,更新结束。平衡因子是1或者-1,父亲所在子树高度变了,继续往上更新,父亲的平衡因子是2或者-2,父亲所在子树已经不平衡了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				// 更新结束
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				// 当前子树出问题了,需要旋转平衡一下


				break;
			}
			else
			{
				// 理论而言不可能出现这个情况
				assert(false);
			}
		}

5. AVL树的旋转

这里用抽象图来代表某一类: abc代表高度为h的AVL子树:

在a这里插入新节点,a的高度变化从h变成h+1,就会把30的平衡因子更新为-1;

具象图:

  1. h==0: 30这里新增一个节点。
  1. h==1: 在20的左边或者右边新增都会引发旋转
  1. h==2 高度为2的AVL树有三种:

b和c是x/y/z中的任意一种 a只能是z这种情况,如果a是x这种情况:

那么当a插入一个节点,像下面这样,高度不变,就不会往上更新:

如果长这样:它自己就不旋转了:

所以a一定就是下面这种形状,插入一个节点才会引发旋转:

当h==2时候,a插入一个节点,就会引发旋转

b和c三种形状都可以,而插入可以选择4个节点中任何一个,所以

  1. h==3 高度为3最满的就是下面这样,4个节点可能是满的C44,四个节点中任意选三,四个节点中任意选两个,四个节点中任意选一个,这些都可能是高度为h的情况。光树就可能有15种。

a如果是下面这种情况:就有8种插入新节点的可能:

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

  1. 新节点插入较高左子树的左侧—左左:右单旋(左边高,往右边压)

b变成60的左边,30<b子树<60<c子树

所以当h==1时候,也是同样的右边压

旋转后的a c位置没变,把60变到30右边,b变到60左边:

此时代码就是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		Node* ppNode = parent->_parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}
  1. 新节点插入较高右子树的右侧—右右:左单旋

现在右边高,让subRL变成30的右边,30<b<60<c:

右边高,左边旋:

代码和右单旋类似:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* ppNode = parent->_parent;

		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}
			subR->_parent = ppNode;
		}

		parent->_bf = subR->_bf = 0;
	}

像下面这样的情况,发现右边高,就左单旋,而出来结果导致左边高,再右单旋,发现结果和刚开始一样。

上面这个图并不是纯粹的右边高,它是右边高,左边高,不像纯粹的右边高(下面图这样):

这不是单纯的右边高,右边高,左边高

以8为旋转点,进行右边单旋啊,经过这个单旋,变成单纯的右边高。在意parent为旋转点进行左单选:

  1. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再 考虑平衡因子的更新。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}

	}
  1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

总结: 假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR 当pSubR的平衡因子为1时,执行左单旋 当pSubR的平衡因子为-1时,执行右左双旋
  2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL 当pSubL的平衡因子为-1是,执行右单旋 当pSubL的平衡因子为1时,执行左右双旋 旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		subRL->_bf = 0;
		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else
		{
			parent->_bf = 0;
			subR->_bf = 0;
		}
	}

6. AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
  2. 验证其为平衡树 (1)每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子) (2)节点的平衡因子是否计算正确
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	  int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}


		int _Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			/*int leftHeight= _Height(root->_left);
			int rightHeight = _Height(root->_right);*/

			return max(_Height(root->_left), _Height(root->_right)) + 1;
		}

		bool _IsBalance(Node* root)
		{
			if (root == nullptr)
				return true;

			int leftHeight = _Height(root->_left);
			int rightHeight = _Height(root->_right);

			if (abs(leftHeight - rightHeight) >= 2)//不平衡
			{
				cout << root->_kv.first << endl;
				return false;
			}
			
			// 顺便检查一下平衡因子是否正确
			if (rightHeight - leftHeight != root->_bf)
			{
				cout << root->_kv.first << endl;
				return false;
			}

			return _IsBalance(root->_left)
				&& _IsBalance(root->_right);
		}

测试用例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void TestAVLTree2()
{
	const int N = 100000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
		//cout << v.back() << endl;
	}

	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
		//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
	}
	size_t end2 = clock();

	cout << "Insert:" << end2 - begin2 << endl;
	//cout << t.IsBalance() << endl;

	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	for (auto e : v)
	{
		t.Find(e);
	}

	// 随机值
	/*for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}*/

	size_t end1 = clock();

	cout << "Find:" << end1 - begin1 << endl;
}

AVL树的性能: AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即

log_2 (N)

。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

7. AVLTree.h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once
#include<assert.h>
#include<vector>


template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;

	int _bf;  // balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	// logN
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		//...
		// 更新平衡因子
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				// 更新结束
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				// 当前子树出问题了,需要旋转平衡一下
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else if(parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}

				break;
			}
			else
			{
				// 理论而言不可能出现这个情况
				assert(false);
			}
		}


		return true;
	}



	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}


	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		Node* ppNode = parent->_parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}

		parent->_bf = subL->_bf = 0;
	}


	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* ppNode = parent->_parent;

		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}
			subR->_parent = ppNode;
		}

		parent->_bf = subR->_bf = 0;
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		subRL->_bf = 0;
		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else
		{
			parent->_bf = 0;
			subR->_bf = 0;
		}
	}

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}


private:
  	  int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}

		int _Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			/*int leftHeight= _Height(root->_left);
			int rightHeight = _Height(root->_right);*/

			return max(_Height(root->_left), _Height(root->_right)) + 1;
		}

		bool _IsBalance(Node* root)
		{
			if (root == nullptr)
				return true;

			int leftHeight = _Height(root->_left);
			int rightHeight = _Height(root->_right);

			if (abs(leftHeight - rightHeight) >= 2)//不平衡
			{
				cout << root->_kv.first << endl;
				return false;
			}
			
			// 顺便检查一下平衡因子是否正确
			if (rightHeight - leftHeight != root->_bf)
			{
				cout << root->_kv.first << endl;
				return false;
			}

			return _IsBalance(root->_left)
				&& _IsBalance(root->_right);
		}


		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_InOrder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_InOrder(root->_right);
		}
private:
	Node* _root = nullptr;
};

void TestAVLTree1()
{
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert({ e,e });
		/*cout <<"Insert"<<e<<"->" << t1.IsBalance() << endl;*/
	}

	t1.InOrder();
	cout << t1.IsBalance() << endl;
}

void TestAVLTree2()
{
	const int N = 100000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
		//cout << v.back() << endl;
	}

	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
		//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
	}
	size_t end2 = clock();

	cout << "Insert:" << end2 - begin2 << endl;
	//cout << t.IsBalance() << endl;

	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	for (auto e : v)
	{
		t.Find(e);
	}

	// 随机值
	/*for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}*/

	size_t end1 = clock();

	cout << "Find:" << end1 - begin1 << endl;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.AVL树
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均 搜索长度。
hope kc
2024/09/23
1100
移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.AVL树
【C++修炼之路】19.AVL树
对于AVL树,相比普通的二叉搜索树,最主要的就是多了一个平衡因子保持AVL高度平衡的结构。而为了能够更加便捷的操作平衡因子,除了左右节点的指针,还要新增一个父亲节点指针,即三叉链的结构,因为左右子树的增加节点就会导致父亲节点平衡因子的变化:
每天都要进步呀
2023/03/28
1.1K0
【C++修炼之路】19.AVL树
C++进阶:AVL树详解及模拟实现(图示讲解旋转过程)
更新后,需要检查父节点的平衡因子是否发生变化,如果发生变化,则继续向上检查祖先节点的平衡因子,直到根节点或者到达一个平衡因子为 ±1 的节点为止。根据更新后节点的平衡因子情况,可以采取以下处理措施:
是Nero哦
2024/05/16
2540
C++进阶:AVL树详解及模拟实现(图示讲解旋转过程)
C++:AVL树
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,时间复杂度为O(N);
二肥是只大懒蓝猫
2023/03/30
4330
C++:AVL树
详谈平衡二叉搜索树(AVL树)
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
南桥
2024/08/24
1650
详谈平衡二叉搜索树(AVL树)
【C++AVL树】枝叶间的旋律:AVL树的和谐之道
那么对于我们的10来说,右边是2,左边是0,那么2-0=2,就不满足AVL树的要求
Undoom
2025/05/29
650
【C++AVL树】枝叶间的旋律:AVL树的和谐之道
【五一创作】|【C++】AVL树的实现
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下, 所以在此基础上提出解决办法: 当向二叉搜索树中插入新节结点时,如果能保证每个节点的左右子树高度之差的绝对值不超过1即可降低树的高度,从而减少平均搜索长度 AVL树又称平衡二叉搜索树
lovevivi
2023/10/16
2360
【五一创作】|【C++】AVL树的实现
AVL树深度解析
我们上一篇博客讲了,二叉搜索树在极端情况下会退化为单支树的情况(具体可以看上一篇博客:http://t.csdnimg.cn/o7PiL)。那我们该如何解决这种问题呢?
小灵蛇
2024/06/06
1360
AVL树深度解析
【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道
AVL树是一种自平衡的二叉查找树(BST),由G.M. Adelson-Velsky和E.M. Landis于1962年提出。AVL树的核心特点是它保证树的每个节点的左右子树的高度差(平衡因子)不超过1,从而保证了AVL树在插入、删除和查找操作时的时间复杂度始终为O(log N)。
半截诗
2025/02/22
1640
【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道
【C++】AVL 树平衡二叉搜索的神奇结构,代码实现全解析,从概念到应用,助你轻松掌握这一高效数据结构,编程能力更上一层楼!
不为空,说明还有节点,判断ppNode左节点是parent的话,左节点连接subL
逆向-落叶
2025/02/10
2450
【C++】AVL 树平衡二叉搜索的神奇结构,代码实现全解析,从概念到应用,助你轻松掌握这一高效数据结构,编程能力更上一层楼!
AVL树模拟实现
AVL树,是一种“平衡”的二叉搜索树,关于搜索树的介绍和模拟,我已经在该篇文章(二叉搜索树的模拟实现-CSDN博客)介绍过,想复习或者了解二叉搜索树的读者可以去看看哦
用户11039529
2024/08/17
1210
AVL树模拟实现
【c++】map和set&&AVL树&&红黑树详解&&模拟实现&&map和set封装
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
用户10925563
2024/06/04
3740
【c++】map和set&&AVL树&&红黑树详解&&模拟实现&&map和set封装
【C++】AVL树
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家 G.M.Adelson-Velskii 和 E.M.Landis 在 1962 年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
YoungMLet
2024/03/01
1730
【C++】AVL树
C++: AVL树
你的时间有限, 所以不要为别人而活, 不要被教条所限, 不要活在别人的观念里, 不要让别人的意见左右自己内心的声音. -- 乔布斯
用户11317877
2024/10/16
1550
C++: AVL树
C++AVL树
AVL树 零、前言 一、AVL树的概念 二、AVL树结点定义 三、AVL树的插入 四、AVL树的旋转 1、左单旋 2、右单旋 3、左右双旋 4、右左双旋 5、总结 五、AVL树的验证 六、AVL树的性能 零、前言 本章主要讲解map和set的底层结构平衡二叉搜索树的一种-AVL树的特性及其实现 一、AVL树的概念 引入: map/multimap/set/multiset其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷 假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化
用户9645905
2022/11/30
4720
C++AVL树
【高阶数据结构】AVL树详解
因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:
YIN_尹
2024/01/23
2.1K0
【高阶数据结构】AVL树详解
AVL 树
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
ahao
2024/03/19
1170
AVL 树
C++【AVL树】
普通的二叉搜索树可能会退化为单支树(歪脖子树),导致搜索性能严重下降,为了解决这个问题,诞生了平衡二叉搜索树,主要是通过某些规则判断后,降低二叉树的高度,从而避免退化,本文介绍的 AVL 树就属于其中一种比较经典的平衡二叉搜索树,它是通过 平衡因子 的方式来降低二叉树高度的,具体怎么操作,可以接着往下看
北 海
2023/07/01
2030
C++【AVL树】
【C++】AVL树和红黑树的插入
1. 虽然二叉搜索树的搜索效率很高,当搜索树接近满二叉树时,搜索效率可以达到logN,但是如果数据是有序的,则二叉搜索树会退化为单支树,搜索效率和普通的序列式容器相同了就,所以在搜索树的基础上,两位俄罗斯数学家研究出了平衡搜索树。
举杯邀明月
2023/04/12
7250
【C++】AVL树和红黑树的插入
(史上超级清晰图解分析版)AVL树的实现--C++
更新停止条件: 5. 更新后parent的平衡因子等于0,更新中parent的平衡因子变化为-1->0 或者 1->0,说明更新前parent子树一边高一边低,新增的结点插入在低的那边,插入后parent所在的子树高度不变,不会影响parent的父亲结点的平衡因子,更新结束。 6. 更新后parent的平衡因子等于1 或 -1,更新前更新中parent的平衡因子变化为0->1 或者 0->-1,说明更新前parent子树两边一样高,新增的插入结点后,parent所在的子树一边高一边低,parent所在的子树符合平衡要求,但是高度增加了1,会影响arent的父亲结点的平衡因子,所以要继续向上更新。 7. 更新后parent的平衡因子等于2 或 -2,更新前更新中parent的平衡因子变化为1->2 或者 -1->-2,说明更新前parent子树一边高一边低,新增的插入结点在高的那边,parent所在的子树高的那边更高了,破坏了平衡,parent所在的子树不符合平衡要求,需要旋转处理,旋转的目标有两个:1、把parent子树旋转平衡。2、降低parent子树的高度,恢复到插入结点以前的高度。所以旋转后也不需要继续往上更新,插入结束。
小志biubiu
2025/02/27
1960
(史上超级清晰图解分析版)AVL树的实现--C++
相关推荐
移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.AVL树
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验