AVL樹和紅黑樹(map和set的底層實現)

map和set的概念及使用

map和set的底層結構

map和set其底層都是按照二叉搜索樹來實現的,但是二叉搜索樹有其自身的缺陷,假如往樹中插入的元素有序或者接近有序,二叉搜索樹就會退化成單支樹,時間複雜度會退化成O(N),因此map、set等關聯式容器的底層結構是對二叉樹進行了平衡處理,即採用平衡樹來實現。

AVL樹

AVL樹的概念

二叉搜索樹雖可以縮短查找的效率,但如果數據有序或接近有序二叉搜索樹將退化爲單支樹,查找元素相當於在順序表中搜索元素,效率低下。因此,兩位俄羅斯的數學家G.M.Adelson-Velskii和E.M.Landis在1962年發明了一種解決上述問題的方法:當向二叉搜索樹中插入新結點後,如果能保證每個結點的左右子樹高度之差的絕對值不超過1(需要對樹中的結點進行調整),即可降低樹的高度,從而減少平均搜索長度。一棵AVL樹或者是空樹,或者是具有以下性質的二叉搜索樹:

  • 它的左右子樹都是AVL樹
  • 左右子樹高度之差(簡稱平衡因子)的絕對值不超過1(-1/0/1)
    在這裏插入圖片描述
    如果一棵二叉搜索樹是高度平衡的,它就是AVL樹。如果它有n個結點,其高度可保持在 O(logN),搜索時間複雜度O(logN )。

AVL樹節點的定義

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _val;
	AVLTreeNode<K, V>* _pLeft;
	AVLTreeNode<K, V>* _pRight;
	AVLTreeNode<K, V>* _pParent;
	int _bf;
	AVLTreeNode(const pair<K,V>& val)
		:_val(val)
		, _pLeft(nullptr)//該節點的左孩子
		, _pRight(nullptr)//該節點的右孩子
		, _pParent(nullptr)//該節點的父節點
		, _bf(0)//該節點的平衡因子
	{}
};

AVL樹的插入

AVL樹就是在二叉搜索樹的基礎上引入了平衡因子,因此AVL樹也可以看成是二叉搜索樹。那麼AVL樹的插入過程可以分爲兩步:

  1. 按照二叉搜索樹的方式插入新節點
  2. 調整節點的平衡因子
