二叉搜索樹--通往高階數據結構的基石

目錄

前言:

1、二叉搜索樹的概念

2、二叉搜索樹性能分析

3、二叉搜索樹的實現

BinarySelectTree.h

test.cpp

4、key 和 key / value( map 和 set 的鋪墊 )?


前言:

? 又回到數據結構了,這次我們將要學習一些復雜的數據結構,例如 map,set,multimap,multiset 和紅黑樹,AVL樹等,而這些復雜數據結構的基礎都是二叉搜索樹,避免大家沒有聽過這個玩意,今天咱們來單獨講一講二叉搜索樹。

1、二叉搜索樹的概念

?二叉搜索樹又稱二叉排序樹(因為二叉搜索樹中序遍歷的結果是從小到大排序的),它或者是一棵空樹,或者是具有以下性質的二叉樹:

? 若它的左子樹不為空,則左子樹上所有結點的值都小于等于根結點的值

? 若它的右子樹不為空,則右子樹上所有結點的值都大于等于根結點的值

? 它的左右子樹也分別為二叉搜索樹

? 二叉搜索樹中可以支持插入相等的值,也可以不支持插入相等的值,具體看使用場景定義,后續我們學習map/set/multimap/multiset系列容器底層就是二叉搜索樹,其中map/set不支持插入相等 值(所以在需要去重的算法題中 set 和 map 是很好用的),multimap/multiset支持插入相等值。

?下面我實現的二叉搜索樹就是以左邊的樹作為例子進行增刪查操作的。

2、二叉搜索樹性能分析

? 由于二叉搜索樹每一層只會選擇左節點和右節點中的一個比較大小,所以正常情況下性能為其高度。

? 最優情況下:二叉樹是一棵完全二叉樹,那么此時,每次搜索都能排除一半情況,此時高度為logN,所以搜索時間復雜度為O(logN)。

? 最壞情況下:二叉樹的所有結點都放在一條分叉上,此時高度最高,為N,此時時間復雜度為O(N)。

? 所以綜合而言,二叉搜索樹增刪查的時間復雜度為O(N),二叉搜索樹一般不支持改,因為一改二叉搜素樹就混亂了。

? 由于當出現一個分叉的情況時,時間復雜度有點高,所以就出現了平衡二叉樹這一做法,這個比較復雜,后面AVL樹和紅黑樹會講。

? 其實二分查找時間復雜度也是O(logN),但是缺陷也很大:

1、首先下標要滿足隨機訪問,而且必須有序。

2、其次插入和刪除效率太低了,刪除或插入一個數據需要挪動一堆數據。

到這里二叉搜索樹的基礎知識就結束了,下面我們來實現一下這個簡單的數據結構吧。

3、二叉搜索樹的實現

我們分兩個文件來寫,代碼中給出了注釋:

