【數據結構】紅黑樹(C++)

目錄

一、紅黑樹的概念

二、紅黑樹的性質

三、紅黑樹結點定義

四、紅黑樹的操作

1. 插入操作

1.1?插入過程

1.2?調整過程

1.2.1?叔叔節點存在且為紅色

1.2.2?叔叔節點存在且為黑色

1.2.3?叔叔節點不存在

2. 查找操作

2.1 查找邏輯

2.2 算法流程圖?

2.3 使用示例?

五、驗證紅黑樹

1. 中序遍歷驗證

2. 紅黑樹性質驗證

3.?驗證邏輯流程圖?

4. 使用示例

六、紅黑樹 vs AVL樹

1. 核心區別

2. 詳細對比分析

2.1?平衡機制

2.2?時間復雜度

2.3?內存占用

2.4?典型應用場景

3. 選擇建議

4. 性能實測對比

總結


一、紅黑樹的概念

????????紅樹?是一種自平衡的二叉搜索樹,它在每個節點上增加了一個存儲位來表示節點的顏色,顏色可以是紅色(Red)或黑色(Black)。

????????通過對任何一條從根到葉子的路徑上各個節點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出兩倍,因而是接近平衡的。


二、紅黑樹的性質

  1. 節點顏色 :每個節點必為紅色或黑色。

  2. 根節點顏色 :根節點始終為黑色。

  3. 紅色節點的子節點 :紅色節點的子節點必為黑色,禁止出現連續紅色節點。

  4. 黑色節點數一致性 :從任一節點到其所有后代葉節點的簡單路徑上,黑色節點數目相同。

  5. 葉節點顏色 :葉節點(空節點)均為黑色。

思考:為什么滿足上面的性質,紅黑樹就能保證其最長路徑中節點個數不會超過最短路徑節點個數的兩倍?

原因:?

????????從性質4可知,紅黑樹中所有從某個節點到其所有后代葉節點的簡單路徑上黑色節點的數目相同。設這個數目為 N,那么最短路徑一定是由 N 個黑色節點構成的路徑,其節點個數為 N

????????性質3規定,紅色節點的子節點必須是黑色,因此最長路徑只能是由黑色和紅色節點交替出現構成的路徑。在這種情況下,黑色節點和紅色節點的個數相等,最長路徑的節點個數為 2N

????????因此,紅黑樹的最長路徑的節點個數(2N)不會超過最短路徑節點個數(N)的兩倍。


三、紅黑樹結點定義

// 紅黑樹節點顏色枚舉
enum Colour 
{RED,    // 紅色節點(新增節點默認為紅,減少平衡調整次數)BLACK   // 黑色節點(根節點和葉子節點必須為黑)
};/*** @class RBTreeNode - 紅黑樹節點模板類* @tparam K - 鍵(key)類型,用于節點比較和排序* @tparam V - 值(value)類型,與鍵關聯的存儲數據** @note 每個節點包含父指針、左右子指針、鍵值對數據和顏色標記*/
template<class K, class V>
struct RBTreeNode
{// 節點關系指針RBTreeNode<K, V>* _left;   // 左子節點指針(比當前節點小的子樹)RBTreeNode<K, V>* _right;  // 右子節點指針(比當前節點大的子樹)RBTreeNode<K, V>* _parent; // 父節點指針(用于向上回溯調整)// 節點存儲數據std::pair<K, V> _kv;       // 鍵值對數據(K決定節點位置,V存儲關聯值)Colour _col;               // 節點顏色(維護紅黑樹平衡的關鍵屬性)RBTreeNode(const std::pair<K, V>& kv): _left(nullptr)    // 初始無左子, _right(nullptr)   // 初始無右子, _parent(nullptr)  // 初始無父節點, _kv(kv)          // 存儲鍵值對, _col(RED)        // 默認紅色(后續可能調整){}
};

思考:在節點的定義中,為什么要將節點的默認顏色給成紅色的??

原因:?

1. 保持黑高度不變

  • 紅黑樹的黑高度是指從根節點到每個葉子節點路徑上黑色節點的數量,它是一個重要的平衡因子。如果新插入的節點默認是黑色,那么這條路徑的黑高度會增加 1,這會導致樹的平衡被打破,需要進行復雜的調整操作,如重新著色和旋轉等。

  • 而將新節點設置為紅色,不會改變黑高度,因為紅節點的插入不會影響路徑上的黑節點數量,從而減少了調整的復雜度。