bool Insert(const pair<K, V> kv)
	{
	// 1. 先按照二叉搜索樹的規則將節點插入到AVL樹中
    // ...
    
    // 2. 新節點插入後,AVL樹的平衡性可能會遭到破壞,此時就需要更新平衡因子,並檢測是否破壞了AVL樹的平衡性
    
    /*
     pCur插入後,pParent的平衡因子一定需要調整,在插入之前,pParent的平衡因子分爲三種情況:-1,0, 1, 分以下兩種情況:
      1. 如果pCur插入到pParent的左側,只需給pParent的平衡因子-1即可
      2. 如果pCur插入到pParent的右側,只需給pParent的平衡因子+1即可
      此時:pParent的平衡因子可能有三種情況:0,正負1, 正負2
      1. 如果pParent的平衡因子爲0,說明插入之前pParent的平衡因子爲正負1,插入後被調整成0,此時滿足AVL樹的性質,插入成功
      2. 如果pParent的平衡因子爲正負1,說明插入前pParent的平衡因子一定爲0,插入後被更新成正負1,此時以pParent爲根的樹的高度增加,需要繼續向上更新
      3. 如果pParent的平衡因子爲正負2,則pParent的平衡因子違反平衡樹的性質,需要對其進行旋轉處理
    */
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_bf = 0;
			return true;
		}
		Node* Parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_val.first > kv.first)
			{
				Parent = cur;
				cur = cur->_pLeft;
			}
			else if (cur->_val.first < kv.first)
			{
				Parent = cur;
				cur = cur->_pRight;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		if (Parent->_val > cur->_val)
		{
			Parent->_pLeft = cur;
			cur->_pParent = Parent;
		}
		else
		{
			Parent->_pRight = cur;
			cur->_pParent = Parent;
		}
		while (Parent)
		{
			if (cur == Parent->_pLeft)
				Parent->_bf--;
			else
			{
				Parent->_bf++;
			}
			if (Parent->_bf == 0)
				break;
			else if (abs(Parent->_bf) == 1)
			{
				cur = Parent;
				Parent = Parent->_pParent;
			}
			else if (abs(Parent->_bf) == 2)
			{
				if (Parent->_bf == 2){
					if (cur->_bf == 1){
						RotateL(Parent);
					}
					else if (cur->_bf == -1){
						RotateRL(Parent);
					}
				}
				else if (Parent->_bf == -2){
					if (cur->_bf == -1){
						RotateR(Parent);
					}
					else if (cur->_bf == 1){
						RotateLR(Parent);
					}
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

AVL樹的旋轉

如果在一棵原本是平衡的AVL樹中插入一個新節點,可能造成不平衡,此時必須調整樹的結構,使之平衡化。根據節點插入位置的不同,AVL樹的旋轉分爲四種:

1. 新節點插入較高左子樹的左側—左左:右單旋

在這裏插入圖片描述

/*
  上圖在插入前,AVL樹是平衡的,新節點插入到30的左子樹(注意:此處不是左孩子)中,30左子樹增
加
  了一層,導致以60爲根的二叉樹不平衡,要讓60平衡,只能將60左子樹的高度減少一層,右子樹增加
一層,
  即將左子樹往上提,這樣60轉下來,因爲60比30大,只能將其放在30的右子樹,而如果30有右子
樹,右子樹根的值一定大於30,小於60,只能將其放在60的左子樹,旋轉完成後,更新節點的平衡因子
即可。在旋轉過程中,有以下幾種情況需要考慮:
  1. 30節點的右孩子可能存在,也可能不存在
  2. 60可能是根節點,也可能是子樹
     如果是根節點,旋轉完成後,要更新根節點
     如果是子樹,可能是某個節點的左子樹,也可能是右子樹
     
 同學們再此處可舉一些詳細的例子進行畫圖,考慮各種情況,加深旋轉的理解
*/
void _RotateR(PNode pParent)
{
    // pSubL: pParent的左孩子
    // pSubLR: pParent左孩子的右孩子,注意:該
    PNode pSubL = pParent->_pLeft;
    PNode pSubLR = pSubL->_pRight;
 
    // 旋轉完成之後,30的右孩子作爲雙親的左孩子
    pParent->_pLeft = pSubLR;
    // 如果30的左孩子的右孩子存在,更新親雙親
    if(pSubLR)
        pSubLR->_pParent = pParent;
 
    // 60 作爲 30的右孩子
    pSubL->_pRight = pParent;
    // 因爲60可能是棵子樹,因此在更新其雙親前必須先保存60的雙親
    PNode pPParent = pParent->_pParent;
    
    // 更新60的雙親
    pParent->_pParent = pSubL;
    
    // 更新30的雙親
    pSubL->_pParent = pPParent;
 
    // 如果60是根節點,根新指向根節點的指針
    if(NULL == pPParent)
    {
        _pRoot = pSubL;
        pSubL->_pParent = NULL;
    }
    else
    {
         // 如果60是子樹,可能是其雙親的左子樹,也可能是右子樹
        if(pPParent->_pLeft == pParent)
            pPParent->_pLeft = pSubL;
        else
            pPParent->_pRight = pSubL;
    }
 
    // 根據調整後的結構更新部分節點的平衡因子
    pParent->_bf = pSubL->_bf = 0;
}
2. 新節點插入較高右子樹的右側—右右:左單旋

在這裏插入圖片描述

//左單旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;
		parent->_pRight = subRL;
		if (subRL)
			subRL->_pParent = parent;

		subR->_pLeft = parent;
		Node* ppNode = parent->_pParent;
		parent->_pParent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_pParent = nullptr;
		}
		else
		{
			if (ppNode->_pLeft == subR)
				ppNode->_pLeft = subR;
			else
			{
				ppNode->_pRight = subR;
			}
			subR->_pParent = ppNode;
		}
		parent->_bf = subR->_bf = 0;
	}
3. 新節點插入較高左子樹的右側—左右:先左單旋再右單旋

在這裏插入圖片描述
將雙旋變成單旋後再旋轉,即:先對30進行左單旋,然後再對90進行右單旋,旋轉完成後再考慮平衡因子的更新。

// 旋轉之前,60的平衡因子可能是-1/0/1,旋轉完成之後,根據情況對其他節點的平衡因子進行調整
void _RotateLR(PNode pParent)
{
    PNode pSubL = pParent->_pLeft;
    PNode pSubLR = pSubL->_pRight;
    
    // 旋轉之前,保存pSubLR的平衡因子,旋轉完成之後,需要根據該平衡因子來調整其他節點的平
衡因子
    int bf = pSubLR->_bf;
    
    // 先對30進行左單旋
    _RotateL(pParent->_pLeft);
    
    // 再對90進行右單旋
    _RotateR(pParent);
 
    if(1 == bf)
        pSubL->_bf = -1;
    else if(-1 == bf)
        pParent->_bf = 1;
}
4. 新節點插入較高右子樹的左側—右左:先右單旋再左單旋

在這裏插入圖片描述

void RotateRL(Node* parent)
	{
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;
		int bf = subRL->_bf;	//保存subRL的平衡因子,因爲在下面兩行會置 0

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

		if (bf == 0){
			parent->_bf = subRL->_bf = subR->_bf = 0;
		}
		else if (bf == 1){		//在c插入的新結點
			subR->_bf = 0;
			parent->_bf = -1;
			subRL->_bf = 0;
		}
		else if (bf == -1){		//在b插入的新結點
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
	}

總結:
假如以pParent爲根的子樹不平衡,即pParent的平衡因子爲2或者-2,分以下情況考慮

  • pParent的平衡因子爲2,說明pParent的右子樹高,設pParent的右子樹的根爲pSubR
    • 當pSubR的平衡因子爲1時,執行左單旋
    • 當pSubR的平衡因子爲-1時,執行右左雙旋
  • pParent的平衡因子爲-2,說明pParent的左子樹高,設pParent的左子樹的根爲pSubL
    • 當pSubL的平衡因子爲-1是,執行右單旋
    • 當pSubL的平衡因子爲1時,執行左右雙旋

旋轉完成後,原pParent爲根的子樹個高度降低,已經平衡,不需要再向上更新。

AVL樹的驗證

AVL樹是在二叉搜索樹的基礎上加入了平衡性的限制,因此要驗證AVL樹,可以分兩步:

  1. 驗證其爲二叉搜索樹
    如果中序遍歷可得到一個有序的序列,就說明爲二叉搜索樹
  2. 驗證其爲平衡樹
    每個節點子樹高度差的絕對值不超過1(注意節點中如果沒有平衡因子)節點的平衡因子是否計算正確
int _Height(PNode pRoot);
bool _IsBalanceTree(PNode pRoot)
{
	// 空樹也是AVL樹
     if (nullptr == pRoot) return true;
    
     // 計算pRoot節點的平衡因子:即pRoot左右子樹的高度差
     int leftHeight = _Height(pRoot->_pLeft);
     int rightHeight = _Height(pRoot->_pRight);
     int diff = rightHeight - leftHeight;
 
     // 如果計算出的平衡因子與pRoot的平衡因子不相等,或者
     // pRoot平衡因子的絕對值超過1,則一定不是AVL樹
     if (diff != pRoot->_bf || (diff > 1 || diff < -1))
         return false;
 
     // pRoot的左和右如果都是AVL樹,則該樹一定是AVL樹
     return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);
 }

AVL樹的刪除

因爲AVL樹也是二叉搜索樹,可按照二叉搜索樹的方式將節點刪除,然後再更新平衡因子,只不錯與刪除不同的時,刪除節點後的平衡因子更新,最差情況下一直要調整到根節點的位置。

AVL樹的性能

AVL樹是一棵絕對平衡的二叉搜索樹,其要求每個節點的左右子樹高度差的絕對值都不超過1,這樣可以保證查詢時高效的時間複雜度,即 。但是如果要對AVL樹做一些結構修改的操作,性能非常低下,比如:

  • 插入時要維護其絕對平衡,旋轉的次數比較多,更差的是在刪除時,有可能一直要讓旋轉持續到根的位置。

因此:如果需要一種查詢高效且有序的數據結構,而且數據的個數爲靜態的(即不會改變),可以考慮AVL樹,但一個結構經常修改,就不太適合。

紅黑樹

紅黑樹的概念

紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
在這裏插入圖片描述

紅黑樹的性質

  1. 每個結點不是紅色就是黑色
  2. 根節點是黑色的
  3. 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
  4. 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
  5. 每個葉子結點都是黑色的(此處的葉子結點指的是空結點)

紅黑樹節點的定義

// 節點的顏色
 enum Color{RED, BLACK};
 
 // 紅黑樹節點的定義
 template<class ValueType>
 struct RBTreeNode
 {
     RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
         : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
         , _data(data), _color(color)
     {}
 
     RBTreeNode<ValueType>* _pLeft;   // 節點的左孩子
     RBTreeNode<ValueType>* _pRight;  // 節點的右孩子
     RBTreeNode<ValueType>* _pParent; // 節點的雙親(紅黑樹需要旋轉,爲了實現簡單給出該字段)
     ValueType _data;            // 節點的值域
     Color _color;               // 節點的顏色
 };

紅黑樹結構

爲了後續實現關聯式容器簡單,紅黑樹的實現中增加一個頭結點,因爲跟節點必須爲黑色,爲了與根節點進行區分,將頭結點給成黑色,並且讓頭結點的 pParent 域指向紅黑樹的根節點,pLeft域指向紅黑樹中最小的節點,_pRight域指向紅黑樹中最大的節點,如下:
在這裏插入圖片描述

紅黑樹的插入操作

紅黑樹是在二叉搜索樹的基礎上加上其平衡限制條件,因此紅黑樹的插入可分爲兩步:

1. 按照二叉搜索的樹規則插入新節點
template<class ValueType>
class RBTree{
	typedef RBTreeNode Node;
public:
	bool Insert(const ValueType& data)
	{
		……
		PNode& pRoot = GetRoot();
		if (nullptr == pRoot)
		{
			pRoot = new Node(data, BLACK);
			// 根的雙親爲頭節點
			pRoot->_pParent = _pHead;
			_pHead->_pParent = pRoot;
		}
		else
		{
			// 1. 按照二叉搜索的樹方式插入新節點
			// 2. 檢測新節點插入後,紅黑樹的性質是否造到破壞,
			//    若滿足直接退出,否則對紅黑樹進行旋轉着色處理
		}

		// 根節點的顏色可能被修改,將其改回黑色

		pRoot->_color = BLACK;
		_pHead->_pLeft = LeftMost();
		_pHead->_pRight = RightMost();
		return true;
	}
private:
	PNode& GetRoot(){ return _pHead->_pParent; }
	// 獲取紅黑樹中最小節點,即最左側節點
	PNode LeftMost();
	// 獲取紅黑樹中最大節點,即最右側節點
	PNode RightMost();
private:
	Node* _root;
};
2. 檢測新節點插入後,紅黑樹的性質是否造到破壞

因爲新節點的默認顏色是紅色,因此:如果其雙親節點的顏色是黑色,沒有違反紅黑樹任何性質,則不需要調整;但當新插入節點的雙親節點顏色爲紅色時,就違反了性質三不能有連在一起的紅色節點,此時需要對紅黑樹分情況來討論:

  • 約定:cur爲當前節點,p爲父節點,g爲祖父節點,u爲叔叔節點
    • 情況一: cur爲紅,p爲紅,g爲黑,u存在且爲紅
      在這裏插入圖片描述
      解決方式:將p,u改爲黑,g改爲紅,然後把g當成cur,繼續向上調整。
    • 情況二: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
      在這裏插入圖片描述
      p爲g的左孩子,cur爲p的左孩子,則進行右單旋轉;相反,
      p爲g的右孩子,cur爲p的右孩子,則進行左單旋轉
      p、g變色–p變黑,g變紅
  • 情況三: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
    在這裏插入圖片描述
    p爲g的左孩子,cur爲p的右孩子,則針對p做左單旋轉;相反,
    p爲g的右孩子,cur爲p的左孩子,則針對p做右單旋轉
    則轉換成了情況2
pair<iterator, bool> Insert(const T& val){
		//插入節點
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_color = BLACK;
			return make_pair(iterator(_root), true);
		}
		//尋找位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_data < val){
				parent = cur;
				cur = cur->_pRight;
			}
			else if (cur->_data > val){
				parent = cur;
				cur = cur->_pLeft;
			}
			else{
				return make_pair(iterator(cur), false);
			}
		}
		
		cur = new Node(val);
		Node* newnode = cur;
		cur->_color = RED;
		//插入適當位置
		if (parent->_data < val){
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		else{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		//變色處理
		//父節點爲紅色節點
		while (parent && parent->_color == RED){
			Node* grandfather = parent->_pParent;
			//父親是祖父節點左孩子
			if (parent == grandfather->_pLeft){
				Node* uncle = grandfather->_pRight;
				// 1.叔叔節點存在且爲紅
				if (uncle && uncle->_color == RED){
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					parent = cur->_pParent;
				}
				//叔叔節點不存在或者叔叔節點爲黑
				else{
					if (cur == parent->_pRight){
						RotateL(parent);
						swap(parent, cur);
					}

					RotateR(grandfather);
					parent->_color = BLACK;
					grandfather->_color = RED;

					break;
				}
			}
			//父親節點是祖父節點右孩子
			else{
				Node* uncle = grandfather->_pLeft;
				if (uncle && uncle->_color == RED){
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					parent = cur->_pParent;
				}
				else{
					if (cur == parent->_pLeft){
						RotateR(parent);
						swap(parent, cur);
					}

					RotateL(grandfather);
					parent->_color = BLACK;
					grandfather->_color = RED;
					break;
				}
			}
		}

		_root->_color = BLACK;

		return make_pair(iterator(newnode), true);
	}

	void RotateL(Node* parent){
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;

		parent->_pRight = subRL;
		if (subRL)
			subRL->_pParent = parent;

		subR->_pLeft = parent;
		Node* pNode = parent->_pParent;
		parent->_pParent = subR;

		if (parent == _root){
			_root = subR;
			_root->_pParent = nullptr;
		}
		else{
			if (pNode->_pLeft == parent)
				pNode->_pLeft = subR;
			else
				pNode->_pRight = subR;

			subR->_pParent = pNode;
		}

	}

	void RotateR(Node* parent){
		Node* subL = parent->_pLeft;
		Node* subLR = subL->_pRight;

		parent->_pLeft = subLR;
		if (subLR)
			subLR->_pParent = parent;

		subL->_pRight = parent;

		Node* pNode = parent->_pParent;
		parent->_pParent = subL;

		if (pNode == nullptr){
			_root = subL;
			_root->_pParent = nullptr;
		}
		else{
			if (pNode->_pLeft == parent){
				pNode->_pLeft = subL;
			}
			else{
				pNode->_pRight = subL;
			}

			subL->_pParent = pNode;
		}
	}

紅黑樹的驗證

紅黑樹的檢測分爲兩步:

  1. 檢測其是否滿足二叉搜索樹(中序遍歷是否爲有序序列)
  2. 檢測其是否滿足紅黑樹的性質
bool IsValidRBTree(){
		Node* pRoot = _root;
		// 空樹也是紅黑樹
		if (nullptr == pRoot)
			return true;

		// 檢測根節點是否滿足情況
		if (BLACK != pRoot->_color){
			cout << "違反紅黑樹性質二:根節點必須爲黑色" << endl;
			return false;
		}

		// 獲取任意一條路徑中黑色節點的個數
		size_t blackCount = 0;
		Node* pCur = pRoot;
		while (pCur){
			if (BLACK == pCur->_color)
				blackCount++;

			pCur = pCur->_pLeft;
		}

		// 檢測是否滿足紅黑樹的性質,k用來記錄路徑中黑色節點的個數
		size_t k = 0;
		return _IsValidRBTree(pRoot, k, blackCount);
	}

	bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){
		//走到null之後,判斷k和black是否相等
		if (nullptr == pRoot){
			if (k != blackCount){
				cout << "違反性質四:每條路徑中黑色節點的個數必須相同" << endl;
				return false;
			}
			return true;
		}

		// 統計黑色節點的個數
		if (BLACK == pRoot->_color)
			k++;

		// 檢測當前節點與其雙親是否都爲紅色
		Node* pParent = pRoot->_pParent;
		if (pParent && RED == pParent->_color && RED == pRoot->_color){
			cout << "違反性質三:沒有連在一起的紅色節點" << endl;
			return false;
		}

		return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
			_IsValidRBTree(pRoot->_pRight, k, blackCount);
	}

