主頁 >  其他 > C++_AVL樹插入,查找與修改的實作(Key_Value+平衡因子+三叉鏈)

C++_AVL樹插入,查找與修改的實作(Key_Value+平衡因子+三叉鏈)

2021-11-09 08:05:00 其他

文章目錄

    • 1.AVL樹的提出
    • 平衡因子
    • 2.AVL樹的節點定義
    • 3.AVL樹類
    • ①AVL樹插入節點(插入成功回傳一個pair值)
    • 更新平衡因子
    • 當平衡因子變為2或-2時,四種旋轉調整AVL樹
    • 右單旋(新節點插入較高左子樹的左側)
    • _右單旋代碼
    • 左單旋(新節點插入較高右子樹的右側)
    • _左單旋代碼
    • 左右雙旋(新節點插入較高左子樹的右側)
    • _左右雙旋代碼
    • 右左雙旋(新節點插入較高右子樹的左側)
    • _右左雙旋代碼
    • AVL樹插入C++實作
    • ②判斷是否是AVL樹代碼
    • ③AVL樹通過鍵值C++查找代碼
    • ④AVL樹的C解構式
    • ⑤利用插入函式的回傳值多載[]實作AVL樹的修改
    • 4.AVL樹插入,修改C++代碼
    • 測驗代碼
    • 運行結果
    • 5.代碼Github/Gitree代碼位置

1.AVL樹的提出

二叉搜索樹雖然可以提高搜索效率,但如果資料接近有序的話搜索二叉樹的效率退化為鏈表了,為了解決這個問題,提出了AVL樹,

向平衡二叉樹中插入新節點,保證每個節點的高度差的絕對值小于等于1,降低樹的高度,提高搜索效率,這種樹稱為AVL樹

平衡因子

每個節點的平衡因子=這個節點的右子樹高度-這個節點的左子樹高度,

注意:平衡因子不是AVL樹必須的,這里實作AVL樹時節點選擇了添加了平衡因子

2.AVL樹的節點定義

template<class Key,class Val>
struct AVLNode
{
	AVLNode<Key, Val>* _Left;
	AVLNode<Key, Val>* _Right;
	AVLNode<Key, Val>* _Parent;//記錄節點的父節點,方便旋轉

	int _bf;//平衡因子
	pair<Key, Val> _KV;
	AVLNode(const pair<Key, Value>& kv) :_Left(nullptr), _Right(nullptr)
		, _Parent(nullptr), _bf(0), _KV(kv)
	{}
};

3.AVL樹類

template<class Key, class Val>
class AVLTree
{
public:
	typedef AVLNode<Key, Val> Node;
	AVLTree() :_root(nullptr) {}

	pair<Node*,bool> Insert(const pair<Key, Val>& kv);//向AVL樹中插入節點,插入成功回傳這個位置的指標和true,失敗回傳這個位置的指標和false

	Node* Find(const Key& key);//通過鍵值來查找對應鍵值節點,成功回傳對應節點的指標,失敗回傳空

	void PrintTree()//中序列印AVL樹
	{
		_PrintTree(_root);
	}

private:
	Node* _root;

	void _PrintTree(Node* root)
	{
		if (root == nullptr)
			return;
		_PrintTree(root->_Left);
		cout << root->_KV.first << "->" << root->_KV.second << endl;
		_PrintTree(root->_Right);
	}
	/*
	四種旋轉,看下文分析,下面的四種旋轉函式為類的成員函式
	*/
};

①AVL樹插入節點(插入成功回傳一個pair值)

向AVL樹中插入節點是否旋轉取決于樹中的平衡因子,當平衡因子的絕對值大于1證明需要調整AVL樹的高度,

更新平衡因子

插入節點后,這個節點的父節點到根節點的路徑上的所有節點的平衡因子可能要改變,新插入的節點平衡因子為0,這在建構式中就可以保證,

平衡因子=右樹-左樹,
所以插入節點在父節點左邊,父節點的平衡因子-1,反之父節點的平衡因子+1;