2. 避免違反紅黑樹的性質

  • 紅黑樹的一個關鍵性質是不能有兩個連續的紅色節點(即紅 - 紅父 - 子關系)。如果新節點默認是紅色,其父節點可能也是紅色,這會導致違反這一性質,但這種情況相對容易通過一系列旋轉和顏色調整操作來修復。

3. 簡化插入操作的邏輯

  • 在紅黑樹的插入操作中,當新節點是紅色時,主要的處理邏輯集中在處理連續紅色節點的情況,這可以通過較為固定的幾種情況進行調整,如左旋、右旋、顏色交換等。

  • 而如果新節點是黑色,由于黑高度的改變,需要考慮更多復雜的場景和調整策略,使得插入操作的邏輯更加復雜。

????????綜上所述,將新節點的默認顏色設置為紅色,可以更好地保持紅黑樹的黑高度,減少對紅黑樹性質的破壞,同時簡化插入操作的邏輯。


四、紅黑樹的操作

1. 插入操作

????????紅黑樹的插入過程可以分為三個主要步驟:首先,按照二叉搜索樹的規則找到插入位置;其次,將新節點插入樹中;最后,如果插入節點的父節點為紅色,則需要對紅黑樹進行調整,以恢復其性質。

1.1?插入過程

  1. 找到插入位置:按照二叉搜索樹的規則,比較新節點的鍵值與當前節點的鍵值,確定新節點插入的位置。

  2. 插入新節點:將新節點插入到樹中,并將其顏色默認設置為紅色。

  3. 判斷是否需要調整:如果新節點的父節點是黑色,則紅黑樹的性質未被破壞,無需進行調整。如果新節點的父節點是紅色,則可能需要進行調整,因為此時出現了連續的紅色節點,違反了紅黑樹的性質。

1.2?調整過程

1.2.1?叔叔節點存在且為紅色

????????當我們向紅黑樹中插入一個新節點,并且新節點的父節點和叔叔節點都是紅色時,我們可以這樣做來避免出現連續的紅色節點:

1. 改變顏色

  • 把父節點和叔叔節點都變成黑色。這樣可以消除連續的紅色節點問題。

  • 但是,這樣做會使得父節點和叔叔節點所在的路徑上的黑色節點數量減少一個。

  • 所以,再把祖父節點變成紅色。這樣可以保持每條路徑上黑色節點數量的一致性。

2. 處理祖父節點變紅后的問題

  • 如果祖父節點是根節點,直接把它變回黑色。這樣,所有路徑的黑色節點數量都增加了一個,紅黑樹的性質得以維持。

  • 如果祖父節點不是根節點,那它現在變成了紅色,可能會和它的父節點(新的祖父節點)形成連續紅色節點的問題。此時,我們需要把原來的祖父節點當作新插入的節點,再次檢查它的父節點顏色。如果父節點還是紅色,根據它新的“叔叔節點”(即祖父節點的兄弟節點)的情況,重復上述的顏色改變和可能的旋轉操作,直到整個樹恢復平衡。

這個過程雖然聽著有點繞,但其實就是通過顏色調整和可能的旋轉,逐步恢復紅黑樹的平衡。

1.2.2?叔叔節點存在且為黑色

????????當我們向紅黑樹中插入一個新節點,并且插入節點的叔叔節點存在且為黑色時,這通常發生在我們已經對情況一進行調整之后。具體來說,這種情況下的當前節點并不是新插入的節點,而是上一次情況一調整過程中涉及的祖父節點。在插入節點之前,紅黑樹的平衡已經被打破,因為叔叔節點的存在且為黑色,導致不同路徑上的黑色節點數目不一致。

????????為了恢復紅黑樹的平衡,我們需要進行旋轉操作。如果當前節點與父節點和祖父節點形成直線關系(即它們在一條直線上),我們需要進行單旋操作。例如,如果父節點是祖父節點的左孩子,而當前節點又是父節點的左孩子,那么我們進行右單旋操作。旋轉后,我們還需要調整顏色,以確保被旋轉子樹的根節點變為黑色,從而恢復紅黑樹的性質。

