C++之二叉搜索樹

目錄

??叉搜索樹的概念

?二叉搜索數的性能分析

二叉搜索樹的模擬實現

定義二叉樹節點結構

?二叉搜索樹的插入

二叉搜索樹的查找

?二叉搜索樹的刪除

中序遍歷?

全部代碼

?二叉搜索樹key和key/value使用場景

?key搜索場景:

key/value搜索場景:

key/value?叉搜索樹代碼實現


?二叉搜索樹的概念

?叉搜索樹?稱?叉排序樹,它或者是?棵空樹,或者是具有以下性質的?叉樹:
? 若它的左?樹不為空,則左?樹上所有結點的值都?于等于根結點的值
? 若它的右?樹不為空,則右?樹上所有結點的值都?于等于根結點的值
? 它的左右?樹也分別為?叉搜索樹
? ?叉搜索樹中可以?持插?相等的值,也可以不?持插?相等的值,具體看使?場景定義

后續我們學習map/set/multimap/multiset系列容器底層就是?叉搜索樹,其中map/set不?持插?相等值,multimap/multiset?持插?相等值?。

?二叉搜索數的性能分析

最優情況:二叉搜索樹為完全二叉樹(或者接近完全二叉樹),其高度為:?log2 N

最差情況:二叉搜索樹為單支數,其高度為N

所以所以綜合???叉搜索樹增刪查改時間復雜度為: O(N)

那么這樣的效率顯然是?法滿?我們需求的,我們后續課程需要繼續講解?叉搜索樹的變形,平衡?叉搜索樹AVL樹和紅?樹,才能適?于我們在內存中存儲和搜索數據。
另外需要說明的是,?分查找也可以實現 O(log2 N) 級別的查找效率,但是?分查找有兩?缺陷:
1. 需要存儲在?持下標隨機訪問的結構中,并且有序。
2. 插?和刪除數據效率很低,因為存儲在下標隨機訪問的結構中,插?和刪除數據?般需要挪動數
據。
這?也就體現出了平衡?叉搜索樹的價值。

二叉搜索樹的模擬實現

定義二叉樹節點結構

定義一個二叉樹節點類,包含節點的值、左子節點指針和右子節點指針。

template<class T>
struct BSNode
{T _data;BSNode<T>* _left;BSNode<T>* _right;
//初始化節點,定義成有參的,后面新增節點需要調用BSNode(const T& data):_data(data), _left(nullptr), _right(nullptr){}
};

?二叉搜索樹的插入