如果父節點經過調整后,平衡因子為1或者-1,說明原來父節點的因子為0,這次插入改變了父節點原來的高度,還要繼續向上調整平衡因子,
如果父節點經過調整后,平衡因子變為0,說明原來父節點的因子為1或-1,這次插入沒有改變父節點的高度,不需要繼續向上調整平衡因子,
如果父節點經過調整后,平衡因子變為2或-2,說明此時不滿足AVL樹特性,要通過旋轉來調整高度,

當平衡因子變為2或-2時,四種旋轉調整AVL樹

旋轉的基本準則:在旋轉后這棵樹還是搜索樹,只是讓其盡可能的保持平衡

這里將二叉樹抽象為高度為h的矩形來概括情況

右單旋(新節點插入較高左子樹的左側)

在這里插入圖片描述
右單旋的情況為插入節點的父節點的平衡因子為-1,且這個節點父節點的父節點平衡因子為-2,
如上圖表現為右高左低,將a節點連接到b右節點,將原來b右子樹連接到a左子樹上,
注意還要調節對應節點的父指標,讓a的父指標指向b,Rh的父指標指向a(如果Rh存在的話,不存在(h=0)不需要處理),b的父指標指向空
旋轉后如上圖,修改平衡因子的值為0,
調整根節點:上圖因為旋轉的是根節點,這時只要把b作為根節點即可,
當旋轉的是子樹時,原來的a的父節點要被保存下來,在旋轉完后b的父節點要指向這個節點,
只要發生旋轉一定是平衡因子從1/-1到2/-2,旋轉完后平衡因子變為0,相當于從1/-1到0,子樹的高度沒有發生變化,所以上面的平衡因子不變,不需要再向上調整了

_右單旋代碼

函式要傳入平衡因子為-2的節點指標,如上圖要傳入節點a的指標,

void _Single_Right(Node* parent)//根據圖把對應關系連接起來
{
	//記錄要移動的節點
	Node* SubL = parent->_Left;
	Node* SubLR = SubL->_Right;

	//連接
	parent->_Left = SubLR;
	if (SubL->_Right != nullptr)//修改父指標
	{
		SubLR->_Parent = parent;
	}
	//連接
	SubL->_Right = parent;
	Node* GradParent = parent->_Parent;//記錄這個節點的父節點,為了修改根節點
	parent->_Parent = SubL;//修改父指標
	//修改平衡因子為0
	parent->_bf = 0; SubL->_bf = 0;
	//調整根節點
	if (parent == _root)//要旋轉的節點為根節點
	{
		_root = SubL;
		SubL->_Parent = GradParent;
	}
	else//要旋轉的節點是子樹,修改GradParent指標
	{
		if (GradParent->_Left == parent)
		{
			GradParent->_Left = SubL;
		}
		else
		{
			GradParent->_Right = SubL;
		}
		SubL->_Parent = GradParent;
	}
}

左單旋(新節點插入較高右子樹的右側)

在這里插入圖片描述
左單旋與右單旋情況類似:觀察上圖:
左單旋的情況為插入節點的父節點的平衡因子為1,且這個節點父節點的父節點平衡因子為2,

將bL連接到a的右樹上,a再連接到b的左樹上,調整平衡因子a,b平衡因子都變為0,調整a,b節點父指標,調整樹的根節點,處理子樹的情況,細節和右單旋類似,

_左單旋代碼

void _Single_Left(Node* parent)//左旋轉
{
	//記錄要移動的節點
	Node* SubR = parent->_Right;
	Node* SubRL = SubR->_Left;

	//連接
	parent->_Right = SubRL;
	if (SubRL != nullptr)
	{
		SubRL->_Parent = parent;
	}
	SubR->_Left = parent;
	Node* GradParent = parent->_Parent;//記錄這個節點的父節點,為了修改根節點
	parent->_Parent = SubR;

	//修改平衡因子
	parent->_bf = 0; SubR->_bf = 0;
	//調整根節點
	if (parent == _root)
	{
		_root = SubR;
		SubR->_Parent = GradParent;
	}
	else //要旋轉的節點是子樹,修改GradParent指標
	{
		if (GradParent->_Left == parent)//旋轉的是左子樹,連接到左邊
		{
			GradParent->_Left = SubR;
		}
		else
		{
			GradParent->_Right = SubR;//反之
		}
		SubR->_Parent = GradParent;
	}
}