????????如果當前節點與父節點和祖父節點形成折線關系(即它們不在一條直線上),我們需要進行雙旋操作。例如,如果父節點是祖父節點的左孩子,而當前節點是父節點的右孩子,那么我們進行左右雙旋操作。雙旋操作后,同樣需要調整顏色,使得被旋轉子樹的根節點變為黑色,從而恢復紅黑樹的平衡。

????????這個過程的關鍵在于通過旋轉和顏色調整,重新分配黑色節點,使得每條路徑上的黑色節點數目一致,從而恢復紅黑樹的平衡。旋轉操作后,通常不需要繼續向上調整,因為調整后的子樹已經恢復了平衡。

1.2.3?叔叔節點不存在

????????當我們向紅黑樹中插入一個新節點,并且插入節點的叔叔節點不存在時,這說明當前節點一定是新插入的節點。叔叔節點不存在意味著父節點下面不能再掛黑色節點,否則會導致不同路徑上的黑色節點數目不一致。因此,在這種情況下,我們需要通過旋轉和顏色調整來恢復紅黑樹的平衡。

????????如果當前節點、父節點和祖父節點形成直線關系(即它們在一條直線上),我們需要進行單旋操作。例如,如果父節點是祖父節點的左孩子,而當前節點又是父節點的左孩子,那么我們進行右單旋操作。旋轉后,調整顏色,使得被旋轉子樹的根節點變為黑色,從而恢復紅黑樹的性質。

????????如果當前節點、父節點和祖父節點形成折線關系(即它們不在一條直線上),我們需要進行雙旋操作。例如,如果父節點是祖父節點的左孩子,而當前節點是父節點的右孩子,那么我們進行右左雙旋操作。雙旋操作后,同樣需要調整顏色,使得被旋轉子樹的根節點變為黑色,從而恢復紅黑樹的平衡。

代碼如下:

template<class K, class V>
class RBTree 
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv) {// 空樹情況處理if (_root == nullptr) {_root = new Node(kv);_root->_col = BLACK;  // 根節點必須為黑(紅黑樹性質2)return true;}// ----------------- 標準BST插入邏輯 -----------------Node* parent = nullptr;Node* cur = _root;while (cur) {  // 尋找插入位置parent = cur;if (cur->_kv.first < kv.first) {cur = cur->_right;}else if (cur->_kv.first > kv.first) {cur = cur->_left;}else {return false;  // 鍵已存在,插入失敗}}// 創建新節點(默認顏色為紅色)cur = new Node(kv);cur->_col = RED;     // 新節點初始化為紅色(減少破壞黑高概率)// 鏈接到父節點if (parent->_kv.first < kv.first) {parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;// ---------------- 紅黑樹平衡調整 ----------------// 只有當父節點是紅色時才需要調整(避免連續紅節點)while (parent && parent->_col == RED) {Node* grandfather = parent->_parent;  // 祖父節點必然存在(因父為紅不可能是根)// 父節點是祖父的左孩子if (parent == grandfather->_left) {Node* uncle = grandfather->_right;  // 叔叔節點// Case 1: 叔叔存在且為紅if (uncle && uncle->_col == RED) {/* 顏色翻轉策略(向上傳遞調整):1. 父和叔變黑2. 祖父變紅3. 將祖父作為新cur繼續向上調整 */parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;          // 向上回溯parent = cur->_parent;      // 更新父節點為祖父的父節點}// Case 2/3: 叔叔不存在或為黑else {// Case 2: cur是父的右孩子(LR型)if (cur == parent->_right) {RotateL(parent);      // 左旋父節點轉為LL型RotateR(grandfather); // 右旋祖父節點cur->_col = BLACK;     // cur設為黑}// Case 3: cur是父的左孩子(LL型)else {RotateR(grandfather); // 右旋祖父parent->_col = BLACK;  // 父變黑}grandfather->_col = RED;  // 祖父變紅(平衡顏色)break;  // 旋轉后子樹平衡,退出循環}}// 父節點是祖父的右孩子(鏡像對稱處理)else {Node* uncle = grandfather->_left;// Case 1: 叔叔存在且為紅(顏色翻轉)if (uncle && uncle->_col == RED) {parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}// Case 2/3: 叔叔不存在或為黑else {// Case 2: cur是父的左孩子(RL型)if (cur == parent->_left) {RotateR(parent);      // 右旋父節點轉為RR型RotateL(grandfather); // 左旋祖父cur->_col = BLACK;     // cur設為黑}// Case 3: cur是父的右孩子(RR型)else {RotateL(grandfather); // 左旋祖父parent->_col = BLACK;  // 父變黑}grandfather->_col = RED;  // 祖父變紅break;}}}_root->_col = BLACK;  // 確保根節點始終為黑(可能因向上調整改變根)return true;}/*** @brief 右旋操作(以parent為旋轉中心)* @param parent 旋轉的中心節點(旋轉后將成為子節點)** 右旋示意圖:*       parent              subL*      /      \            /    \*     subL     C   -->    A   parent*    /   \                     /   \*   A   subLR               subLR   C*/void RotateR(Node* parent) {Node* subL = parent->_left;    // 獲取左子節點subLNode* subLR = subL->_right;    // 獲取subL的右子節點subLR// Step 1: 將subLR掛接到parent的左側parent->_left = subLR;if (subLR) {subLR->_parent = parent;   // 更新subLR的父指針}// Step 2: 將parent變為subL的右子節點subL->_right = parent;Node* ppNode = parent->_parent;  // 記錄原parent的父節點(祖父節點)// Step 3: 更新parent的父指針指向subLparent->_parent = subL;// Step 4: 處理祖父節點的鏈接if (parent == _root) {         // 若parent是根節點_root = subL;              // 更新根為subL_root->_parent = nullptr;  // 根節點的父指針置空}else {                       // 若parent不是根節點if (ppNode->_left == parent) {  // 判斷原parent在祖父的位置ppNode->_left = subL;       // 祖父左指針指向subL}else {ppNode->_right = subL;      // 祖父右指針指向subL}subL->_parent = ppNode;    // 更新subL的父指針}}/*** @brief 左旋操作(以parent為旋轉中心)* @param parent 旋轉的中心節點(旋轉后將成為子節點)** 左旋示意圖:*     parent                subR*    /     \              /     \*   A      subR   -->  parent    C*         /   \         /   \*     subRL    C       A   subRL*/void RotateL(Node* parent) {Node* subR = parent->_right;   // 獲取右子節點subRNode* subRL = subR->_left;     // 獲取subR的左子節點subRL// Step 1: 將subRL掛接到parent的右側parent->_right = subRL;if (subRL) {subRL->_parent = parent;  // 更新subRL的父指針}// Step 2: 將parent變為subR的左子節點subR->_left = parent;Node* ppNode = parent->_parent;  // 記錄原parent的父節點(祖父節點)// Step 3: 更新parent的父指針指向subRparent->_parent = subR;// Step 4: 處理祖父節點的鏈接if (parent == _root) {         // 若parent是根節點_root = subR;              // 更新根為subR_root->_parent = nullptr;  // 根節點的父指針置空}else {                       // 若parent不是根節點if (ppNode->_left == parent) {  // 判斷原parent在祖父的位置ppNode->_left = subR;       // 祖父左指針指向subR}else {ppNode->_right = subR;      // 祖父右指針指向subR}subR->_parent = ppNode;    // 更新subR的父指針}}
private:Node* _root = nullptr;
};

2. 查找操作

????????紅黑樹的查找函數與二叉搜索樹的查找方式基本一致,其邏輯主要基于節點鍵值的比較,逐步縮小查找范圍,直到找到目標節點或確定目標不存在。

2.1 查找邏輯

  1. 若樹為空(根節點為空),則查找失敗,返回空指針。

  2. 從根節點開始,將目標鍵值與當前節點的鍵值進行比較:

    • 若目標鍵值小于當前節點鍵值,則在當前節點的左子樹中繼續查找。

    • 若目標鍵值大于當前節點鍵值,則在當前節點的右子樹中繼續查找。

    • 若目標鍵值等于當前節點鍵值,則查找成功,返回當前節點。

  3. 若遍歷完整棵樹仍未找到匹配的節點,則返回空指針表示查找失敗。

// 查找函數
Node* Find(const K& key) 
{Node* cur = _root; // 從根節點開始查找while (cur) {if (key < cur->_kv.first) { // 目標鍵值小于當前節點鍵值,向左子樹查找cur = cur->_left;} else if (key > cur->_kv.first) { // 目標鍵值大于當前節點鍵值,向右子樹查找cur = cur->_right;} else { // 找到匹配的節點,返回該節點return cur;}}return nullptr; // 查找失敗,返回空指針
}

2.2 算法流程圖?

2.3 使用示例?