紅黑樹與AVL樹的比較

紅黑樹和AVL樹都是高效的平衡二叉樹,增刪改查的時間複雜度都是O(log N),紅黑樹不追求絕對平衡,其只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉的次數,所以在經常進行增刪的結構中性能比AVL樹更優,而且紅黑樹實現比較簡單,所以實際運用中紅黑樹更多。

紅黑樹的迭代器

迭代器的好處是可以方便遍歷,是數據結構的底層實現與用戶透明。如果想要給紅黑樹增加迭代器,需要考慮以前問題:

  • begin()與end()
    STL明確規定,begin()與end()代表的是一段前閉後開的區間,而對紅黑樹進行中序遍歷後,可以得到一個有序的序列,因此:begin()可以放在紅黑樹中最小節點(即最左側節點)的位置,end()放在最大節點
    (最右側節點)的下一個位置,關鍵是最大節點的下一個位置在哪塊?能否給成nullptr呢?答案是行不通的,因爲對end()位置的迭代器進行–操作,必須要能找最後一個元素,此處就不行,因此最好的方式是將end()放在頭結點的位置:
    在這裏插入圖片描述
// 找迭代器的下一個節點,下一個節點肯定比其大
void Increasement()
{
    //分兩種情況討論:_pNode的右子樹存在和不存在
    // 右子樹存在
    if(_pNode->_pRight)
    {
         // 右子樹中最小的節點,即右子樹中最左側節點
        _pNode = _pNode->_pRight;
        while(_pNode->_pLeft)
            _pNode = _pNode->_pLeft;
    }
    else
    {
         // 右子樹不存在,向上查找,直到_pNode != pParent->right
        PNode pParent = _pNode->_pParent;
        while(pParent->_pRight == _pNode)
        {
            _pNode = pParent;
            pParent = _pNode->_pParent;
        }
 
        // 特殊情況:根節點沒有右子樹
        if(_pNode->_pRight != pParent)
            _pNode = pParent;
    }
}
 