插?的具體過程如下:
1. 樹為空,則直接新增結點,賦值給root指針
2. 樹不空,按?叉搜索樹性質,插?值?當前結點?往右?,插?值?當前結點?往左?,找到空位置,插?新結點。
3. 如果?持插?相等的值,插?值跟當前結點相等的值可以往右?,也可以往左?,找到空位置,插?新結點。(要注意的是要保持邏輯?致性,插?相等的值不要?會往右?,?會往左?

template<class T>
class BSTree
{typedef BSNode<T> Node;
public:
//此插入不插入相同的bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > data){parent = cur;cur = cur->_left;}else if (cur->_data < data){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(data);if (parent->_data > data){parent->_left = cur;}else{parent->_right = cur;}return true;}
//相同的向右走
/*bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > data){parent = cur;cur = cur->_left;}else{parent = cur;cur = cur->_right;}}cur = new Node(data);if (parent->_data > data){parent->_left = cur;}else{parent->_right = cur;}return true;}*/
//相同的向左走
/*bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data < data){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}cur = new Node(data);if (parent->_data < data){parent->_right = cur;}else{parent->_left = cur;}return true;}*/
private:Node* _root=nullptr;
};

二叉搜索樹的查找

1. 從根開始?較,查找x,x?根的值?則往右邊?查找,x?根值?則往左邊?查找。
2. 最多查找?度次,?到到空,還沒找到,這個值不存在。
3. 如果不?持插?相等的值,找到x即可返回
4. 如果?持插?相等的值,意味著有多個x存在,?般要求查找中序的第?個x。

    bool Find(const T&data){Node* cur = _root;while (cur){if (cur->_data > data){cur = cur->_left;}else if ((cur->_data < data)){cur = cur->_right;}elsereturn true;}return false;}

?二叉搜索樹的刪除

?先查找元素是否在?叉搜索樹中,如果不存在,則返回false。
如果查找元素存在則分以下四種情況分別處理:(假設要刪除的結點為N)

1. 要刪除結點N左右孩?均為空
2. 要刪除的結點N左孩?位空,右孩?結點不為空
3. 要刪除的結點N右孩?位空,左孩?結點不為空
4. 要刪除的結點N左右孩?結點均不為空

對應以上四種情況的解決?案:

1. 把N結點的?親對應孩?指針指向空,直接刪除N結點
2. 把N結點的?親對應孩?指針指向N的右孩?,直接刪除N結點
3. 把N結點的?親對應孩?指針指向N的左孩?,直接刪除N結點
4. ?法直接刪除N結點,因為N的兩個孩??處安放,只能?替換法刪除。找N左?樹的值最?結點R(最右結點)或者N右?樹的值最?結點R(最左結點)替代N,因為這兩個結點中任意?個,放到N的位置,都滿??叉搜索樹的規則。替代N的意思就是N和R的兩個結點的值交換,轉?變成刪除R結點,可以直接刪除。

    bool Erase(const T& data){if (!Find(data)){return false;}Node* parent = nullptr;//漏了分號,編譯器報錯錯誤。Node*cur = _root;while (cur){if (cur->_data > data){parent = cur;cur=cur->_left;}else if (cur->_data < data){parent = cur;cur = cur->_right;}else{//開始刪除//cur只有右節點if (cur->_left==nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}//cur只有左節點else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{//cur的左右子樹都不為空//找右子樹的最小節點(最左節點)代替Node* replaceParent = cur;Node* replace = cur->_right;while (replace->_left){replaceParent = replace;replace=replace->_left;}swap(cur->_data, replace->_data);//replace 已經是最左節點了,所以replace只可能是葉子節點后者只有右節點if (replaceParent->_left == replace){replaceParent->_left = replace->_right;}else{replaceParent->_right= replace->_right;}delete replace;return true;}}}return false;}

中序遍歷?

用中序遍歷我們就可以得到從小到大的排序。

void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}

全部代碼

#include<iostream>
//cout endl swap都在這個頭文件中
using namespace std;
template<class T>
struct BSNode
{T _data;BSNode<T>* _left;BSNode<T>* _right;BSNode(const T& data):_data(data), _left(nullptr), _right(nullptr){}
};
template<class T>
class BSTree
{typedef BSNode<T> Node;
public:bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > data){parent = cur;cur = cur->_left;}else{parent = cur;cur = cur->_right;}}cur = new Node(data);if (parent->_data > data){parent->_left = cur;}else{parent->_right = cur;}return true;}bool Find(const T&data){Node* cur = _root;while (cur){if (cur->_data > data){cur = cur->_left;}else if ((cur->_data < data)){cur = cur->_right;}elsereturn true;}return false;}bool Erase(const T& data){if (!Find(data)){return false;}Node* parent = nullptr;//漏了分號,編譯器報錯錯誤。Node*cur = _root;while (cur){if (cur->_data > data){parent = cur;cur=cur->_left;}else if (cur->_data < data){parent = cur;cur = cur->_right;}else{//開始刪除//cur有0/1個孩子if (cur->_left==nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{//cur的左右子樹都不為空(有兩個孩子//找右子樹的最小節點(最左節點)代替Node* replaceParent = cur;Node* replace = cur->_right;while (replace->_left){replaceParent = replace;replace=replace->_left;}swap(cur->_data, replace->_data);//replace 已經是最左節點了,所以replace只可能是葉子節點或者還有右節點if (replaceParent->_left == replace){replaceParent->_left = replace->_right;}else{replaceParent->_right= replace->_right;}delete replace;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}
private:Node* _root=nullptr;
};