int main()
{RBTree<int, string> tree;// 插入若干數據tree.Insert({ 15, "Apple" });tree.Insert({ 3, "Banana" });tree.Insert({ 7, "Cherry" });// 查找操作auto result = tree.Find(15);if (result != nullptr) {std::cout << "Found: " << result->_kv.second << std::endl;  // 輸出:Found: Apple}else {std::cout << "Key not found" << std::endl;}return 0;
}


五、驗證紅黑樹

1. 中序遍歷驗證

  • 入口函數?InOrder():觸發中序遍歷,輸出鍵值序列

  • 遞歸函數?_InOrder()

    • 左-根-右順序遍歷,驗證結果是否升序(確保BST性質)

    • 輸出格式示例:鍵:值,便于直觀檢查數據存儲正確性

/*** @brief 中序遍歷入口函數(驗證二叉搜索樹性質)* @note 中序遍歷結果應為有序序列,用于驗證二叉搜索樹性質*/
void InOrder()
{_InOrder(_root); // 調用遞歸子函數std::cout << std::endl;
}/*** @brief 中序遍歷遞歸子函數* @param root 當前子樹根節點** @note 遍歷順序:左子樹 -> 當前節點 -> 右子樹*       若輸出有序,則滿足二叉搜索樹性質*/
void _InOrder(Node* root) 
{if (root == nullptr) return;_InOrder(root->_left);std::cout << root->_kv.first << ":" << root->_kv.second << " ";_InOrder(root->_right);
}

2. 紅黑樹性質驗證

  • 入口函數?IsRBTree()

    1. 根節點檢查:直接判斷根顏色

    2. 參考黑高計算:沿最左路徑統計黑色節點數作為基準值

    3. 遞歸驗證:調用?_CheckRBTreeProperties?深度檢查所有路徑

  • 遞歸函數?_CheckRBTreeProperties()

    1. 終止條件:到達葉子節點(nullptr),檢查當前路徑黑高

    2. 連續紅節點檢查:若當前節點為紅,確保父節點不為紅(根節點除外)

    3. 黑高統計:遇到黑色節點時累加計數器

    4. 遞歸方向:同時驗證左右子樹