左右雙旋(新節點插入較高左子樹的右側)

在這里插入圖片描述
由上圖可以看到a節點的平衡因子為-2,b節點的平衡因子為1時要進行左右雙旋,
特點為:b節點進行左旋,a節點進行右旋,
因為上面已經寫過左旋右旋的代碼,直接復用即可,
但是這時要分析平衡因子的變化

觀察上圖,相當于把c節點拆開c做根節點,c的左子樹給b右樹,c的右子樹給a的左樹,c的左子樹連接b,c的右子樹連接a,從而達到降低樹的高度的目的,

根據上面的分析可知c在旋轉后平衡因子一定為0.
如果新節點插入的位置在c的左子樹上,經過旋轉會到b的右子樹上,b的平衡因子為0,a的平衡因子為1.
如果新節點插入的位置在c的右子樹上,經過旋轉會到a的左子樹上,b的平衡因子為-1,a的平衡因子為0(如上圖).

特殊情況:當b節點沒有子樹時,即這顆樹只有cba這三個節點,此時a,b,c這三個節點的平衡因子都為0,

上面的這三種情況都是以c節點的平衡因子來判斷的,如果c的平衡因子變為1,說明其插入到了c節點的右子樹上,對應第二種情況,如果c的平衡因子變為-1,說明其插入到了c節點的左子樹上,對應第一種情況,如果c作為插入的節點,此時對應的是特殊情況,

在這里插入圖片描述

先記錄旋轉改變的這三個節點指標,通過判斷SubLR中平衡因子來判斷是那種情況,與上面的對應處理即可,

_左右雙旋代碼

void _Single_LeftRight(Node* parent)//左右雙旋
{
	//記錄節點的位置
	Node* SubL = parent->_Left;
	Node* SubLR = SubL->_Right;
	//先左旋,再右旋
	_Single_Left(parent->_Left);
	_Single_Right(parent);
	//根據SubRL判斷是什么型別,修改平衡因子
	if (SubLR->_bf == 1)//插入到右子樹上
	{
		SubLR->_bf = 0;
		SubL->_bf = -1;
		parent->_bf = 0;
	}
	else if (SubLR->_bf == -1)//插入到左子樹上
	{
		parent->_bf = 1;
		SubL->_bf = 0;
		SubLR->_bf = 0;
	}
	else if (SubLR->_bf == 0)//特殊情況,插入后只有三個節點,三個節點的平衡因子都為0,
	{
		parent->_bf = 0;
		SubL->_bf = 0;
		SubLR->_bf = 0;
	}
	else//發生錯誤
	{
		assert(false);
	}
}

右左雙旋(新節點插入較高右子樹的左側)

在這里插入圖片描述
由上圖可以看到a節點的平衡因子為2,b節點的平衡因子為-1時要進行右左雙旋,
特點為:b節點進行右旋,a節點進行左旋,
具體細節看上圖,這里不在贅述

_右左雙旋代碼

void _Single_RightLeft(Node* parent)//右左雙旋
{
	//記錄節點的位置
	Node* SubR = parent->_Right;
	Node* SubRL = SubR->_Left;
	//先右旋,再左旋
	_Single_Right(parent->_Right);
	_Single_Left(parent);
	//根據SubRL判斷是什么型別,修改平衡因子
	if (SubRL->_bf == 1)//插入到右子樹上
	{
		SubRL->_bf = 0;
		SubR->_bf = 0;
		parent->_bf = -1;
	}
	else if (SubRL->_bf == -1)//插入到左子樹上
	{
		parent->_bf = 0;
		SubRL->_bf = 0;
		SubR->_bf = 1;
	}
	else if (SubRL->_bf == 0)//特殊情況,插入后只有三個節點,三個節點的平衡因子都為0,
	{
		parent->_bf = 0;
		SubR->_bf = 0;
		SubRL->_bf = 0;
	}
	else//發生錯誤
	{
		assert(false);
	}
}

AVL樹插入C++實作

pair<Node*,bool> Insert(const pair<Key, Val>& kv)//向AVL樹中插入節點,插入成功回傳這個位置的指標和true,失敗回傳這個位置的指標和false
{
	//根節點為空
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return make_pair(_root, true);
	}
	//普通平衡二叉樹的插入(不允許鍵值冗余)
	Node* parent = nullptr; Node* cur = _root;
	while (cur != nullptr)
	{
		if (kv.first > cur->_KV.first)
		{
			parent = cur;
			cur = cur->_Right;
		}
		else if (kv.first < cur->_KV.first)
		{
			parent = cur;
			cur = cur->_Left;
		}
		else//找到重復鍵值退出回圈
		{
			return make_pair(cur, false);
		}
	}
	//cur==nullptr
	cur = new Node(kv);
	Node* NewCur = cur;//保存新插入的節點,來回傳
	if (parent->_KV.first > kv.first)
	{
		cur->_Parent = parent;//鏈接上一個節點
		parent->_Left = cur;
	}
	else
	{
		cur->_Parent = parent;
		parent->_Right = cur;
	}
	/*------------------------------------------------ */
		//更新平衡因子(右-左)
	while (parent != nullptr)
	{
		//判斷插入位置
		if (parent->_Left == cur)
		{
			parent->_bf--;
		}
		else
		{
			parent->_bf++;
		}
		if (parent->_bf == 0)
		{
			break;//說明插入后仍然滿足AVL樹,不需要調整了,插入結束,
		}
		else if (parent->_bf == 1 || parent->_bf == -1)//高度改變,繼續向上調整
		{
			cur = parent;
			parent = parent->_Parent;//繼續向上調整
		}
		else if (parent->_bf == 2 || parent->_bf == -2)//不滿足AVL樹特征,需要調整樹結構
		{
			//旋轉,對應四種不同的旋轉,看節點的平衡因子
			if (parent->_bf == -2)
			{
				if (cur->_bf == -1)//右單旋
				{
					_Single_Right(parent);
				}
				else if (cur->_bf == 1)//左右雙旋
				{
					_Single_LeftRight(parent);
				}
			}
			else//parent->_bf==2
			{
				if (cur->_bf == 1)//左單旋
				{
					_Single_Left(parent);
				}
				else if (cur->_bf == -1)//右左雙旋
				{
					_Single_RightLeft(parent);
				}
			}
			break;
		}
		else//發生錯誤
		{
			assert(false);
		}
	}
	return make_pair(NewCur, true);
}

②判斷是否是AVL樹代碼

思路:通過計算每個節點的左子樹和右子樹的差值的絕對值<1,并且這個差值和這個節點的平衡因子相同

二叉樹節點高度=這個節點左樹高度與這個節點右樹高度的最大值+1,

先檢查這個節點是不是符合AVL樹,再遞回檢查其他節點

//私有:
bool _IsAVLTree(Node* _root)
{
	if (_root == nullptr)
		return true;
	int LeftHeight = _Height(_root->_Left);
	int RightHeight = _Height(_root->_Right);
	if (RightHeight - LeftHeight != _root->_bf)//發生錯誤
	{
		cout << "平衡因子錯誤" << endl;
		return false;
	}
	return abs(LeftHeight - RightHeight) < 2//檢查左右子樹的高度再遞回檢查左子樹和右子樹
		&& _IsAVLTree(_root->_Left) && _IsAVLTree(_root->_Right);
}

int _Height(Node* root)
{
	if (root == nullptr)
		return 0;
	int LeftTreeHeight = _Height(root->_Left);
	int RightTreeHeight = _Height(root->_Right);
	return max(LeftTreeHeight, RightTreeHeight) + 1;
}
//共有
template<class Key, class Val>
bool AVLTree<Key, Val>::IsAVLTree()
{
	return _IsAVLTree(_root);
}

