一文學會二叉搜索樹,AVL樹,紅黑樹

文章目錄

  • 二叉搜索樹
    • 查找
    • 插入
    • 刪除
  • AVL樹
    • 概念
    • 插入
    • 旋轉
    • AVL驗證
  • 紅黑樹
    • 概念
    • 插入
    • 檢測

二叉搜索樹

也稱二叉排序樹二叉查找樹

二叉搜索樹可以為空,若不為空滿足以下性質
?1,非空左子樹小于根節點的值
?2,非空右子大于根節點的值
?3,左右子樹都是二叉搜索樹
在這里插入圖片描述

二叉搜索樹節點代碼:

template<class K>
struct TreeNode
{TreeNode<K>* _left;TreeNode<K>* _right;K _key;TreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};

查找

a,大于根節點向右找小于向左找
b,最多查找高度次,若走到空了,說沒沒有這個數。

bool find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn true;}return false;

插入

a, 若樹是空=
=,新增節點,將節點賦值給_root;
b, 若樹不為空,按
二叉搜索樹性質==找到插入位置,插入
在這里插入圖片描述
代碼實現

bool Insert(const K& key)
{if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}elsereturn false;}cur = new Node(key);if (cur->_key > parent->_key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

刪除

刪除要分4種情況:
a, 刪除節點無左右子樹
b, 刪除節點只有左子樹
c, 刪除節點只有右子樹
d, 刪除節點左右子樹都有

實際情況中,a可以和b/c結合起來,因此情況有三種

b讓父親節點指向左子樹(兩種情況,要刪除的節點是父親的左節點還是右節點),刪除節點
c讓父親節點指向右子樹,刪除節點
d:找到左子樹最大節點與根節點值替換?因為最大,所以該節點無左子樹,或只能有左子樹再按照b方法處理,刪除替換的節點。 //////////////或者找右子樹最小節點,再按照c方法處理,刪除替換節點。

代碼實現:

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key == key)break;else if (cur->_key < key){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}if (cur == nullptr)return false;//左空 a情況的左右子樹都空也進入這里if (cur->_left == nullptr){if (cur == _root)_root = cur->_right;else{if (parent->_left == cur){parent->_left = cur->_right;}elseparent->_right = cur->_right;}}//右空else if (cur->_right == nullptr){if (cur == _root)_root = cur->_left;else{if (parent->_left == cur){parent->_left = cur->_left;}elseparent->_right = cur->_left;}}else //替換{parent = cur;Node* leftmax = cur->_left;while (leftmax->_right){parent = leftmax;leftmax = leftmax->_right;}swap(cur->_key, leftmax->_key);if (parent->_left == leftmax){parent->_left = leftmax->_left;}elseparent->_right = leftmax->_left;cur = leftmax;}delete cur;return true;return false;
}```c
template<class K>
class BSTree
{typedef TreeNode<K> Node;
public:BSTree():_root(nullptr){}
void Inorder()
{_Inorder(_root);
}
void _Inorder(Node* cur)
{if (cur == nullptr)return;_Inorder(cur->_left);cout << cur->_key << " ";_Inorder(cur->_right);
}
private:Node* _root;
};

AVL樹

概念

雖然二叉搜索樹加快了查找效率,但是如果數據接近順序,那么二叉樹就退化成單側樹了,查找元素就相當于在順序表查找,效率低下,所以發明了AVL樹
在這里插入圖片描述
AVL樹:是空樹滿足以下性質的二叉搜索樹
?1,左右樹都是二叉搜索樹
?2,左右子樹高度之差(簡稱平衡因子)絕對值不超過1
在這里插入圖片描述
搜索時間復雜度log2n
AVL節點代碼:

struct AVLTreeNode
{AVLTreeNode(const T& date = T()):_left(nullptr),_right(nullptr),_parent(nullptr),_date(date),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;T _date;int _bf;
};

插入

AVL樹插入分兩步
1,按二叉搜索樹性質插入節點
2,調整平衡因子

插入新節點,那父節點平衡因子一定要調整,如果插在父節點左子樹,則父節點平衡–,反之++
接下來會有三種情況:
a,如果父節點平衡因子=0,說明平衡了,不用再調整了
b,如果父節點等于±1,說明還需要向上調整,祖父節點的平衡因子也要調整
c,如果父節點平衡因子等于±2,說明不平衡了需要旋轉

旋轉

旋轉分4種
1,如果新節點插在較高左子樹左側左左右單旋
在這里插入圖片描述
2,如果新節點插在較高右子樹右側右右左單旋
在這里插入圖片描述
3,如果新節點插在較高左子樹右側:左右先左單旋再右單旋
在這里插入圖片描述
旋轉后考慮平衡因子更新,據圖得知90父節點為130子節點為0

如果新插入節點是60,則父子為0,
根據新插入節點左右子樹不同,平衡因子更新不同

4,如果新節點插在較高右子樹左側右左,先右單旋再左單旋
在這里插入圖片描述

參考左右單旋

總結

  • 當父節點等于2時,

若子節點=1,則左單旋
若子節點=-1,則右單旋再單旋

  • 當父節點等于-2

若字節點=-1,則右單旋
若子節點=1,則左右單旋

代碼實現:

template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:AVLTree():_root(nullptr){}bool Insert(const T& date){if (nullptr == _root){_root = new Node(date);return true;}Node* pCur = _root;Node* pParent = nullptr;while (pCur){if (pCur->_date < date){pParent = pCur;pCur = pCur->_right;}else if (pCur->_date > date){pParent = pCur;pCur = pCur->_left;}else{return false;}}pCur = new Node(date);if (pParent->_date > date){pParent->_left = pCur;}else if (pParent->_date < date){pParent->_right = pCur;}pCur->_parent = pParent;while (pParent){if (pParent->_left == pCur){pParent->_bf--;}elsepParent->_bf++;if (pParent->_bf == 0)break;else if (pParent->_bf == -1 || pParent->_bf == 1){pCur = pParent;pParent = pParent->_parent;}else if (pParent->_bf == 2 || pParent->_bf == -2){if (pParent->_bf == 2 && pCur->_bf == 1){RotateL(pParent);}else if (pParent->_bf == -2 && pCur->_bf == -1){RotateR(pParent);}else if (pParent->_bf == 2 && pCur->_bf == -1){RotateRL(pParent);}else if (pParent->_bf == -2 && pCur->_bf == 1){RotateLR(pParent);}elsereturn false;}}return true;}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;Node* pnode = parent->_parent;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}parent->_bf = cur->_bf = 0;}void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;Node* pnode = parent->_parent;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}cur->_bf = parent->_bf = 0;}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(cur);RotateL(parent);if (bf == 0){cur->_bf = parent->_bf = curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){cur->_bf = 1;curleft->_bf = 0;parent->_bf = 0;}elseassert(false);}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(cur);RotateR(parent);if (bf == 0){cur->_bf = parent->_bf = curright->_bf = 0;}else if (bf == 1){cur->_bf = -1;parent->_bf = 0;curright->_bf = 0;}else if (bf == -1){cur->_bf = 1;curright->_bf = 0;parent->_bf = 0;}elseassert(false);}void Inorder(Node* root){if (root == nullptr){return;}Inorder(root->_left);cout << root->_date;Inorder(root->_right);}void Inorder(){Inorder(_root);}int Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool IsBalance(Node* root){if (root == nullptr)return true;int left = Height(root->_left);int right = Height(root->_right);if (right - left != root->_bf){std::cout << "平衡因子錯誤" << root->_bf << std::endl;}return abs(left - right) < 2&& IsBalance(root->_left)&& IsBalance(root->_right);}private:Node* _root;
};

AVL驗證

1,中序遍歷,如果是升序則說明是二叉平衡樹

2,判斷左右子樹高度差不超過1,確認平衡性

紅黑樹

概念

AVL樹限制比較大,旋轉次數多,所以發明了紅黑樹。
紅黑樹:一種二叉搜索樹,每個節點加了顏色限制紅或黑,通過對每一條路徑顏色限制,確保紅黑樹最短路徑不會超過最短路徑的兩倍,因而平衡。

?紅黑樹性質:
1,根節點必須是黑色的
2,每個節點不是黑色的就是紅色的
3,每一條路徑黑色節點數相同
4,如果一個節點是紅色的,那么兩個孩子都是黑色的(不能出現連續的紅色)
5,葉子節點是黑色的(此處葉子節點是空)
在這里插入圖片描述

插入

💪如果樹是空的,說明插入的是根節點,直接設置為黑
否則來的節點就設置為紅節點,因為每條路徑黑節點數相同,如果新來的變為黑,那么整個樹都要做調整
如果插入的紅節點父親是黑節點,那么無事發生,但如果父節點也是紅,那就違反紅黑樹性質(紅節點的孩子必須是黑節點),需要調整,接下來分兩種情況分析
💪1,孩子的叔叔(與父節點相對的另一支)存在且為紅,則把叔叔和父親都設為黑,祖父設為紅(依舊保持黑節點數不變),祖父設為紅了,還要檢查祖父的父親是否為紅,繼續向上調整
在這里插入圖片描述
💪2,叔叔節點 不存在/存在且為黑,旋轉,旋轉又分兩種情況,單旋和雙旋
在這里插入圖片描述
在這里插入圖片描述
旋轉之后父節點為黑,祖父為紅
代碼實現:

bool insert(const pair<key,value>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}elsereturn false;}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first <cur->_kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent&&parent->_col==RED){Node* grandparent = parent->_parent;if(grandparent->_left==parent){Node* uncle = grandparent->_right;//uncle存在且為紅if (uncle && uncle->_col==RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else//uncle不存在或存在且為黑{if (parent->_left == cur){//g//p//cRotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else if (parent->_right == cur){//g//p//cRotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}	}else{Node* uncle = grandparent->_left;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}//把根設為黑_root->_col = BLACK;return true;}void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;Node* pnode = parent->_parent;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}parent->_bf = cur->_bf = 0;
}
void RotateR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;Node* pnode = parent->_parent;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}
}

檢測

檢測是否為二叉樹分兩步
1,中序遍歷是否是升序
2,是否滿足紅黑樹性質

bool checkcolour(Node* root, int blacknum, int benchblack){//黑節點數目不一樣錯誤if (root == nullptr){if (blacknum != benchblack)return false;return true;}//遇到黑++if (root->_col == BLACK){++blacknum;}//是否連續紅節點if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "flase" << root->_kv.first;return false;}return checkcolour(root->_left,blacknum,benchblack) && checkcolour(root->_right,blacknum,benchblack);}bool isbalance(){return isbalance(_root);}bool isbalance(Node* root){//空樹正確//根不黑直接錯誤if (root == nullptr)return true;if (root->_col != BLACK)return false;Node* cur = root;int benchblack = 0;while (cur){//確定一條路上的黑節點if (cur->_col == BLACK)++benchblack;cur = cur->_left;			}return checkcolour(root,0,benchblack);}

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

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

相關文章

Android實戰進階 - 啟動頁

場景&#xff1a;當啟動頁處于倒計時階段&#xff0c;用戶將其切換為后臺的多任務卡片狀態&#xff0c;倒計時會繼續執行&#xff0c;直到最后執行相關邏輯&#xff08;一般會跳轉引導頁、進入主頁等&#xff09; 期望&#xff1a;而綜合市場來看&#xff0c;一般我們期望的是當…

無標記點動捕技術:重塑展廳展館的沉浸式數字交互新時代

在元宇宙浪潮的持續推進下&#xff0c;虛擬數字人正逐漸成為連接虛實世界的重要媒介。在展廳展館中&#xff0c;數字人不僅能夠扮演導覽員、講解員角色&#xff0c;更可通過情感化交互提升參觀體驗&#xff0c;使文化傳播更具感染力和沉浸感。虛擬人的引入&#xff0c;為傳統展…

輕松Linux-7.Ext系列文件系統

天朗氣清&#xff0c;惠風和煦&#xff0c;今日無事&#xff0c;遂來更新。 1.概述 總所周知&#xff0c;我們存的數據都是在一個叫硬盤的東西里面&#xff0c;這個硬盤又像個黑盒&#xff0c;這章就來簡單解析一下Linux中文件系統。 現在我們用的大都是固態硬盤&#xff0c;…

Matlab機器人工具箱使用4 蒙特卡洛法繪制工作區間

原理&#xff1a;利用rand隨機數&#xff0c;給各個關節設置隨機關節變量&#xff0c;通過正運動學得到末端位姿變換矩陣&#xff0c;然后利用變換矩陣2三維坐標標記出末端坐標&#xff0c;迭代多次就可以構成點云。教程視頻&#xff1a;【MATLAB機器人工具箱10.4 機械臂仿真教…

【項目】在AUTODL上使用langchain實現《紅樓夢》知識圖譜和RAG混合檢索(三)知識圖譜和路由部分

首先在數據集 - 開放知識圖譜下載紅樓夢的知識圖譜&#xff0c;這個網站上有各種各樣的知識圖譜&#xff0c;可以挑你感興趣的做( ? ?ω?? ) 這個知識圖譜的作者們已經將三元組抽取出來了&#xff0c;我們可以直接用&#xff0c;如果你對三元組是如何生成的感興趣&#xf…

pycharm 最新版上一次編輯位置

2025nipycharm方法一&#xff1a;用快捷鍵&#xff08;最方便&#xff09;跳回上一次編輯位置&#xff1a;Windows/Linux: Ctrl Alt ←macOS: ? Option ←跳到前一次位置&#xff1a;Windows/Linux: Ctrl Alt →macOS: ? Option →方法二&#xff1a;顯示工具欄按鈕在…

前端性能監控與優化:從 Lighthouse 到 APM

在當今競爭激烈的數字環境中&#xff0c;用戶對Web應用性能的要求日益提高。一個緩慢或響應遲鈍的應用不僅會流失用戶&#xff0c;更可能損害品牌形象和商業價值。因此&#xff0c;前端性能的監控與優化已成為前端開發不可或缺的關鍵環節。本文將深入探討從基礎的性能評估工具L…

TC_Motion多軸運動-電子齒輪

目錄 電子齒輪 【基本概念】 【應用示例】 【開發總結】 END 電子齒輪 【基本概念】 定義:通過軟件方法實現機械齒輪的速比調節功能(兩個軸成線性比例旋轉) 優點 免維護,告別機械損耗 易調節,任意修改齒輪比 精度高,無機械背隙 應用場景 多臺電機拖動同一負載,要求多臺…

CentOS 7 下載教程

訪問阿里云鏡像站 阿里巴巴開源鏡像站 選擇centos 點這個 選擇7版本 進入isos目錄 點這個 選擇這個版本 因為這個鏡像的日期更新 推薦下載 DVD 版&#xff1a;包含完整系統和常用軟件&#xff0c;無需額外聯網安裝組件Minimal 版&#xff1a;精簡版&#xff0c;僅包含基礎系…

MAC在home下新建文件夾報錯“mkdir: test: Operation not supported”

在Mac電腦中&#xff0c;home文件夾下不能直接mkdir&#xff0c;sudo 也還是不行&#xff0c;提示“mkdir: test: Operation not supported”。網上找的解決方案不好使&#xff0c;因為沒有關閉系統完整性保護關閉系統完整性保護查看SIP當前的狀態csrutil status如果是開啟狀態…

交叉導軌從測試儀到工作臺的精密運動控制

在精密儀器領域微米級的運動精度與納米級的穩定性往往是決定設備性能上限的核心指標。而支撐這一技術鴻溝跨越的&#xff0c;往往隱匿于機械結構的“毫厘之間”——交叉導軌。以下是其在不同精密儀器中的具體應用&#xff1a;光學測試儀&#xff1a;光學測試儀主要用于各種高精…

內網穿透的應用-Navidrome與cpolar本地搭建跨網絡訪問的云音樂服務器

文章目錄前言1. 安裝Docker2. 創建并啟動Navidrome容器3. 公網遠程訪問本地Navidrome3.1 內網穿透工具安裝3.2 創建遠程連接公網地址3.3 使用固定公網地址遠程訪問前言 音樂收藏存在平臺版權限制、音質壓縮和訪問不便等問題。Navidrome 開源音樂服務器與 cpolar 內網穿透服務的…

FastAPI 訪問不了API文檔或配置不生效的解決方法

FastAPI中文教程 本文背景 FastAPI框架自帶交互式api文檔,通過路由/docs或者/redoc 訪問&#xff0c;但是FastAPI 的文檔界面&#xff08;如 /docs 和 /redoc&#xff09;依賴于外部的 JavaScript 和 CSS 庫&#xff0c;如果項目部署環境網絡不佳或者無法訪問外網的時候&…

IAR 集成開發環境入門指南:字體設置與調試實戰

一、IAR 的基本使用教程1. IAR 顏色字體大小設置打開設置路徑&#xff1a;點擊頂部菜單欄 Tools → 選擇 Options&#xff0c;打開 IDE 配置窗口。進入字體顏色設置界面&#xff1a;在彈出的 “IDE Options” 窗口中&#xff0c;雙擊展開 Editor 選項&#xff0c;然后點擊 Colo…

10:00開始面試,10:06就出來了,問的問題有點變態。。。

從小廠出來&#xff0c;沒想到在另一家公司又寄了。到這家公司開始上班&#xff0c;加班是每天必不可少的&#xff0c;看在錢給的比較多的份上&#xff0c;就不太計較了。沒想到8月一紙通知&#xff0c;所有人不準加班&#xff0c;加班費不僅沒有了&#xff0c;薪資還要降40%,這…

Flink 狀態管理的核心能力

我們來看一個復雜的實際案例&#xff1a;阿里巴巴菜鳥的實時物流追蹤系統。 該系統處理來自多個電商平臺&#xff08;天貓、淘寶、速賣通&#xff09;的訂單包裹&#xff0c;通過一個復雜的處理流程&#xff1a; 合并與去重&#xff1a;通過聚合操作將不同來源的訂單合并并去重…

基于go語言的云原生TodoList Demo 項目,驗證云原生核心特性

以下是一個基于 Go 語言 的云原生 TodoList Demo 項目&#xff0c;涵蓋 容器化、Kubernetes 編排、CI/CD、可觀測性、彈性擴縮容 等核心云原生特性&#xff0c;代碼簡潔且附詳細操作指南&#xff0c;適合入門學習。項目概覽 目標&#xff1a;實現一個支持增刪改查&#xff08;C…

手機能看、投屏 / 車機不能看與反向鏈接驗證類似嗎?

有一定關聯&#xff0c;但兩者的技術邏輯并非完全等同 ——“手機能看、投屏 / 車機不能看” 的核心原因更復雜&#xff0c;反向鏈接驗證是其中一種可能的限制手段&#xff0c;但不是唯一甚至不是最主要的手段。要理清這個問題&#xff0c;需要先拆解 “投屏 / 車機播放受限” …

25年9月通信基礎知識補充1:NTN-TDL信道建模matlab代碼(satellite-communications toolbox學習)

看文獻過程中不斷發現有太多不懂的基礎知識&#xff0c;故長期更新這類blog不斷補充在這過程中學到的知識。由于這些內容與我的研究方向并不一定強相關&#xff0c;故記錄不會很深入請見諒。 【通信基礎知識補充10】25年9月通信基礎知識補充1&#xff1a;NTN-TDL信道建模matlab…

洛谷P3370 【模板】字符串哈希 (哈希表)詳解

題目如下&#xff1a;&#xff08;注&#xff1a;解此題我只需左手一根指頭&#xff0c;哈哈哈哈哈哈哈&#xff09;注意&#xff0c;哈希表的好處是能大幅度減少尋找遍歷的時間可能有人不理解哈希值&#xff0c; 這里哈希的模的值一般得是比較大的質數&#xff0c;如標準的100…