11.1新的一月加油!這個購物狂歡的季節,一看,已囊中羞澀!趕忙來惡補一下紅黑樹和2-3樹吧!紅黑樹真的算是大名鼎鼎了吧?即便你不瞭解它,但必定聽過吧?下面跟隨我來揭開神祕的面紗吧!node
今天的主角是紅黑樹,是無疑的,主角光環在呢!那2-3樹又是什麼鬼呢?學習2-3樹不只對理解紅黑樹有幫助,對理解B類樹,也是有巨大幫助的,因此學習2-3樹很必要!ios
2-3樹知足二分搜索樹的基本性質,但節點能夠存放一個元素或兩個元素!以下圖,就是2-3樹:面試
說明:2-3樹一顆絕對平衡的樹(絕對平衡:對於任意一個節點,左右子樹高度相同)算法
2-3樹在插入過程當中如何維護絕對平衡呢?進行畫圖演示,實在有點很差畫,以下圖:ide
說明:學習
由於那樣如上圖,就不知足絕對平衡了,因此能夠將37和42合併,2-3支持3節點。字體
再插入12時,也不能插入空節點,也要合併,但2-3樹不支持4節點,因此進行進行拆分。this
再依次插入1八、6,達到4節點,進行拆分,但不符合絕對平衡了怎麼辦?將12和37合併,就造成了最後3節點的圖了spa
總結:講到這裏,應該對2-3樹如何維護絕對平衡,應該瞭解了吧?理解2-3樹,對於再理解紅黑樹,是很是有幫助的,其實,它們有等價性的,接下來會說明的。code
也想達到像2-3樹那樣的絕對平衡,但2-3樹的實現比較麻煩,因此產生了紅黑樹;那麼,紅黑樹和2-3樹有怎麼樣的等價性呢?以下圖:
說明:紅黑樹最開始想用紅線區別b、c,但實現起來比較困難,而後用紅黑來表示節點,就比較好實現了!
紅黑樹和2-3樹整體對比圖,能夠參考一下:
紅黑樹有五個重要性質,引自算法界一本聖潔《算法導論》中的內容,以下:
是否是看着有點暈,下面我進行解釋。
一、每個節點或者紅色的,或是黑色的
二、根節點是黑色的
三、每個葉子節點(最後的空節點)是黑色的
四、若是一個節點是紅色的,那麼它的孩子節點都是黑色的
五、從任意節點到葉子節點,通過的黑色節點是同樣的
解釋:最重要的性質是第五條,前4條在理解2-3樹以後,就很好理解了,第5條性質說明了:紅黑樹是保持「黑平衡」的二叉樹;
嚴格意義上來講,紅黑樹不是平衡二叉樹,最大高度:2logn,可是時間複雜度仍然是O(logn),由於2是常數,但比AVL樹查詢要稍微慢一些。
紅黑樹添加元素,比較繁瑣,由於要保持上面的五個性質,要否則就不是紅黑樹了;
紅黑樹的節點類也能夠從二分搜索樹上進行修改,但要新增「color」成員變量,來標註節點顏色,節點類以下:
template<typename Key, typename Value> class RBTree { private: static const bool RED = true; static const bool BLACK = false; struct Node { Key key; Value value; Node *left; Node *right; bool color; Node(Key key, Value value) { this->key = key; this->value = value; this->left = this->right = nullptr; color = RED; //默認初始化爲紅色 } Node(Node *node) { this->key = node->key; this->value = node->value; this->left = node->left; this->right = node->right; this->color = node->color; } }; Node *root; int size; }
由於紅黑樹性質1要求根節點爲黑色,因此要保持根節點爲黑色;
像AVL樹同樣,紅黑樹也須要左旋和右旋,以下圖就須要左旋轉,由於「紅色節點是左傾斜的」:
說明:圖中黑色字體標識黑色節點,紅色表示紅色節點,並演示了旋轉過程,最後還要改變節點顏色。
代碼以下:
Node *leftRotate(Node *node) { Node *x = node->right; node->right = x->left; x->left = node; x->color = node->color; node->color = RED; return x; }
下面這種狀況就須要顏色反轉,以下圖:
代碼以下:
void flipColors(Node *node) { node->color = RED; node->left->color = BLACK; node->right->color = BLACK; }
下面狀況須要右旋轉,以下圖:
旋轉以後,以下圖:
代碼以下:
Node *rightRotate(Node *node) { Node *x = node->left; node->left = x->right; x->right = node; x->color = node->color; node->color = RED; return x; }
整體代碼以下,供參考和學習:
#ifndef RED_BLACK_TREE_RBTREE_H #define RED_BLACK_TREE_RBTREE_H #include <iostream> #include <vector> template<typename Key, typename Value> class RBTree { private: static const bool RED = true; static const bool BLACK = false; struct Node { Key key; Value value; Node *left; Node *right; bool color; Node(Key key, Value value) { this->key = key; this->value = value; this->left = this->right = nullptr; color = RED; } Node(Node *node) { this->key = node->key; this->value = node->value; this->left = node->left; this->right = node->right; this->color = node->color; } }; Node *root; int size; public: RBTree() { root = nullptr; size = 0; } ~RBTree() { destroy(root); } int getSize() { return size; } int isEmpty() { return size == 0; } bool isRed(Node *node) { if (node == nullptr) { return BLACK; } return node->color; } void add(Key key, Value value) { root = add(root, key, value); root->color = BLACK; } bool contains(Key key) { return getNode(root, key) != nullptr; } Value *get(Key key) { Node *node = getNode(root, key); return node == nullptr ? nullptr : &(node->value); } void set(Key key, Value newValue) { Node *node = getNode(root, key); if (node != nullptr) { node->value = newValue; } } private: // 向以node爲根的二叉搜索樹中,插入節點(key, value) // 返回插入新節點後的二叉搜索樹的根 Node *add(Node *node, Key key, Value value) { if (node == nullptr) { size++; return new Node(key, value); } if (key == node->key) { node->value = value; } else if (key < node->key) { node->left = add(node->left, key, value); } else { node->right = add(node->right, key, value); } if (isRed(node->right) && !isRed(node->left)) { node = leftRotate(node); } if (isRed(node->left) && isRed(node->left->left)) { node = rightRotate(node); } if (isRed(node->left) && isRed(node->right)) { flipColors(node); } return node; } // 在以node爲根的二叉搜索樹中查找key所對應的Node Node *getNode(Node *node, Key key) { if (node == nullptr) { return nullptr; } if (key == node->key) { return node; } else if (key < node->key) { return getNode(node->left, key); } else { return getNode(node->right, key); } } void destroy(Node *node) { if (node != nullptr) { destroy(node->left); destroy(node->right); delete node; size--; } } Node *leftRotate(Node *node) { Node *x = node->right; node->right = x->left; x->left = node; x->color = node->color; node->color = RED; return x; } Node *rightRotate(Node *node) { Node *x = node->left; node->left = x->right; x->right = node; x->color = node->color; node->color = RED; return x; } void flipColors(Node *node) { node->color = RED; node->left->color = BLACK; node->right->color = BLACK; } }; #endif //RED_BLACK_TREE_RBTREE_H
面試時99.9%不會讓手寫一下紅黑樹的添加過程,除非你面試算法工程師,那就打擾了!主要理解紅黑樹的性質、左旋和右旋等。
歡迎點贊和評論,感謝支持!