上文中的T相當于就是下文中的K,_data相當于key。

?二叉搜索樹key和key/value使用場景

?key搜索場景:

只有key作為關鍵碼,結構中只需要存儲key即可,關鍵碼即為需要搜索到的值,搜索場景只需要判斷key在不在。key的搜索場景實現的?叉樹搜索樹支持增刪查,但是不支持修改,修改key破壞搜索樹結構了。

場景1:小區無人值守車庫,小區車庫買了車位的業主車才能進小區,那么物業會把買了車位的業主的車牌號錄入后臺系統,車輛進入時掃描車牌在不在系統中,在則抬桿,不在則提示非本小區車輛,無法進?。
場景2:檢查?篇英文文章單詞拼寫是否正確,將詞庫中所有單詞放入二叉搜索樹,讀取文章中單
詞,查找是否在?叉搜索樹中,不在則波浪線標紅提示。

key/value搜索場景:

每?個關鍵碼key,都有與之對應的值value,value可以任意類型對象。樹的結構中(結點)除了需要存儲key還要存儲對應的value,增/刪/查還是以key為關鍵字??叉搜索樹的規則進??較,可以快速查找到key對應的value。key/value的搜索場景實現的?叉樹搜索樹?持修改,但是不?持修改key修改key破壞搜索樹性質了,可以修改value。

場景1:簡單中英互譯字典,樹的結構中(結點)存儲key(英文)和vlaue(中文),搜索時輸入英文,則同時查找到了英文對應的中文。
場景2:商場無人值守車庫,入口進場時掃描車牌,記錄車牌和入場時間,出口離場時,掃描車牌,查找?場時間,?當前時間-入場時間計算出停車時長,計算出停車費用,繳費后抬桿,車輛離場。
場景3:統計?篇文章中單詞出現的次數,讀取?個單詞,查找單詞是否存在,不存在這個說明第?次出現,(單詞,1),單詞存在,則++單詞對應的次數。

key/value?叉搜索樹代碼實現

#include<iostream>
using namespace std;
template <class K,class V>
struct BSNode {BSNode<K,V>* _left;BSNode<K, V>* _right;K _key;V _value;BSNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}
};
template< class K, class V>
class BSTree
{typedef BSNode<K, V> Node;
public:void destroy(Node* root){if (root == nullptr){return;}destroy(root->_left);destroy(root->_right);delete root;}~BSTree(){destroy(_root);_root = nullptr;}bool Insert(const K& key, const V& val){if (_root == nullptr){_root = new Node(key, val);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if(cur->_key< key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key, val);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return cur;}}return nullptr;}bool erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{if (cur->_left == nullptr){if (cur==_root){_root = cur->_right;}else{if (parent->_left==cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;}else if(cur->_right==nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}else{Node* repalceP = cur;Node* repalce = repalceP->_right;while (repalce->left){repalceP = repalce;repalce = repalce->_left;}cur->_key = repalce->_key;cur->_value = repalce->_value;if (repalceP->_left == repalce){repalceP->_left = repalce->_right;}else{repalceP->_right = repalce->_right;}delete repalce;return true;}}}return false;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl; _InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}
private:Node* _root = nullptr;
};

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

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

相關文章

數據結構——哈希詳解

數據結構——哈希詳解 目錄 一、哈希的定義 二、六種哈希函數的構造方法 2.1 除留取余法 2.2 平方取中法 2.3 隨機數法 2.4 折疊法 2.5 數字分析法 2.6 直接定值法 三、四種解決哈希沖突的方法 3.1 開放地址法 3.1.1 線性探測法 3.1.2 二次探測法 3.2 鏈地址法 3…

使用U盤安裝 ubuntu 系統

1. 準備U 盤制作鏡像 1.1 下載 ubuntu iso https://ubuntu.com/download/ 這里有多個版本以供下載&#xff0c;本文選擇桌面版。 1.2 下載rufus https://rufus.ie/downloads/ 1.3 以管理員身份運行 rufus 設備選擇你用來制作啟動項的U盤&#xff0c;不能選錯了&#xff1b;點…

RadioMaster POCKET遙控器進入ExpressLRS界面一直顯示Loading的問題解決方法

RadioMaster POCKET遙控器進入ExpressLRS界面一直顯示Loading的問題解決方法 問題描述解決方法 問題描述 有一天我發現我的 RadioMaster POCKET 遙控器進入 ExpressLRS 設置界面時&#xff0c;界面卻一直停留在 “Loading” 狀態&#xff0c;完全無法進入設置界面。 我并沒有…

計算機網絡 - 三次握手相關問題

通過一些問題來討論 TCP 協議中的三次握手機制 說一下三次握手的大致過程&#xff1f;為什么需要三次握手&#xff1f;2 次不可以嗎&#xff1f;第三次握手&#xff0c;可以攜帶數據嗎&#xff1f;第二次呢&#xff1f;三次握手連接階段&#xff0c;最后一次ACK包丟失&#xf…

【RabbitMQ】核心概念和工作流程

文章目錄 RabbitMQ 工作流程流程圖 Producer 和 ConsumerConnecting 和 ChannelVirtual hostQueueExchangeRabbitMQ 工作流程 RabbitMQ 工作流程 流程圖 RabbitMQ 就是一個生產者/消費者模型 Producer 就是生產者、Consumer 就是消費者Broker 是 RabbitMQ 服務器生產者和消費…

龍虎榜——20250414

今天縮量上漲有些乏力&#xff0c;壓力位還在~ 2025年4月14日龍虎榜行業方向分析 一、核心主線方向 黃金與貴金屬&#xff08;避險邏輯強化&#xff09; ? 驅動邏輯&#xff1a;國際地緣沖突持續升溫&#xff08;如中東局勢、臺海動態&#xff09;&#xff0c;疊加美國特朗普…

蔚來汽車智能座艙接入通義大模型,并使用通義靈碼全面提效

為加速AI應用在企業市場落地&#xff0c;4月9日&#xff0c;阿里云在北京召開AI勢能大會。阿里云智能集團資深副總裁、公共云事業部總裁劉偉光發表主題演講&#xff0c;大模型的社會價值正在企業市場釋放&#xff0c;阿里云將堅定投入&#xff0c;打造全棧領先的技術&#xff0…

探索 Go 與 Python:性能、適用場景與開發效率對比

1 性能對比&#xff1a;執行速度與資源占用 1.1 Go 的性能優勢 Go 語言被設計為具有高效的執行速度和低資源占用。它編譯后生成的是機器碼&#xff0c;能夠直接在硬件上運行&#xff0c;避免了 Python 解釋執行的開銷。 以下是一個用 Go 實現的簡單循環計算代碼&#xff1a; …

虛幻引擎 Anim To Tex| RVT | RT

本文上篇分為4個部分&#xff1a;動畫驅動材質&#xff0c;虛擬紋理&#xff0c;Rendertarget&#xff0c;以及其他雜項的地編ta干貨整理。&#xff08;其中RT部分基本為UOD重要截圖摘錄&#xff09; 本文下篇為&#xff1a;skylight和directional light的區別&#xff0c;未完…

kingbase權限管理

1. kingbase模式權限管理 1.1授予用戶對模式的權限 以具有足夠權限的用戶登錄后&#xff0c;執行以下 SQL 語句來授予用戶對模式的相應權限。假設你要授予用戶 your_user 對模式 your_schema 的使用權限&#xff1a; sql -- 授予用戶使用模式的權限 GRANT USAGE ON SCHEMA …

9.thinkphp的請求

請求對象 當前的請求對象由think\Request類負責&#xff0c;該類不需要單獨實例化調用&#xff0c;通常使用依賴注入即可。在其它場合則可以使用think\facade\Request靜態類操作。 項目里面應該使用app\Request對象&#xff0c;該對象繼承了系統的think\Request對象&#xff…

Java從入門到“放棄”(精通)之旅——方法的使用⑤

Java從入門到“放棄”&#xff08;精通&#xff09;之旅&#x1f680;——方法的使用⑤ &#x1f4d6;引言&#xff1a; 在編程領域&#xff0c;代碼如同精密的齒輪相互咬合驅動程序運轉。隨著項目規模漸長&#xff0c;重復的代碼片段如同冗余的齒輪&#xff0c;不僅增加負重…

鴻蒙NEXT開發格式化工具類(ArkTs)

import { i18n } from kit.LocalizationKit;/*** 格式化工具類* 提供電話號碼格式化、歸屬地查詢、字符轉換等功能。* author: 鴻蒙布道師* since: 2025/04/14*/ export class FormatUtil {/*** 判斷傳入的電話號碼格式是否正確。* param phone - 待驗證的電話號碼* param coun…

[Python基礎速成]2-模塊與包與OOP

上篇??[Python基礎速成]1-Python規范與核心語法 目錄 Python模塊創建模塊與導入屬性__name__dir()函數標準模塊 Python包類類的專有方法 對象繼承多態 Python模塊 Python 中的模塊&#xff08;Module&#xff09;是一個包含 Python 定義和語句的文件&#xff0c;文件名就是模…

OSI參考模型和TCP/IP模型

1.OSI參考模型 OSI模型&#xff1a; OSI參考模型有7層&#xff0c;自下而上依次為物理層&#xff0c;數據鏈路層&#xff0c;網絡層&#xff0c;傳輸層&#xff0c;會話層&#xff0c;表示層&#xff0c;應用層。&#xff08;記憶口訣&#xff1a;物聯網叔會用&#xff09;。低…

linux Shell編程之循環語句(三)

目錄 一. for 循環語句 1. for語句的結構 2. for 語句應用示例 (1) 根據姓名列表批量添加用戶 (2) 根據 IP 地址列表檢查主機狀態 二. 使用 while 循環語句 1. while 語句的結構 2. while 語句應用示例 (1) 批量添加規律編號的用戶 (2) 猜價格游戲 三. until 循環語…

最新扣子實戰教程,利用扣子平臺通過在線表格記錄,批量生圖,再也不要一條條的粘貼提示詞了

1、功能描述 大家好&#xff0c;我是濤濤。今天我要給大家講解如何在扣子平臺上對接飛書電子表格。由于多維表格相對復雜&#xff0c;而很多業務場景其實只需要電子表格就能滿足&#xff0c;因此今天我們將演示如何在扣子平臺上讀取飛書電子表格并批量生成圖片。 先看效果&am…

java -jar指定類加載

在 Java 中&#xff0c;使用 java -jar 命令運行 JAR 文件時&#xff0c;默認會加載 JAR 文件的 MANIFEST.MF 文件中指定的 Main-Class。如果你想在運行時指定一個類來加載&#xff0c;可以通過以下方式實現&#xff1a; 方法 1&#xff1a;直接指定類路徑和類名 如果你不想使用…

多模態思維鏈(Multimodal Chain of Thought, MCoT)六大技術支柱在醫療領域的應用

多模態思維鏈(Multimodal Chain of Thought, MCoT)通過整合文本、圖像、視頻等多模態數據,結合邏輯推理與深度學習技術,在醫療領域展現出強大的應用潛力。其六大技術支柱在醫療場景中的具體應用如下: 一、推理構建視角:醫學診斷的流程優化 MCoT通過多模態推理鏈生成技術…

從文本到視頻:基于擴散模型的AI生成系統全解析(附PyTorch實現)

當語言遇見動態視覺 "用文字生成電影場景"曾是科幻作品中的幻想&#xff0c;如今借助擴散模型&#xff08;Diffusion Models&#xff09;正逐步成為現實。本文將手把手帶你實現一個創新的文本到視頻生成系統&#xff0c;通過深度解析擴散模型原理&#xff0c;結合獨…