BinarySelectTree.h

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <algorithm>
using namespace std;template <class K>
class BSTreeNode
{
public:K _key;BSTreeNode<K>* _left;BSTreeNode<K>* _right;BSTreeNode(const K& key):_key(key), _left(nullptr), _right(nullptr){ }
};template <class K>
class BSTree
{
public:typedef BSTreeNode<K> Node;BSTree() = default;//由于我們已經有了拷貝構造,所以不寫構造函數就會報錯(因為缺少默認構造),//而這個類沒必要寫構造,這個句子就是告訴編譯器構造函數交給你默認生成了~BSTree()//析構函數,利用后序遍歷析構,從葉子節點開始析構,運用遞歸{Destroy(_root);_root = nullptr;}void Destroy(Node* root)//這里我們把Destroy單獨封裝,因為要寫遞歸{if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}BSTree(const BSTree<K>& bst)//拷貝構造{_root = Copy(bst._root);}Node* Copy(Node* root)//這里要從根節點開始復制,運用的是先序遍歷{if (root == nullptr){return nullptr;}Node* newRoot = new Node(root->_key);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}bool Insert(K key){if (_root == nullptr)//如果樹為空,則直接插入{_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur)//直到cur為空為止{if (cur->_key > key)//結點值大于插入值,則往左孩子方向走{parent = cur;cur = cur->_left;}else if (cur-> _key < key)//結點值小于插入值,則往右孩子方向走{parent = cur;cur = cur->_right;}else//結點值等于插入值,我們這里不支持插入相等值{return false;}}if (key < parent->_key)//cur到空后你并不知道插入值比父節點大還是小,判斷后再插入{parent->_left = new Node(key);}if (key > parent->_key){parent->_right = new Node(key);}return true;}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ' ';_InOrder(root->_right);}bool Find(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{return true;}}return false;}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->_right == nullptr)//只有左子樹{if (cur == _root)//如果要刪除的為根節點,如果不寫,下面的parent就會使空,//空的引用會報錯{_root = cur->_left;}else{if (parent->_left == cur)//如果父節點的左孩子為要刪除的節點{parent->_left = cur->_left;//cur只有左子樹,把左子樹放在父節點的左子樹上}if (parent->_right == cur)//如果父節點的右孩子為要刪除的節點{parent->_right = cur->_left;//把cur左子樹放到父節點的右子樹上}}delete cur;}else if (cur->_left == nullptr)//只有右子樹{if (_root == cur){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}if (parent->_right == cur){parent->_right = cur->_right;}}delete cur;}else//左右子樹都有,將要刪除的節點與中序遍歷的前驅節點(中序遍歷前一個結點)//或后繼節點(中序遍歷的后一個節點)交換//中序遍歷的前驅節點即待刪除節點的左子樹的最大節點,也就是左子樹最右節點//中序遍歷的后繼節點即待刪除節點的右子樹的最小節點,也就是右子樹最左節點{Node* replace = cur->_right;Node* replaceParent = cur;while (replace->_left)//尋找右子樹的最左節點{replaceParent = replace;replace = replace->_left;}swap(replace->_key, cur->_key);if (replaceParent->_left == replace)//有兩種情況(通過replaceParent和replace有沒有移動來判斷):有可能找到了最左節點replaceParent->_left = replace->_left;else//還有可能replace就沒有左節點,此時它為右子樹最小節點replaceParent->_right = replace->_right;delete replace;}return true;}}return false;}
private :Node* _root = nullptr;
};void test()
{int arr[] = { 8, 3, 1, 10, 1, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : arr){t.Insert(e);}t.InOrder();//cout << t.Find(3) << endl;//cout << t.Find(5) << endl;////左右都不為空//t.Erase(8);//t.InOrder();//t.Erase(3);//t.InOrder();////右為空//t.Erase(14);//t.InOrder();////左為空//t.Erase(6);//t.InOrder();//for (auto e : arr)//{//	t.Erase(e);//	t.InOrder();//}BSTree<int> t1(t);t1.InOrder();
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include "BinarySelectTree.h"int main()
{test();return 0;
}

4、key 和 key / value( map 和 set 的鋪墊 )?

? 在二叉搜索樹中,我們都是以 key 作為樹排序的依據的,比如整型的插入,結點里存儲的整型就是它的鍵值,也就是我們這里所說的 key ,key 是二叉搜索樹排序的依據。

? key的應用場景,比如我們小區的門衛系統,如果車主住在小區,那么業主的汽車的信息就會存入門衛系統,每次只需要按照 key 來查找就可以了(當然小區系統存儲肯定用的是數據庫,但也可以是二叉搜索樹的應用,只不過存儲空間小,這樣存儲的數據都存到內存里了,容易弄丟)。

? 接著就是鍵值對 key / value,這個就是 map 的關鍵,上面說過,multimap 是支持等值存儲的,在 multimap 中,key 和 value 是綁定存儲的,比如我們在 multimap 中存儲了 3 這個整型,那么鍵值 key 就是 3 ,如果他只出現了一次,那么 value 就是 1 ,每多存儲一個 key ,value 就++,當然,這只是? multimap 的一個應用,value同樣可以存儲字符串等,這里只是告訴大家兩者是綁定的。

? 但是有一點大家要知道,不管是 map 和 set ,都只看key,而不去看value。

OK,到這里我們的二叉搜索樹就結束了,接下來我們將要面對的是高階數據結構,讓我們一起加油吧。

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

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

相關文章

Profinet轉Ethernet IP網關接入五軸車床上下料機械手控制系統的配置實例

本案例為西門子1200PLC借助PROFINET轉EtherNet/IP網關與搬運機器人進行連接的配置案例。所需設備包括&#xff1a;西門子1200PLC、Profinet轉EtherNet/IP網關以及發那科&#xff08;Fanuc&#xff09;機器人。開啟在工業自動化控制領域廣泛應用、功能強大且專業的西門子博圖配置…

專題二_滑動窗口_長度最小的子數組

引入&#xff1a;滑動窗口首先&#xff0c;這是滑動窗口的第一道題&#xff0c;所以簡短的說一下滑動窗口的思路&#xff1a;當我們題目要求找一個滿足要求的區間的時候&#xff0c;且這個區間的left和right指針&#xff0c;都只需要同向移動的時候&#xff0c;就可以使用滑動窗…

解鎖高效開發:AWS 前端 Web 與移動應用解決方案詳解

告別繁雜的部署與運維&#xff0c;AWS 讓前端開發者的精力真正聚焦于創造卓越用戶體驗。在當今快速迭代的數字環境中&#xff0c;Web 與移動應用已成為企業與用戶交互的核心。然而&#xff0c;前端開發者常常面臨諸多挑戰&#xff1a;用戶認證的復雜性、后端 API 的集成難題、跨…

北京JAVA基礎面試30天打卡04

1. 單例模式的實現方式及線程安全 單例模式&#xff08;Singleton Pattern&#xff09;確保一個類只有一個實例&#xff0c;并提供一個全局訪問點。以下是常見的單例模式實現方式&#xff0c;以及如何保證線程安全&#xff1a; 單例模式的實現方式餓漢式&#xff08;Eager Init…

Redis 緩存三大核心問題:穿透、擊穿與雪崩的深度解析

引言在現代互聯網架構中&#xff0c;緩存是提升系統性能、降低數據庫壓力的核心手段之一。而 Redis 作為高性能的內存數據庫&#xff0c;憑借其豐富的數據結構、靈活的配置選項以及高效的網絡模型&#xff0c;已經成為緩存領域的首選工具。本文將從 Redis 的基本原理出發&#…

耘瞳科技國產化點云處理軟件,開啟智能化三維測量新時代

在現代工業制造領域&#xff0c;三維點云數據已成為推動生產效率提升、質量控制優化以及智能制造轉型的關鍵技術之一。三維點云數據能夠提供高精度的物體表面信息&#xff0c;廣泛應用于制造零件的質量檢測&#xff1b;通過點云數據與CAD模型的對比分析&#xff0c;可以快速檢測…

RabbitMQ面試精講 Day 8:死信隊列與延遲隊列實現

【RabbitMQ面試精講 Day 8】死信隊列與延遲隊列實現 文章標簽 RabbitMQ,消息隊列,死信隊列,延遲隊列,面試技巧,分布式系統 文章簡述 本文是"RabbitMQ面試精講"系列第8天&#xff0c;深入講解死信隊列與延遲隊列的實現原理與實戰應用。文章詳細解析死信隊列的觸發…

團結引擎 1.5.0 版本發布:Android App View 功能詳解

核心亮點 原生安卓應用支持 2D & 3D 雙形態呈現 編輯器全流程集成 靈活調控功能 多應用并行展示 智能座艙應用示例 快速入門指南 開發說明 功能支持 實驗性功能 資源鏈接 團結引擎 1.5.0 版本已于 4 月 14 日正式上線。本次更新中&#xff0c;車機版引入了一項突…

基于SpringBoot的OA辦公系統的設計與實現

文章目錄前言詳細視頻演示具體實現截圖后端框架SpringBoot持久層框架MyBaits成功系統案例&#xff1a;代碼參考數據庫源碼獲取前言 博主介紹:CSDN特邀作者、985高校計算機專業畢業、現任某互聯網大廠高級全棧開發工程師、Gitee/掘金/華為云/阿里云/GitHub等平臺持續輸出高質量…

知識隨記-----用 Qt 打造優雅的密碼輸入框:添加右側眼睛圖標切換顯示

Qt 技巧&#xff1a;通過 QLineEdit 右側眼睛圖標實現密碼可見性切換 文章目錄Qt 技巧&#xff1a;通過 QLineEdit 右側眼睛圖標實現密碼可見性切換概要整體架構流程技術名詞解釋技術細節實現效果展示概要 本文介紹如何使用 Qt 框架為 QLineEdit 控件添加一個右側的眼睛圖標&a…

Unity里的對象旋轉數值跳轉問題的原理與解決方案

文章目錄1. 問題描述2. 問題原因3. 解決方案3.1通過多個父子關系從而控制旋轉&#xff08;推薦&#xff09;3.2 使用四元數進行旋轉1. 問題描述 我們現在寫一個3D的Unity程序&#xff0c;我們現在設置了一個物體后&#xff0c;我們想旋轉使其改為我們想要的情況。但是我們如果…

為什么現代 C++ (C++11 及以后) 推薦使用 constexpr和模板 (Templates) 作為宏 (#define) 的替代品??

我們用現實世界的比喻來深入理解??為什么 C 中的宏 (#define) 要謹慎使用&#xff0c;以及為什么現代 C (C11 及以后) 推薦使用 constexpr 和模板 (Templates) 作為替代品。??&#x1f9e9; ??核心問題&#xff1a;宏 (#define) 是文本替換??想象宏是一個 ??“無腦的…

PyCharm vs. VSCode 到底哪個更好用

在 Python 開發者中&#xff0c;關于 PyCharm 和 VSCode 的討論從未停止。一個是功能齊備的集成開發環境&#xff08;IDE&#xff09;&#xff0c;另一個是輕快靈活的代碼編輯器。它們代表了兩種不同的開發哲學&#xff0c;選擇哪個&#xff0c;往往取決于你的項目需求、個人習…

FPGA學習筆記——VGA彩條顯示

目錄 一、任務 二、分析 三、代碼 四、實驗現象 五、更新 一、任務 使用VGA實現彩條顯示&#xff0c;模式是640x48060。 二、分析 首先&#xff0c;模式是640x48060&#xff0c;那么對照以下圖標&#xff0c;知道其它信息&#xff0c;不清楚時序和VGA掃描方式的可以看看這…

ES-301A :讓 Modbus 設備無縫接入工業以太網的高效橋梁

在工業自動化領域&#xff0c;串口設備與以太網的互聯互通是提升系統效率的關鍵。ES-301A 工業以太網串口網關作為上海泗博自動化精心打造的專業解決方案&#xff0c;以強大的協議轉換能力、工業級可靠性和靈活配置特性&#xff0c;成為連接 Modbus RTU/ASCII 設備與 Modbus TC…

【學習筆記】FTP庫函數學習

【學習筆記】FTP庫函數學習 FTP基本指令步驟 1、初始化會話句柄&#xff1a;CURL *curl curl_easy_init(); 2、設置會話選項&#xff1a; 設置服務器地址&#xff0c;設置登錄用戶和密碼 curl_easy_setopt(curl, CURLOPT_URL, ftp_server); curl_easy_setopt(curl, CURLOPT_US…

ARM Cortex-M異常處理高級特性詳解

1. 異常處理概述 ARM Cortex-M處理器提供了高效的異常處理機制&#xff0c;包含多種硬件優化特性&#xff0c;顯著提升了中斷響應性能和系統效率。這些特性對于實時嵌入式系統和網絡協議棧&#xff08;如LwIP&#xff09;的性能至關重要。 1.1 Cortex-M異常處理架構 Cortex-M異…

【圖像算法 - 08】基于 YOLO11 的抽煙檢測系統(包含環境搭建 + 數據集處理 + 模型訓練 + 效果對比 + 調參技巧)

一、項目背景與需求 【打怪升級 - 08】基于 YOLO11 的抽煙檢測系統&#xff08;包含環境搭建 數據集處理 模型訓練 效果對比 調參技巧&#xff09;今天我們使用YOLO11來訓練一個抽煙檢測系統&#xff0c;基于YOLO11的抽煙檢測系統。我們使用了大概兩萬張圖片的數據集訓練了…

vue2升級vue3中v-model的寫法改造

vue2選項式 <template><div><el-rowclass"group-title":title"$t(restore_default_parameters)">{{ $t(restore_default_parameters) }}</el-row><el-form-item :label"$t(restore_default_parameters)" class"…

5G-LEO 簡介

1. 什么是 5G-LEO 5G-LEO 指的是將 5G 新空口&#xff08;5G NR&#xff09;服務擴展到低軌衛星&#xff08;LEO&#xff09;上的非地面網絡&#xff08;NTN, Non-Terrestrial Network&#xff09;方案。通過在距地面約500–2 000 km 的低軌道衛星上部署通信載荷&#xff0c;5G…