③AVL樹通過鍵值C++查找代碼

與搜索二叉樹查找類似,遍歷樹即可

Node* Find(const Key& key)//通過鍵值來查找對應鍵值節點,成功回傳對應節點的指標,失敗回傳空
{
	Node* cur = _root;
	while (cur!=nullptr)
	{
		if (cur->_KV.first < key)
		{
			cur = cur->_Right;
		}
		else if (cur->_KV.first > key)
		{
			cur = cur->_Left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

④AVL樹的C解構式

這棵樹節點是由我們動態開辟的,為了防止記憶體泄漏,要手動寫解構式

思路:先遞回銷毀左子樹,再遞回銷毀右子樹,最后銷毀節點,后序遍歷

//私有
void _Destory(Node* root)
{
	if (root == nullptr)
		return;
	_Destory(root->_Left);
	_Destory(root->_Right);
	delete root;
}

~AVLTree()
{
	_Destory(_root);
}

⑤利用插入函式的回傳值多載[]實作AVL樹的修改

具體思路見博客:
利用插入函式回傳值多載思路

這里直接寫C++代碼

Val& operator[](const Key& key)
{
	pair<Node*, bool>ret = Insert(make_pair(key, Val()));//利用插入函式的回傳值
	return ret.first->_KV.second;
}

4.AVL樹插入,修改C++代碼

#pragma once

#include<iostream>
#include<assert.h>

using namespace std;

template<class Key,class Val>
struct AVLNode
{
	AVLNode<Key, Val>* _Left;
	AVLNode<Key, Val>* _Right;
	AVLNode<Key, Val>* _Parent;

	int _bf;//平衡因子
	pair<Key, Val> _KV;
	AVLNode(const pair<Key, Val>& kv) :_Left(nullptr), _Right(nullptr)
		, _Parent(nullptr), _bf(0), _KV(kv)
	{}
};

template<class Key, class Val>
class AVLTree
{
public:
	typedef AVLNode<Key, Val> Node;
	AVLTree() :_root(nullptr) {}

	~AVLTree()
	{
		_Destory(_root);
	}

	//[]運算子多載
	Val& operator[](const Key& key)
	{
		pair<Node*, bool>ret = Insert(make_pair(key, Val()));//利用插入函式的回傳值
		return ret.first->_KV.second;
	}

	pair<Node*,bool> Insert(const pair<Key, Val>& kv)//向AVL樹中插入節點,插入成功回傳這個位置的指標和true,失敗回傳這個位置的指標和false
	{
		//根節點為空
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return make_pair(_root, true);
		}
		//普通平衡二叉樹的插入(不允許鍵值冗余)
		Node* parent = nullptr; Node* cur = _root;
		while (cur != nullptr)
		{
			if (kv.first > cur->_KV.first)
			{
				parent = cur;
				cur = cur->_Right;
			}
			else if (kv.first < cur->_KV.first)
			{
				parent = cur;
				cur = cur->_Left;
			}
			else//找到重復鍵值退出回圈
			{
				return make_pair(cur, false);
			}
		}
		//cur==nullptr
		cur = new Node(kv);
		Node* NewCur = cur;//保存新插入的節點,來回傳
		if (parent->_KV.first > kv.first)
		{
			cur->_Parent = parent;//鏈接上一個節點
			parent->_Left = cur;
		}
		else
		{
			cur->_Parent = parent;
			parent->_Right = cur;
		}
		/*------------------------------------------------ */
			//更新平衡因子(右-左)
		while (parent != nullptr)
		{
			//判斷插入位置
			if (parent->_Left == cur)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			if (parent->_bf == 0)
			{
				break;//說明插入后仍然滿足AVL樹,不需要調整了,插入結束,
			}
			else if (parent->_bf == 1 || parent->_bf == -1)//高度改變,繼續向上調整
			{
				cur = parent;
				parent = parent->_Parent;//繼續向上調整
			}
			else if (parent->_bf == 2 || parent->_bf == -2)//不滿足AVL樹特征,需要調整樹結構
			{
				//旋轉,對應四種不同的旋轉,看節點的平衡因子
				if (parent->_bf == -2)
				{
					if (cur->_bf == -1)//右單旋
					{
						_Single_Right(parent);
					}
					else if (cur->_bf == 1)//左右雙旋
					{
						_Single_LeftRight(parent);
					}
				}
				else//parent->_bf==2
				{
					if (cur->_bf == 1)//左單旋
					{
						_Single_Left(parent);
					}
					else if (cur->_bf == -1)//右左雙旋
					{
						_Single_RightLeft(parent);
					}
				}
				break;
			}
			else//發生錯誤
			{
				assert(false);
			}
		}
		return make_pair(NewCur, true);
	}

	Node* Find(const Key& key)//通過鍵值來查找對應鍵值節點,成功回傳對應節點的指標,失敗回傳空
	{
		Node* cur = _root;
		while (cur!=nullptr)
		{
			if (cur->_KV.first < key)
			{
				cur = cur->_Right;
			}
			else if (cur->_KV.first > key)
			{
				cur = cur->_Left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	void PrintTree()//中序列印AVL樹
	{
		_PrintTree(_root);
	}

	bool IsAVLTree()//檢查是否是AVL樹
	{
		return _IsAVLTree(_root);
	}

private:
	Node* _root;

	void _Destory(Node* root)
	{
		if (root == nullptr)
			return;
		_Destory(root->_Left);
		_Destory(root->_Right);
		delete root;
	}

	bool _IsAVLTree(Node* _root)
	{
		if (_root == nullptr)
			return true;
		int LeftHeight = _Height(_root->_Left);
		int RightHeight = _Height(_root->_Right);
		if (RightHeight - LeftHeight != _root->_bf)//發生錯誤
		{
			cout << "平衡因子錯誤" << endl;
			return false;
		}
		bool tmp = abs(LeftHeight - RightHeight) < 2;
		bool tmp2 = _IsAVLTree(_root->_Left);
		bool tmp3 = _IsAVLTree(_root->_Right);
		/*return abs(LeftHeight - RightHeight) < 2
			&& _IsAVLTree(_root->_Left) && _IsAVLTree(_root->_Right);*/
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int LeftTreeHeight = _Height(root->_Left);
		int RightTreeHeight = _Height(root->_Right);
		return max(LeftTreeHeight, RightTreeHeight) + 1;
	}

	void _PrintTree(Node* root)
	{
		if (root == nullptr)
			return;
		_PrintTree(root->_Left);
		cout << root->_KV.first << "->" << root->_KV.second << endl;
		_PrintTree(root->_Right);
	}
	void _Single_Right(Node* parent)//右單旋根據圖把對應關系連接起來
	{
		//記錄要移動的節點
		Node* SubL = parent->_Left;
		Node* SubLR = SubL->_Right;

		//連接
		parent->_Left = SubLR;
		if (SubL->_Right != nullptr)//修改父指標
		{
			SubLR->_Parent = parent;
		}
		//連接
		SubL->_Right = parent;
		Node* GradParent = parent->_Parent;//記錄這個節點的父節點,為了修改根節點
		parent->_Parent = SubL;//修改父指標
		//修改平衡因子為0
		parent->_bf = 0; SubL->_bf = 0;
		//調整根節點
		if (parent == _root)//要旋轉的節點為根節點
		{
			_root = SubL;
			SubL->_Parent = GradParent;
		}
		else//要旋轉的節點是子樹,修改GradParent指標
		{
			if (GradParent->_Left == parent)
			{
				GradParent->_Left = SubL;
			}
			else
			{
				GradParent->_Right = SubL;
			}
			SubL->_Parent = GradParent;
		}
	}

	void _Single_Left(Node* parent)//左旋轉
	{
		//記錄要移動的節點
		Node* SubR = parent->_Right;
		Node* SubRL = SubR->_Left;

		//連接
		parent->_Right = SubRL;
		if (SubRL != nullptr)
		{
			SubRL->_Parent = parent;
		}
		SubR->_Left = parent;
		Node* GradParent = parent->_Parent;//記錄這個節點的父節點,為了修改根節點
		parent->_Parent = SubR;

		//修改平衡因子
		parent->_bf = 0; SubR->_bf = 0;
		//調整根節點
		if (parent == _root)
		{
			_root = SubR;
			SubR->_Parent = GradParent;
		}
		else //要旋轉的節點是子樹,修改GradParent指標
		{
			if (GradParent->_Left == parent)//旋轉的是左子樹,連接到左邊
			{
				GradParent->_Left = SubR;
			}
			else
			{
				GradParent->_Right = SubR;//反之
			}
			SubR->_Parent = GradParent;
		}
	}

	void _Single_LeftRight(Node* parent)//左右雙旋
	{
		//記錄節點的位置
		Node* SubL = parent->_Left;
		Node* SubLR = SubL->_Right;
		//先左旋,再右旋
		_Single_Left(parent->_Left);
		_Single_Right(parent);
		//根據SubRL判斷是什么型別,修改平衡因子
		if (SubLR->_bf == 1)//插入到右子樹上
		{
			SubLR->_bf = 0;
			SubL->_bf = -1;
			parent->_bf = 0;
		}
		else if (SubLR->_bf == -1)//插入到左子樹上
		{
			parent->_bf = 1;
			SubL->_bf = 0;
			SubLR->_bf = 0;
		}
		else if (SubLR->_bf == 0)//特殊情況,插入后只有三個節點,三個節點的平衡因子都為0,
		{
			parent->_bf = 0;
			SubL->_bf = 0;
			SubLR->_bf = 0;
		}
		else//發生錯誤
		{
			assert(false);
		}
	}

	void _Single_RightLeft(Node* parent)//右左雙旋
	{
		//記錄節點的位置
		Node* SubR = parent->_Right;
		Node* SubRL = SubR->_Left;
		//先右旋,再左旋
		_Single_Right(parent->_Right);
		_Single_Left(parent);
		//根據SubRL判斷是什么型別,修改平衡因子
		if (SubRL->_bf == 1)//插入到右子樹上
		{
			SubRL->_bf = 0;
			SubR->_bf = 0;
			parent->_bf = -1;
		}
		else if (SubRL->_bf == -1)//插入到左子樹上
		{
			parent->_bf = 0;
			SubRL->_bf = 0;
			SubR->_bf = 1;
		}
		else if (SubRL->_bf == 0)//特殊情況,插入后只有三個節點,三個節點的平衡因子都為0,
		{
			parent->_bf = 0;
			SubR->_bf = 0;
			SubRL->_bf = 0;
		}
		else//發生錯誤
		{
			assert(false);
		}
	}
};

測驗代碼

#include"C++_AVL.h"
#include<vector>

void TestAVL_Insert()//檢測插入AVL樹的操作是否正確,并且檢查是否是AVL樹
{
	vector<int>Arr = { 16, 4, 2,29, 9, 26, 10, 25 };
	AVLTree<int, int>AVL;
	for (const auto& e : Arr)
	{
		AVL.Insert(make_pair(e, 1));
		//cout << "插入" << e << "->" << AVL.IsAVLTree() << endl;
	}
	AVL.PrintTree();
	if (AVL.IsAVLTree())
	{
		cout << "AVL樹" << endl;
	}

	//測驗修改AVL樹的值
	AVL[0] = 100;
	AVL[2] = 30;
	AVL.PrintTree();
}

int main()
{
	TestAVL_Insert();
	return 0;
}

運行結果

在這里插入圖片描述

5.代碼Github/Gitree代碼位置

Github鏈接
Gitree鏈接

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/353388.html

標籤:其他

上一篇:[演算法題解詳細]DFS解力扣78子集

下一篇:資料結構: 線段樹Segment Tree

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more