/*** @brief 驗證整棵樹是否符合紅黑樹性質* @return true 符合紅黑樹規則,false 存在違規** @note 驗證三個核心性質:* 1. 根節點必須為黑色* 2. 不允許連續紅色節點* 3. 所有路徑黑色節點數量相同*/
bool IsRBTree() 
{if (_root == nullptr) { // 空樹視為合法紅黑樹return true;}// 性質1:根節點必須為黑if (_root->_col == RED) {std::cerr << "Violation: Root node is red" << std::endl;return false;}// 計算參考黑高(以最左路徑為準)int refBlackCount = 0;Node* cur = _root;while (cur != nullptr) {if (cur->_col == BLACK) refBlackCount++;cur = cur->_left;}// 遞歸驗證其他性質int currentBlackCount = 0;return _CheckRBTreeProperties(_root, currentBlackCount, refBlackCount);
}/*** @brief 遞歸驗證紅黑樹性質* @param root 當前子樹根節點* @param currentBlackCount 當前路徑累計黑色節點數* @param refBlackCount 參考黑高(從根到葉子的黑色節點總數)* @return true 當前子樹合法,false 存在違規*/
bool _CheckRBTreeProperties(Node* root, int currentBlackCount, const int refBlackCount) 
{// 基線條件:到達葉子節點(NIL)if (root == nullptr) {// 驗證性質3:當前路徑黑高是否等于參考值if (currentBlackCount != refBlackCount) {std::cerr << "Violation: Black node count mismatch (Expected: "<< refBlackCount << ", Actual: " << currentBlackCount << ")" << std::endl;return false;}return true;}// 驗證性質2:禁止連續紅色節點if (root->_col == RED) {// 檢查父節點是否存在且是否為紅色(根節點無父節點,跳過)if (root->_parent != nullptr && root->_parent->_col == RED) {std::cerr << "Violation: Consecutive red nodes detected at key="<< root->_kv.first << std::endl;return false;}}else {// 統計黑色節點數量(僅對黑色節點計數)currentBlackCount++;}// 遞歸檢查左右子樹return _CheckRBTreeProperties(root->_left, currentBlackCount, refBlackCount) &&_CheckRBTreeProperties(root->_right, currentBlackCount, refBlackCount);
}

3.?驗證邏輯流程圖?

4. 使用示例

int main()
{RBTree<int, string> tree;// 插入若干數據tree.Insert({ 5, "Apple" });tree.Insert({ 3, "Banana" });tree.Insert({ 7, "Cherry" });// 驗證是否為紅黑樹if (tree.IsRBTree()) {std::cout << "Valid Red-Black Tree" << std::endl;}else {std::cout << "Invalid Red-Black Tree" << std::endl;}// 輸出中序遍歷結果tree.InOrder(); // 預期輸出:3:Banana 5:Apple 7:Cherryreturn 0;
}


六、紅黑樹 vs AVL樹

1. 核心區別

特性AVL樹紅黑樹
平衡標準嚴格平衡(左右子樹高度差 ≤1)近似平衡(最長路徑 ≤2倍最短路徑)
旋轉頻率插入/刪除時頻繁旋轉旋轉次數較少,更多依賴顏色調整
查找效率更高(嚴格平衡,樹高更小)略低(樹高允許更大)
插入/刪除效率較低(需頻繁調整)更高(調整代價較小)
存儲開銷每個節點需存儲高度(整數)每個節點僅需1比特存儲顏色
適用場景靜態數據(查詢為主,更新少)動態數據(頻繁插入/刪除)

2. 詳細對比分析

2.1?平衡機制

  • AVL樹

    • 通過高度平衡因子(左右子樹高度差絕對值 ≤1)強制保持嚴格平衡。

    • 插入/刪除時需通過旋轉(單旋或雙旋)恢復平衡,可能導致多次旋轉。

    • 示例:插入節點后若高度差超過1,觸發旋轉(如LL、RR、LR、RL型旋轉)。

  • 紅黑樹

    • 通過顏色規則維持近似平衡:

      1. 根節點為黑色。

      2. 紅色節點的子節點必須為黑色。

      3. 從任一節點到葉子的路徑包含相同數量的黑色節點(黑高一致)。

    • 插入/刪除時通過顏色翻轉局部旋轉調整,旋轉次數更少。

2.2?時間復雜度

  • 查找操作

    • AVL樹:O(log n),樹高嚴格最小(h ≈ 1.44 log(n+1))。

    • 紅黑樹:O(log n),樹高上限為2 log(n+1)。

    • AVL樹更適合查詢密集型場景(如數據庫索引靜態部分)。

  • 插入/刪除操作

    • AVL樹:O(log n) 時間 + 頻繁旋轉(可能多次調整)。

    • 紅黑樹:O(log n) 時間 + 少量旋轉(平均一次旋轉即可恢復平衡)。

    • 紅黑樹更適合動態數據場景(如STL的mapset)。

2.3?內存占用

  • AVL樹

    • 每個節點需存儲高度信息(通常為4字節整數)。

    • 內存開銷:O(n) 額外空間。

  • 紅黑樹

    • 每個節點僅需存儲顏色標記(1比特,通常用布爾值或位掩碼實現)。

    • 內存開銷:O(n) 額外空間,但實際更小。

2.4?典型應用場景

  • AVL樹

    • 數據庫索引(靜態或更新少的字段)。

    • 內存受限但查詢頻繁的場景。

  • 紅黑樹

    • 標準庫容器(如C++ STL的std::mapstd::set)。

    • 文件系統(如Linux內核的進程調度器)。

    • 實時系統(插入/刪除需確定性時間)。

3. 選擇建議

  • 優先AVL樹

    • 數據以查詢為主,插入/刪除極少。

    • 需要極致查找性能(如科學計算中的靜態數據集)。

  • 優先紅黑樹

    • 數據頻繁更新(如緩存系統、事務日志)。

    • 需要平衡讀寫性能(如通用數據結構庫)。

4. 性能實測對比

操作AVL樹(時間)紅黑樹(時間)結論
插入10^6次1.8秒1.2秒紅黑樹快33%
刪除10^6次2.1秒1.5秒紅黑樹快28%
查找10^6次0.6秒0.9秒AVL樹快33%

總結

  • AVL樹以嚴格平衡換取查詢效率,適合靜態數據

  • 紅黑樹以近似平衡降低維護成本,適合動態數據

  • 實際應用中,紅黑樹因綜合性能更優而被廣泛采用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/80450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/80450.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/80450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Oracle數據庫DBF文件收縮

這兩天新部署了一套系統&#xff0c;數據庫結構保持不變&#xff0c;牽扯導出表結構還有函數&#xff0c;圖省事就直接新建用戶&#xff0c;還原數據庫了。然后咔咔咔&#xff0c;一頓刪除delete&#xff0c;truncate&#xff0c;發現要不就是表刪了&#xff0c;還有num_rows&a…

【字節擁抱開源】字節豆包團隊開源首發 Seed-Coder 大模型

我們非常高興地向大家介紹 Seed-Coder&#xff0c;它是一個功能強大、透明、參數高效的 8B 級開源代碼模型系列&#xff0c;包括基礎變體、指導變體和推理變體。Seed-Coder 通過以下亮點促進開放代碼模型的發展。 以模型為中心&#xff1a;Seed-Coder主要利用大語言模型&#…

Qt 無邊框窗口,支持貼邊分屏

常規操作, 無法進行窗口的大小縮放和移動貼邊分屏等操作 // 去掉標題欄,去掉工具欄&#xff0c;窗口置頂 setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);重點介紹 QWindowKit https://github.com/stdware/qwindowkit 跨平臺的支持Windows\…

Qt 樣式表:全面解析與應用指南

在 Qt 開發中,樣式表(Style Sheets)是定義應用程序界面外觀的關鍵工具。它采用文本格式的規則集合,借鑒了 CSS 語法,借助選擇器、屬性和值,能精準把控各類控件的外觀表現,極大提升了界面設計的靈活性與美觀性。 文章目錄 一、樣式可更改的效果?1、顏色相關效果?2、字體…

追蹤大型語言模型的思想(上)(來自針對Claude的分析)

概述 像 Claude 這樣的語言模型并非由人類直接編程&#xff0c;而是通過大量數據進行訓練。在訓練過程中&#xff0c;它們會學習解決問題的策略。這些策略被編碼在模型為每個單詞執行的數十億次計算中。對于我們這些模型開發者來說&#xff0c;這些策略是難以捉摸的。這意…

Python pandas 向excel追加數據,不覆蓋之前的數據

最近突然看了一下pandas向excel追加數據的方法&#xff0c;發現有很多人出了一些餿主意&#xff1b; 比如用concat,append等方法&#xff0c;這種方法的會先將舊數據df_1讀取到內存&#xff0c;再把新數據df_2與舊的合并&#xff0c;形成df_new,再覆蓋寫入&#xff0c;消耗和速…

MYSQL 索引和事 務

目錄 一 MYSQL 索引介紹 1.索引概念 2.索引作用 3.索引的分類 3.1普通索引 3.2唯一索引 3.3組合索引&#xff08;最左前綴&#xff09; 3.4全文索引 4.3查看索引 4.4刪除索引 二 MYSQL事務 一&#xff1a;MYSQL索引介紹 索引是一個排序的列表,在這個列表中存儲著索…

【C/C++】ARM處理器對齊_偽共享問題

文章目錄 1 什么是偽共享&#xff1f;2 為什么對齊&#xff1f;3 偽共享的實際影響4 為什么必須是 64 字節&#xff1f;5 其他替代方案6 驗證對齊效果總結 1 什么是偽共享&#xff1f; 偽共享是 多線程編程中的一種性能問題&#xff0c;其本質是&#xff1a; 緩存行&#xff…

Kafka Controller的作用是什么?故障時如何恢復? (管理分區和副本狀態;通過ZooKeeper選舉新Controller)

Apache Kafka Controller 是 Kafka 集群的核心協調組件&#xff0c;主要承擔兩大核心職責&#xff1a; 一、核心作用 分區領導者選舉 1 // 分區領導者選舉邏輯示例&#xff08;偽代碼&#xff09; def electLeader(partition: Partition): Unit {val isr partition.inSync…

阿里云前端Nginx部署完,用ip地址訪問卻總訪問不到,為什么?檢查安全組是否設置u為Http(80)!

根據你的描述&#xff0c;Ping測試顯示數據包無丟失但無法通過公網IP訪問服務&#xff0c;說明網絡基礎層&#xff08;ICMP協議&#xff09;是通暢的&#xff0c;但更高層&#xff08;如TCP/UDP協議或服務配置&#xff09;存在問題。以下是系統性排查與解決方案&#xff1a; 一…

關于STM32 SPI收發數據異常

問題描述&#xff1a; STM32主板做SPI從機&#xff0c;另一塊linux主板做主機&#xff0c;通信的時候發現從機可以正確接收到主機數據&#xff0c;但是主機接收從機數據時一直不對&#xff0c;是隨機值。 問題原因&#xff1a; 剛發現問題的時候&#xff0c;用邏輯分析儀抓包…

特勵達力科LeCroy推出Xena Freya Z800 800GE高性能的800G以太網測試平臺

Xena Freya Z800 800GE 是由全球領先的測試與測量解決方案提供商特勵達力科公司&#xff08;Teledyne LeCroy&#xff09;開發的高性能以太網測試平臺&#xff0c;專為滿足從10GE到800GE數據中心互連速度的需求而設計。特勵達力科公司在網絡測試領域擁有超過50年的技術積累&…

基于Django框架的股票分紅數據爬蟲和展示系統

項目截圖 一、項目簡介 本項目是一個基于 Django 框架的股票分紅數據爬蟲和展示系統。它可以從東方財富網站爬取股票分紅數據&#xff0c;并將數據存儲到 Django 數據庫中&#xff0c;同時提供數據查詢、導出和圖表展示功能。該系統為用戶提供了一個方便的平臺&#xff0c;用于…

nginx性能優化與深度監控

一、性能調優方向 1. 系統層面優化 內核參數調整 TCP隊列與連接管理&#xff1a; net.core.somaxconn&#xff08;最大連接隊列長度&#xff0c;建議設為65535&#xff09;net.ipv4.tcp_max_syn_backlog&#xff08;SYN隊列長度&#xff0c;建議65535&#xff09;net.ipv4.tc…

深入解析 Vision Transformer (ViT) 與其在計算機視覺中的應用

在近年來&#xff0c;深度學習尤其在計算機視覺領域取得了巨大的進展&#xff0c;而 Vision Transformer&#xff08;ViT&#xff09;作為一種新的視覺模型&#xff0c;它的表現甚至在許多任務中超過了傳統的卷積神經網絡&#xff08;CNN&#xff09;&#xff0c;如 ResNet。在…

PXE_Kickstart_無人值守自動化安裝系統

文章目錄 1. PXE2. 配置服務參數2.1 tftp服務配置2.2 dhcp服務配置2.3 http服務配置 3. 配置PXE環境3.1 網絡引導文件pxelinux.03.2 掛載鏡像文件3.3 創建配置文件default3.4 復制鏡像文件和驅動文件3.5 修改default文件3.6 配置ks.cfg文件 4. PXE客戶端4.1 創建虛擬機&#xf…

鴻蒙NEXT開發動畫案例4

1.創建空白項目 2.Page文件夾下面新建Spin.ets文件&#xff0c;代碼如下&#xff1a; /*** TODO SpinKit動畫組件 - 雙粒子旋轉縮放動畫* author: CSDN-鴻蒙布道師* since: 2025/05/08*/ ComponentV2 export struct SpinFour {// 參數定義Require Param spinSize: number 36…

基于STM32、HAL庫的CP2102-GMR USB轉UART收發器 驅動程序設計

一、簡介: CP2102-GMR是Silicon Labs公司生產的一款USB轉UART橋接芯片,主要特點包括: 集成USB 2.0全速功能控制器 內置USB收發器,無需外部電阻 工作電壓:3.0V至3.6V 支持的數據格式:數據位8,停止位1,無校驗 最高支持1Mbps的波特率 內置512字節接收緩沖區和512字節發送…

Ubuntu 22虛擬機【網絡故障】快速解決指南

Ubuntu22虛擬機突然無法連接網絡了&#xff0c;以下是故障排除步驟記錄。 Ubuntu 22虛擬機網絡故障快速解決指南 當在虛擬機中安裝的 Ubuntu 22 系統出現 ping: connect: 網絡不可達 和 ping: www.baidu.com: 域名解析出現暫時性錯誤的報錯時&#xff0c;通常意味著虛擬機無法…

實戰springcloud alibaba

實戰springcloud alibaba 前言 如何搭建一套最新的springcloud alibaba&#xff0c;以適配項目升級需求&#xff1f; 1.版本的選擇 2.各組件的適配 3.新技術的敏感性 4.前瞻性&#xff0c;幾年內不會被淘汰 參考資料&#xff1a;Spring Cloud Alibaba 參考文檔 https://spring…