溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

數(shù)據(jù)結(jié)構(gòu)之——紅黑樹

發(fā)布時間:2020-06-24 20:29:05 來源:網(wǎng)絡(luò) 閱讀:427 作者:給我個bit位 欄目:編程語言

    紅黑樹是一棵二叉搜索樹,它在每個節(jié)點上增加了一個存儲位來表示節(jié)點的顏色,可以是red或black。通過對任何一條從根到葉子簡單路徑上的顏色來約束,紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似于平衡。


紅黑樹是滿足下面紅黑性質(zhì)的二叉搜索樹:

  1. 每個節(jié)點,不是紅色就是黑色的

  2. 根節(jié)點是黑色的

  3. 如果一個節(jié)點是紅色的,則它的兩個子節(jié)點是黑色的(沒有連續(xù)的紅節(jié)點)

  4. 對每個節(jié)點,從該節(jié)點到其所有后代葉節(jié)點的簡單路徑上,均包含相同數(shù)目的黑色節(jié)點。(每條路徑的黑色節(jié)點的數(shù)量相等


這里分析一下為什么紅黑樹能保證最長路徑不超過最短路徑的兩倍:首先因為第4條約束條件假設(shè)一棵樹如下所示:

   B

 B   B

B      B    B表示為黑色結(jié)點,那么要在其中插入任何一個黑色結(jié)點就需要保證第4條約定,而如果要插入紅色結(jié)點,則第3條約束又使得紅色結(jié)點只能插在黑色結(jié)點之間,因此一條路徑最多變成:

   B

 B   R

B      B

        R

         B      因此,最長路徑不超過最短路徑的兩倍,也就保證了搜索的效率;



下面是實現(xiàn)紅黑樹的插入過程:

#pragma once

#include <iostream>
using namespace std;

//結(jié)點的顏色 紅or黑
enum Color
{
	RED,
	BLACK
};

//結(jié)點結(jié)構(gòu)體
template <class K, class V>
struct RBTreeNode
{
	K _key;
	V _val;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;

	RBTreeNode(const K& key, const V& val)
		:_key(key)
		,_val(val)
		,_left(NULL)
		,_right(NULL)
		,_parent(NULL)
		,_col(RED)
	{}
};

//紅黑樹類
template <class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;

public:
	RBTree()
		:_root(NULL)
	{}
	~RBTree()
	{
		_Clear(_root);
	}

	//插入結(jié)點
	bool Insert(const K& key, const V& val)
	{
		if(_root == NULL)//如果根結(jié)點為NULL,創(chuàng)建新的結(jié)點為根結(jié)點返回true
		{
			_root = new Node(key, val);
			_root->_col = BLACK;
			return true;
		}

		//如果根結(jié)點不為NULL,則遍歷樹直到找到合適的插入位置
		Node* cur = _root;
		Node* prev = cur;
		while(cur != NULL)
		{
			if(cur->_key == key)//如果樹中已經(jīng)有該結(jié)點,則返回false
				return false;
			else if(cur->_key > key)//如果關(guān)鍵值小于結(jié)點的關(guān)鍵值,則去左子樹找
			{
				prev = cur;
				cur = cur->_left;
			}
			else//否則關(guān)鍵值大于結(jié)點的關(guān)鍵值,去右子樹找
			{
				prev = cur;
				cur = cur->_right;
			}
		}
		//循環(huán)結(jié)束,找到了合適的插入位置,判斷應(yīng)該插入到結(jié)點的左邊還是右邊
		Node* tmp = new Node(key, val);
		if(prev->_key > key)
			prev->_left = tmp;
		else
			prev->_right = tmp;
		tmp->_parent = prev;

		//插入結(jié)點之后,就要開始判斷目前的樹是否符合紅黑樹的性質(zhì)
		while((tmp != _root) && (prev->_col == RED))
		{
			Node* grandfather = prev->_parent;//提取出tmp的祖父結(jié)點
			if(grandfather->_left == prev)//如果prev是grandfather的左結(jié)點
			{
				//第一種情況
				//tmp為紅,prev為紅,grandfather為黑,uncle存在且為紅
				//則將prev,uncle改為黑,grandfather改為紅,然后把grandfather當(dāng)成tmp,繼續(xù)向上調(diào)整。
				Node* uncle = grandfather->_right;
				if(uncle != NULL && uncle->_col == RED)
				{
					prev->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					tmp = grandfather;
					prev = tmp->_parent;
				}
				else//第二種情況:tmp為紅,prev為紅,grandfather為黑,uncle不存在/uncle為黑
					//prev為grandfather的左孩子,tmp為prev的左孩子,則進行右單旋轉(zhuǎn);
					//prev、grandfather變色--prev變黑,grandfather變紅

				{
					//第三種情況
					//tmp為紅,prev為紅,grandfather為黑,uncle不存在/uncle為黑
					//prev為grandfather的左孩子,tmp為prev的右孩子,則針對prev做左單旋轉(zhuǎn);
					//則轉(zhuǎn)換成了情況二

					if(prev->_right == tmp)
					{
						_RotateL(prev);
						swap(tmp, prev);
					}
					_RotateR(grandfather);//進行右單旋
					prev->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
			}
			else//當(dāng)perv是grandfather的右結(jié)點的時候,和上面的情況相反
			{
				//第一種情況
				//tmp為紅,prev為紅,grandfather為黑,uncle存在且為紅
				//則將prev,uncle改為黑,grandfather改為紅,然后把grandfather當(dāng)成tmp,繼續(xù)向上調(diào)整。
				Node* uncle = grandfather->_left;
				if(uncle != NULL && uncle->_col == RED)
				{
					prev->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					tmp = grandfather;
					prev = tmp->_parent;
				}
				else//第二種情況:tmp為紅,prev為紅,grandfather為黑,uncle不存在/uncle為黑
					//prev為grandfather的右孩子,tmp為prev的右孩子,則進行左單旋轉(zhuǎn)
					//prev、grandfather變色--prev變黑,grandfather變紅

				{
					//第三種情況
					//tmp為紅,prev為紅,grandfather為黑,uncle不存在/uncle為黑
					//prev為grandfather的右孩子,tmp為prev的左孩子,則針對prev做右單旋轉(zhuǎn)
					//則轉(zhuǎn)換成了情況二

					if(prev->_left == tmp)
					{
						_RotateR(prev);
						swap(tmp, prev);
					}
					_RotateL(grandfather);//進行右單旋
					prev->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
			}
		}
		//如果根結(jié)點被調(diào)整成了紅色,將其改為黑色,并會不影響左右黑結(jié)點的個數(shù)
		if(_root->_col == RED)
			_root->_col = BLACK;
		return true;
	}

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

	/*bool Find(const K& key)
	{

	}*/

private:
	void _Clear(Node* root)
	{
		if(root == NULL)
			return;

		_Clear(root->_left);
		_Clear(root->_right);
		delete root;
		root = NULL;
	}
	
	void _RotateL(Node* parent)//左單旋
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
 
        parent->_right = subRL;
        if (subRL != NULL)
            subRL->_parent = parent;
         
        Node* ppNode = parent->_parent;
 
        subR->_left = parent;
        parent->_parent = subR;
 
        if (ppNode == NULL)
            _root = subR;
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subR;
            else
                ppNode->_right = subR;
        }
		subR->_parent = ppNode;
    }

    void _RotateR(Node* parent)//右單旋
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
 
        parent->_left = subLR;
        if (subLR != NULL)
            subLR->_parent = parent;
 
        Node* ppNode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
 
        if (ppNode == NULL)
            _root = subL;
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subL;
            else
                ppNode->_right = subL;
        }
		subL->_parent = ppNode;
	}

	void _InOrder(Node* root)
	{
		if(root != NULL)
		{
			_InOrder(root->_left);
			cout<<root->_key<<" ";
			_InOrder(root->_right);
		}
	}

private:
	Node* _root;
};


void Test()
{
	int arr[] = {3, 4, 6, 1, 7, 2, 8};
	RBTree<int, int> t;
	for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
		t.Insert(arr[i], i);

	t.InOrder();
}


運行程序:

數(shù)據(jù)結(jié)構(gòu)之——紅黑樹



《完》


向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI