【C++】第十七節—二叉搜索樹(概念+性能分析+增刪查+實現+使用場景)

好久不見,我是云邊有個稻草人

《C++》本文所屬專欄—持續更新中—歡迎訂閱

目錄

一、二叉搜索樹的概念

二、二叉搜索樹的性能分析

三、二叉搜索樹的插入

SearchBinaryTree.h

test.cpp

四、?叉搜索樹的查找

【只有一個3】

【有多個3】?

五、?叉搜索樹的刪除

六、二叉搜索樹的實現代碼

SearchBinaryTree.h

test.cpp?

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

7.1 key搜索場景

7.2 key/value搜索場景

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

.h

.cpp


正文開始——

一、二叉搜索樹的概念

?叉搜索樹?稱?叉排序樹,它或者是?棵空樹,或者是具有以下性質的?叉樹:

  • 若它的左?樹不為空,則左?樹上所有結點的值都?于等于根結點的值
  • 若它的右?樹不為空,則右?樹上所有結點的值都?于等于根結點的值
  • 它的左右?樹也分別為?叉搜索樹
  • ?叉搜索樹中可以?持插?相等的值,也可以不?持插?相等的值,具體看使?場景定義,后續我們學習map/set/multimap/multiset系列容器底層就是?叉搜索樹,其中map/set不?持插?相等值,multimap/multiset?持插?相等值


二、二叉搜索樹的性能分析

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

最差情況下,?叉搜索樹退化為單?樹(或者類似單?),其高度為: N

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

那么這樣的效率顯然是?法滿?我們需求的,我們后續需要繼續講解?叉搜索樹的變形,平衡?叉搜索樹AVL樹和紅?樹,才能適?于我們在內存中存儲和搜索數據。

另外需要說明的是,?分查找也可以實現 O(log2 N) 級別的查找效率,但是?分查找有兩?缺陷:

  1. 需要存儲在?持下標隨機訪問的結構中,并且有序。
  2. 插?和刪除數據效率很低,因為存儲在下標隨機訪問的結構中,插?和刪除數據?般需要挪動數據。

這?也就體現出了平衡?叉搜索樹的價值。


三、二叉搜索樹的插入

插?的具體過程如下:

  1. 樹為空,則直接新增結點,賦值給root指針
  2. 樹不空,按?叉搜索樹性質,插?值比當前結點?往右走,插?值比當前結點?往左走,找到空位置,插?新結點。
  3. 如果?持插?相等的值,插?值跟當前結點相等的值可以往右?,也可以往左?,找到空位置,插?新結點。(要注意的是要保持邏輯?致性,插?相等的值不要?會往右?,?會往左?)
SearchBinaryTree.h
#pragma once
#include<iostream>
using namespace std;template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}
};//class SearchBinaryTree
template<class K>
class BSTree
{typedef BSTNode<K> Node;
public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node * root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}Node* _root = nullptr;
};
test.cpp
#include"SearchBinaryTree.h"
#include<vector>int main()
{vector<int> a = { 0, 3, 1, 10, 1, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.Insert(e);}t.InOrder();return 0;
}

四、?叉搜索樹的查找

  1. 從根開始?較,查找x,x?根的值?則往右邊?查找,x比根值小則往左邊走查找。
  2. 最多查找?度次,?到空,還沒找到,這個值不存在。
  3. 如果不?持插?相等的值,找到x即可返回
  4. 如果支持插入相等的值,意味著有多個x存在,?般要求查找中序的第?個x。
【只有一個3】
bool Find(const K& key)
{Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return true;}}return false;
}
【有多個3】?

查找3,要求查找中序的第一個3。具體后面會講


五、?叉搜索樹的刪除

?先查找元素是否在?叉搜索樹中,如果不存在,則返回false。

如果查找元素存在則分以下四種情況分別處理:(假設要刪除的結點為N)

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

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

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

下面代碼的實現思路:分為3種情況(將上面的情況1歸為情況2或者是情況3) ,分為情況2,情況3,情況4來進行刪除結點

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{//找到了,刪除結點//情況2if (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;}//情況3else 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;}//情況4else{// 找右子樹最小節點(最左)替代我的位置Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;
}

六、二叉搜索樹的實現代碼

SearchBinaryTree.h
#pragma once
#include<iostream>
using namespace std;template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}
};//class SearchBinaryTree
template<class K>
class BSTree
{typedef BSTNode<K> Node;
public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}bool Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return true;}}return false;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{//找到了,刪除結點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;}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;}else{// 找右子樹最小節點(最左)替代我的位置Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node * root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}Node* _root = nullptr;
};
test.cpp?
#include"SearchBinaryTree.h"
#include<vector>int main()
{vector<int> a = { 0, 3, 1, 10, 1, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.Insert(e);}t.InOrder();//if (t.Find(300))//{//	cout << "找到了" << endl;//}//else//{//	cout << "沒找到" << endl;//}t.Erase(8);t.InOrder();t.Erase(14);t.InOrder();t.Erase(1);t.InOrder();for (auto e : a){t.Erase(e);t.InOrder();}return 0;
}

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

7.1 key搜索場景

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

場景1:?區??值守?庫,?區?庫買了?位的業主?才能進?區,那么物業會把買了?位的業主的 ?牌號錄?后臺系統,?輛進?時掃描?牌在不在系統中,在則抬桿,不在則提示非本小區車輛,無法進?。

場景2:檢查?篇英?章單詞拼寫是否正確,將詞庫中所有單詞放入二叉搜索樹,讀取?章中的單詞,查找是否在?叉搜索樹中,不在則波浪線標紅提示。

7.2 key/value搜索場景

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

場景1:簡單中英互譯字典,樹的結構中(結點)存儲key(英?)和vlaue(中?),搜索時輸?英?,則同時 查找到了英?對應的中?。

場景2:商場??值守?庫,??進場時掃描車牌,記錄車牌和入場時間,出口離場時,掃描車牌,查找入場時間,用當前時間-?場時間計算出停?時?,計算出停?費?,繳費后抬桿,車輛離場。 場景3:統計?篇?章中單詞出現的次數,讀取?個單詞,查找單詞是否存在,不存在這個說明第?次 出現,(單詞,1),單詞存在,則++單詞對應的次數。