// 獲取迭代器指向節點的前一個節點
void Decreasement()
{
     //分三種情況討論:_pNode 在head的位置,_pNode 左子樹存在,_pNode 左子樹不存在
     // 1. _pNode 在head的位置,--應該將_pNode放在紅黑樹中最大節點的位置
    if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
        _pNode = _pNode->_pRight;
    else if(_pNode->_pLeft)
    {
        // 2. _pNode的左子樹存在,在左子樹中找最大的節點,即左子樹中最右側節點
        _pNode = _pNode->_pLeft;
        while(_pNode->_pRight)
            _pNode = _pNode->_pRight;
    }
    else
    {
         // _pNode的左子樹不存在,只能向上找
        PNode pParent = _pNode->_pParent;
        while(_pNode == pParent->_pLeft)
        {
            _pNode = pParent;
            pParent = _pNode->_pParent;
        }
        _pNode = pParent;
    }
}

RBTree的完整代碼

#include<iostream>
using namespace std;

enum Color{
	RED,
	BLACK
};
template<class ValueType>
struct RBTreeNode
{
	RBTreeNode<ValueType>* _pLeft;
	RBTreeNode<ValueType>* _pRight;
	RBTreeNode<ValueType>* _pParent;
	ValueType _data;
	Color _color;
	RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
		:_pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _color(color)
	{}
};