7.3 key/value?叉搜索樹代碼實現
.h
	template<class K, class V>class BSTree{typedef BSTNode<K, V> Node;public:bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}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->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{//刪除if (cur->_left == nullptr){//if (parent == nullptr)if (cur == _root){_root = cur->_right;}else{// 父親指向我的右if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete 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;}else{// 找右子樹最小節點(最左)替代我的位置Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " " << root->_value << endl;_InOrder(root->_right);}Node* _root = nullptr;};
}
.cpp
int main()
{string arr[] = { "蘋果","香蕉","香蕉","西瓜", "蘋果", "西瓜", "蘋果", "蘋果", "西瓜",
"蘋果", "香蕉", "蘋果", "香蕉","香蕉","香蕉" };key_value::BSTree<string, int> countTree;for (auto& e : arr){//key_value::BSTNode<string, int>* ret = countTree.Find(e);auto ret = countTree.Find(e);if (ret == nullptr){countTree.Insert(e, 1);}else{ret->_value++;}}countTree.InOrder();return 0;
}

完——


冬眠_司南

快長大

至此結束——

我是云邊有個稻草人

期待與你的下一次相遇......

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

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

相關文章

Redis都有哪些數據結構,使用場景與原理解析

? String&#xff1a;字符串&#xff08;最常用、最簡單的類型&#xff09;&#x1f4cc; 應用場景&#xff1a;計數器&#xff08;如&#xff1a;頁面瀏覽量、點贊數、轉發數等&#xff09;緩存單個值&#xff08;如&#xff1a;token、驗證碼、用戶昵稱&#xff09;分布式鎖…

將EXCEL或者CSV轉換為鍵值對形式的Markdown文件

# 創建命令行參數解析器parser argparse.ArgumentParser(description將 CSV 或 Excel 文件轉換為帶標頭的 Markdown 格式)# 必需參數parser.add_argument(input_file, help輸入文件路徑 (CSV 或 Excel))parser.add_argument(output_file, help輸出 Markdown 文件路徑)# 可選參…

MySQL 配置性能優化實操指南:分版本5.7和8.0適配方案

在 MySQL 性能優化中&#xff0c;不同版本的特性差異會直接影響優化效果。本文基于 MySQL 5.7 和 8.0 兩個主流版本&#xff0c;通過版本適配的配置代碼、場景舉例和通俗解釋&#xff0c;讓優化方案更精準落地。一、硬件與系統配置優化&#xff08;基礎層優化&#xff09;1. 服…

【STM32實踐篇】:串口通信

文章目錄1. 串行通信與并行通信2. 異步通信與同步通信3. 單工&#xff0c;半雙工和全雙工通信4. 通信速率和接口標準5. USART 結構框圖6. 串口電路6.1 串口之間的連接6.2 串口與 RS232 的轉換和連接6.3 串口與 RS485 的轉換和連接6.4 串口與 USB 的轉換和連接7. USART 字符說明…

Trae IDE評測體驗:通過 MCP Server - Figma AI Bridge 一鍵將 Figma 轉為前端代碼

Trae IDE評測體驗&#xff1a;通過 MCP Server - Figma AI Bridge 一鍵將 Figma 轉為前端代碼 在現代前端開發中&#xff0c;從設計稿到可用頁面的交付往往需要大量重復勞動&#xff1a;切圖、手寫樣式、布局調整……而借助 MCP Server - Figma AI Bridge&#xff0c;我們可以…

文獻閱讀 250715-Atmospheric rivers cause warm winters and extreme heat events

Atmospheric rivers cause warm winters and extreme heat events 來自 <Atmospheric rivers cause warm winters and extreme heat events | Nature> ## Abstract: Definition: Atmospheric rivers (ARs) are narrow regions of intense water vapour transport in the …

線上協同辦公時代:以開源AI大模型等工具培養網感,擁抱職業變革

摘要&#xff1a;在提倡線上協同辦公的時代背景下&#xff0c;職場人需迅速提升工作能力以適應職業變革。培養網感成為時代所需&#xff0c;它為快速連接時代奠定基礎。本文深入探討了開源AI大模型、AI智能名片、S2B2C商城小程序源碼等工具在培養網感過程中的重要作用&#xff…

Netty網絡聊天室及擴展序列化算法

一、前言Netty是一個基于Java的高性能、事件驅動的網絡應用框架&#xff0c;廣泛應用于各種網絡通信場景。本文將介紹如何使用Netty構建一個簡單的網絡聊天室&#xff0c;并擴展序列化算法來提高數據傳輸效率和靈活性。二、Netty網絡聊天室的實現1. 項目結構我們將使用Maven構建…

基于單片機金沙河糧倉環境監測系統設計與實現

摘 要 本文圍繞基于單片機的金沙河糧倉環境監測系統展開設計與實現研究。系統以單片機為核心&#xff0c;集成 DHT11、MQ - 135 等傳感器&#xff0c;可實時精準監測糧倉溫濕度、氣體成分等關鍵環境參數。借助 LoRa、ESP8266 實現數據的可靠傳輸與遠程通信 &#xff0c;OLED 屏…

如何解決Android Studio安裝時無法下載SDK的問題(Windows、Linux、Mac解決方案大全)

如何解決Android Studio安裝時無法下載SDK的問題&#xff08;Windows、Linux、Mac解決方案大全&#xff09; 前言 對于全棧開發者而言&#xff0c;安裝 Android Studio 是邁向 Android 開發的第一步&#xff0c;但在 Windows、Linux、macOS 等不同平臺上&#xff0c;經常會遇…

SQL Server從入門到項目實踐(超值版)讀書筆記 21

9.5 數據的內連接查詢連接是關系數據庫模型的主要特點&#xff0c;連接查詢是關系數據庫中最主要的查詢&#xff0c;主要包括內連接、外連接等。內連接查詢操作列出與連接條件匹配的數據行&#xff0c;它使用比較運算符比較被鏈接列的列值。具體語法格式如下&#xff1a;SELECT…

瑞芯微7月17日舉辦開發者大會,多款AIoT新品發布,觸覺智能RK方案商報導

瑞芯微第九屆開發者大會RKDC 2025將有多款新品發布。 據瑞芯微電子Rockchip此前宣布&#xff1a;該企業的本年度開發者大會RKDC 2025將于7月17~18日在福建福州海峽國際會展中心舉行。本次瑞芯微開發者大會以“AIoT模型創新重做產品”為主題&#xff0c;關注傳統IoT功能設備向場…

Eureka+Ribbon實現服務注冊與發現

目錄 一、相關文章 二、兼容說明 三、服務注冊 四、服務發現 一、相關文章 基礎工程&#xff1a;gradle7.6.1springboot3.2.4創建微服務工程-CSDN博客 Eureka服務端啟動&#xff1a;https://blog.csdn.net/cherishSpring/article/details/149473554 Ribbon負載均衡&#…

數據庫、HTML

一、數據庫 數據庫文件與普通文件區別: 普通文件對數據管理(增刪改查)效率低2.數據庫對數據管理效率高&#xff0c;使用方便 常用數據庫: 1.關系型數據庫: 將復雜的數據結構簡化為二維表格形式 大型:0racle、DB2 中型:MySq1、sQLServer 小型:Sqlite 2.非關系型數據庫以鍵值對…

RCE隨筆(1)

哪些是可以執行代碼執行&#xff1a;php代碼。eval如&#xff1a;eval:<?php eval($_post[key]);eval&#xff1a;php中不被叫做函數叫動態執行命令assert&#xff1a;執行函數call_user_func_array<?php call_user_func_array(assert,array($_REQUEST[shu]));傳入xxs-…

FPGA——ZYNQ7020學習日記(PS端)4(開始PS控制VGA顯示)

1.DMA 我們的整體VGA顯示分為幾步&#xff1a;比如先導入VIDEO TIMING CONTROL來做對輸入數據的時序“對齊”&#xff0c;這里開源騷客寫的很詳細&#xff0c;先用了一個虛擬IO&#xff08;VIO)來作為輸入&#xff0c;導入了一個簡單的RTL模塊&#xff08;當VTL的使能信號有效…

AGX Xavier 搭建360環視教程【補充一:魚眼去畸變(Fisheye Undistortion)】

對每路幀做魚眼去畸變除了用cv::cuda::remap是否有更好的辦法呢&#xff1f;確實 cv::cuda::remap 不是唯一可選項&#xff0c;甚至未必是最高效或最適合實際業務量級的方案。&#x1f3af; 1?? 去畸變的原理魚眼相機&#xff08;或者大廣角相機&#xff09;會把直線拉彎&…

tomato靶機練習

下載完靶機后&#xff0c;直接運行&#xff0c;選擇安裝路徑后將虛擬機的網絡設置為nat模式&#xff0c;設置完成后重啟虛擬機掃描同一網段&#xff0c;查找主機&#xff0c;這里我們使用kali的nmap&#xff0c;既能找到主機&#xff0c;也能查看開啟的端口依次嘗試&#xff0c…

136. Java 泛型 - 下限通配符

文章目錄136. Java 泛型 - 下限通配符 (? super T)**1. 什么是下限通配符 (? super T)&#xff1f;****2. 為什么使用下限通配符&#xff1f;****3. 示例&#xff1a;使用 ? super Integer 允許添加 Integer****? 正確示例****4. 為什么 List<? super Integer> 和 L…

C++23中的std::expected:異常處理

C23中的std::expected:異常處理 眾所周知&#xff0c;C23以前的異常處理是比較麻煩的&#xff0c;尤其是自己要在可能拋出異常的地方&#xff0c;需要自己去捕獲它&#xff0c;比如除數為0的異常、使用std::stoi函數將字符串轉換成int整型數據、處理文件讀寫的異常等等&#x…