template<class T,class Ptr,class Ref>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ptr, Ref> Self;
	Node* _node;
	RBTreeIterator(Node* node)
		:_node(node)
	{}
	Ref operator*(){
		return _node->_data;
	}
	Ref operator->(){
		return &_node->_data;
	}
	Self& operator++()
	{
		if (_node->_pRight != nullptr)
		{
			_node = _node->_pRight;
			while (_node->_pLeft != nullptr)
			{
				_node = _node->_pLeft;
			}
		}
		else
		{
			Node* parent = _node->_pParent;
			while (parent != nullptr && _node == parent->_pRight){
				_node = parent;
				parent = _node->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self operator++(int){
		Self tmp(*this);
		++(*this);
		return tmp;
	}
	Self& operator--(){
		if (_node->_pLeft != nullptr)
		{
			_node = _node->_pLeft;
			while (_node->_pLeft != nullptr)
			{
				_node = _node->_pRight;
			}
		}
		else
		{
			Node* parent = _node->_pParent;
			while (parent != nullptr && _node == parent->_pLeft){
				_node = parent;
				parent = _node->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	Self operator--(int){
		Self tmp(*this);
		--(*this);
		return tmp;
	}

	bool operator != (const Self& s) const{
		return _node != s._node;
	}

	bool operator == (const Self& s) const{
		return _node == s._node;
	}
};

template<class K, class T>
class RBTree{
	typedef RBTreeNode<T> Node;
public:
	typedef RBTreeIterator<T, T*, T&> iterator;
	typedef RBTreeIterator<T, const T*, const T&> const_iterator;

	RBTree() = default;
	// 拷貝構造 + operator=
	// 析構函數

	iterator begin(){
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return iterator(cur);
	}

	iterator end(){
		return iterator(nullptr);
	}

	const_iterator begin() const{
		Node* cur = _root;
		while (cur && cur->_left){
			cur = cur->_left;
		}

		return const_iterator(cur);
	}

	const_iterator end() const{
		return const_iterator(nullptr);
	}
	
	pair<iterator, bool> Insert(const T& val){
		//插入節點
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_color = BLACK;
			return make_pair(iterator(_root), true);
		}
		//尋找位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_data < val){
				parent = cur;
				cur = cur->_pRight;
			}
			else if (cur->_data > val){
				parent = cur;
				cur = cur->_pLeft;
			}
			else{
				return make_pair(iterator(cur), false);
			}
		}
		
		cur = new Node(val);
		Node* newnode = cur;
		cur->_color = RED;
		//插入適當位置
		if (parent->_data < val){
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		else{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		//變色處理
		//父節點爲紅色節點
		while (parent && parent->_color == RED){
			Node* grandfather = parent->_pParent;
			//父親是祖父節點左孩子
			if (parent == grandfather->_pLeft){
				Node* uncle = grandfather->_pRight;
				// 1.叔叔節點存在且爲紅
				if (uncle && uncle->_color == RED){
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					parent = cur->_pParent;
				}
				//叔叔節點不存在或者叔叔節點爲黑
				else{
					if (cur == parent->_pRight){
						RotateL(parent);
						swap(parent, cur);
					}

					RotateR(grandfather);
					parent->_color = BLACK;
					grandfather->_color = RED;

					break;
				}
			}
			//父親節點是祖父節點右孩子
			else{
				Node* uncle = grandfather->_pLeft;
				if (uncle && uncle->_color == RED){
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;

					cur = grandfather;
					parent = cur->_pParent;
				}
				else{
					if (cur == parent->_pLeft){
						RotateR(parent);
						swap(parent, cur);
					}

					RotateL(grandfather);
					parent->_color = BLACK;
					grandfather->_color = RED;
					break;
				}
			}
		}

		_root->_color = BLACK;

		return make_pair(iterator(newnode), true);
	}

	void RotateL(Node* parent){
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;

		parent->_pRight = subRL;
		if (subRL)
			subRL->_pParent = parent;

		subR->_pLeft = parent;
		Node* pNode = parent->_pParent;
		parent->_pParent = subR;

		if (parent == _root){
			_root = subR;
			_root->_pParent = nullptr;
		}
		else{
			if (pNode->_pLeft == parent)
				pNode->_pLeft = subR;
			else
				pNode->_pRight = subR;

			subR->_pParent = pNode;
		}

	}

	void RotateR(Node* parent){
		Node* subL = parent->_pLeft;
		Node* subLR = subL->_pRight;

		parent->_pLeft = subLR;
		if (subLR)
			subLR->_pParent = parent;

		subL->_pRight = parent;

		Node* pNode = parent->_pParent;
		parent->_pParent = subL;

		if (pNode == nullptr){
			_root = subL;
			_root->_pParent = nullptr;
		}
		else{
			if (pNode->_pLeft == parent){
				pNode->_pLeft = subL;
			}
			else{
				pNode->_pRight = subL;
			}

			subL->_pParent = pNode;
		}
	}



	iterator Find(const K& k){
		Node* cur = _root;
		while (cur){
			KeyOfValue kov;
			if (kov(cur->_data) < k){
				cur = cur->_pRight;
			}
			else if (kov(cur->_data) > k){
				cur = cur->_pLeft;
			}
			else{
				return iterator(cur);
			}
		}

		return end();
	}

	bool IsValidRBTree(){
		Node* pRoot = _root;
		// 空樹也是紅黑樹
		if (nullptr == pRoot)
			return true;

		// 檢測根節點是否滿足情況
		if (BLACK != pRoot->_color){
			cout << "違反紅黑樹性質二:根節點必須爲黑色" << endl;
			return false;
		}

		// 獲取任意一條路徑中黑色節點的個數
		size_t blackCount = 0;
		Node* pCur = pRoot;
		while (pCur){
			if (BLACK == pCur->_color)
				blackCount++;

			pCur = pCur->_pLeft;
		}

		// 檢測是否滿足紅黑樹的性質,k用來記錄路徑中黑色節點的個數
		size_t k = 0;
		return _IsValidRBTree(pRoot, k, blackCount);
	}

	bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){
		//走到null之後,判斷k和black是否相等
		if (nullptr == pRoot){
			if (k != blackCount){
				cout << "違反性質四:每條路徑中黑色節點的個數必須相同" << endl;
				return false;
			}
			return true;
		}

		// 統計黑色節點的個數
		if (BLACK == pRoot->_color)
			k++;

		// 檢測當前節點與其雙親是否都爲紅色
		Node* pParent = pRoot->_pParent;
		if (pParent && RED == pParent->_color && RED == pRoot->_color){
			cout << "違反性質三:沒有連在一起的紅色節點" << endl;
			return false;
		}

		return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
			_IsValidRBTree(pRoot->_pRight, k, blackCount);
	}

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

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

		_InOrder(root->_pLeft);
		cout << root->_data << " ";
		_InOrder(root->_pRight);
	}

private:
	Node* _root = nullptr;
};

void TestRBtree(){
	RBTree<int, int> t;
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, };
 
	for (auto e : a){
		t.Insert(e);
	}
 
	t.InOrder();
	cout << t.IsValidRBTree() << endl;
}

map的模擬實現

#include"RBTree.h"

namespace fxl
{
	template<class K, class V>
	class map
	{
		typedef pair<K, V> ValueType;
		// 作用:將value中的key提取出來
		struct KeyOfValue
		{
			const K& operator()(const ValueType& v)
			{
				return v.first;
			}
		};
		typedef RBTree<K, ValueType, KeyOfValue> RBTree;
	public:
		typedef typename RBTree::Iterator iterator;
	public:
		map(){}

		iterator begin(){ return _t.Begin(); }
		iterator end(){ return _t.End(); }


		size_t size()const{ return _t.Size(); }
		bool empty()const{ return _t.Empty(); }

		V& operator[](const K& key)
		{
			return (*(_t.Insert(ValueType(key, V()))).first).second;
		}
		const V& operator[](const K& key)const;

		pair<iterator, bool> insert(const ValueType& data) { return _t.Insert(data); }
		void clear(){ _t.Clear(); }
		iterator find(const K& key){ return _t.Find(key); }
	private:
		RBTree _t;
	};
}

set的模擬實現

#include"RBTree.h"

namespace fxl
{
	template<class K>
	class set
	{
		typedef K ValueType;

		struct KeyOfValue
		{
			const K& operator()(const ValueType& key)
			{
				return key;
			}
		};

		// 紅黑樹類型重命名
		typedef RBTree<K, ValueType, KeyOfValue> RBTree;
	public:
		typedef typename RBTree::Iterator iterator;
	public:
		Set(){}

		iterator begin(){ return _t.Begin(); }
		iterator end(){ return _t.End(); }


		size_t size()const{ return _t.Size(); }
		bool empty()const{ return _t.Empty(); }

		pair<iterator, bool> insert(const ValueType& data)
		{
			return _t.Insert(data);
		}
		void clear(){ _t.Clear(); }
		iterator find(const K& key){ return _t.Find(key); }

	private:
		RBTree _t;
	};
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章