二叉搜索樹K和KV結構模擬

一 什么是二叉搜索樹

? 這個的結構特性非常重要,是后面函數實現的結構基礎,二叉搜索樹的特性是每個根節點都比自己的左樹任一節點大,比自己的右樹任一節點小。

例如這個圖,

?41是根節點,要比左樹大,比右樹小,滿足但還不夠,還要去看看41的左子樹的根和右子樹的根是否滿足,更要判斷這棵樹上所有的根節點是不是都滿足。而這棵樹最厲害的地方之一我們用中序遍歷(順序左根右)便可以知道,遍歷結果為13,15,17,22,28,33,37,41,42,50,53,58,61,66,78,排序不就排好了嗎,復雜度可媲美快排和歸并。二叉搜索樹另一個功能那當然就是搜索了,例如我們要找66,66比根節點大,就不用去左子樹找了,一下子少遍歷一半,然后就去右子樹找,和根節點58比較,66比58大,再去右子樹找,再比較就找到了,最多查找高度次,滿二叉樹下為log(n)。而二叉搜索樹是不是完美無缺,我也以為已經完美了,不好意思,我太年輕了,直到我看到下面這顆樹。

?這個查找一次的效率就退化為O(N)了,解決辦法:轉化為平衡二叉樹,通俗點就是換個根節點重新構造二叉樹,例如把5或者7換成根節點,大家可以試試練習一下構建二叉樹,構建完后的高度肯定比上圖低,查找效率不就高了嗎。

? ?在說二叉搜索樹的實現前,我們先說說什么是K結構,什么是KV結構,K結構就是只存一個數據,這個數據稱為關鍵碼,例如在英文詞庫里找一個英文單詞,就是用關鍵碼查找,需要的數據也是找到的關鍵碼,但是KV結構就不同了,例如,通過拼音找漢字,這個時候拼音就是關鍵碼,但是我們需要的數據不是拼音這個關鍵碼,而是與之對應的漢字,這就是KV結構。

二 二叉搜索樹K結構實現

1 樹的節點類

template<class T>struct TreeNode{TreeNode(const T& val):_val(val)//_val可能為自定義類型,在初始化列表初始化方便調用構造函數{;}TreeNode* left = nullptr;TreeNode* right = nullptr;T _val;};

2 BinaryTree樹類

查找和排序都封裝到了BinaryTree類中,和list一樣,將節點類和樹類分開封裝。

?(1)?默認構造函數

template<class T>class BinaryTree{public:typedef TreeNode<T> Node;BinaryTree(Node*node=nullptr)  該構造函數是用節點指針初始化,也可以再寫個構造函數用個BinaryTree對象初始化:_root(node){;}private:Node* _root = nullptr;樹類只需根節點地址即可管理整棵樹};

? (2)? 拷貝構造函數

因為兩個copy函數都是用遞歸遍歷二叉樹,所以只能再寫一個子函數,畢竟外部無法傳_root指針。

void copy1(Node*tree)//前序遍歷加復用insert拷貝二叉樹{if (tree == nullptr)return;insert(tree->_val); 先插入根節點的值,再去左子樹和右子樹獲取節點的數值insert內部會開辟空間copy(tree->left);copy(tree->right);}方法二比較巧妙的是它的第一個參數,root是外部傳參_root的引用所以root=new node(),可以直接修改根節點而要拷貝左子樹就傳root->left的引用,這樣new出來的節點可以直接連接到根的_left指針上。右子樹同理。void copy2(Node*&root,Node*tree){if (tree== nullptr)return;root = new Node(tree->_val);copy2(root->left,tree->left);copy2(root->right,tree->right);}BinaryTree(const BinaryTree<T>&tree){//copy(tree._root);copy2(_root, tree._root);}

copy2函數傳指針引用我是受下面一個成員函數實現的啟發,這個傳引用一定要好好體會,方便理解后面的函數,非常巧妙。

(3)? 賦值

? ?用的是現代寫法,比如t1,t2是兩個BinaryTree對象,t1=t2就會調用下面的賦值函數,可是我的參數不是引用,那按規定自定義類型傳值傳參要用拷貝構造(我在類的成員函數博客曾提及),t2傳參給tree就要調用拷貝構造,那tree就是一個新拷貝的對象,我們就可以用swap直接交換tree和t1的根節點指針,并且tree就是一個局部對象,函數調用完后會自動調用析構函數,省去了我們寫析構t1樹和創建新樹的功夫,都給編譯器做了。(string模擬的賦值也用到了現代寫法)

?void swap(BinaryTree<T>& tree){std::swap(_root, tree._root);}void operator=(BinaryTree<T> tree){swap(tree);}?

(4) find函數

搜索樹怎么能缺少搜索功能呢


這個是find函數的子函數,子函數原因和上面同理,都是一開始傳參外部無法獲取_root,因為遞歸遍歷代碼量少,所以我實現的是遞歸版本bool _findR(Node*root,const T& val){if (root == nullptr)return false;if (val > root->_val)//val比當前_val大,去右樹找{return _findR(root->right, val);}else if (val < root->_val)//val比當前_val小,去左樹找{return _findR(root->left, val);}else  {return true;//val和當前_val相等,返回true}}//下面這個是外部調用的find函數,只需要傳要查找的值即可  bool find(const T& val){return _findR(_root, val);}

我之前在寫find函數時,我還想著返回false是不是應該當左樹和右樹都沒找到才返回false,好一會才醒悟,我們之所以去左樹找,就是因為要找的val比根節點的值小,那右樹更不會有了,所以左樹找到nullptr就應該返回false,同理右樹找到nullptr也返回false。

(5) insert函數

因為要遞歸去找合適的位置插入,所以同樣要寫一個子函數  void _insertR(Node*& root, const T&val){if (root == nullptr)當找到空節點,就可以插入了,此時才是引用起作用的時候{root = new Node(val);  直接就可以修改了,因為root是上一個節點的left或者 right指針的引用。return;}Node* cur = root;if (val > cur->_val)//val大于當前根,插入到右樹去{_insertR(cur->right,val);}else if (val < cur->_val)//val小于當前根,插入到左樹去{_insertR(cur->left, val);}else{return;}}void insert(const T& val){_insertR(_root, val);}

(6) 中序遍歷

		void _Inorder(Node* root)//中序遞歸遍歷{if (root == nullptr)return;_Inorder(root->left);  一直往左子樹遞歸,直到左子樹為空,算訪問完,可以訪問根。cout << root->_val << " "; _Inorder(root->right); 然后去右子樹訪問,同樣分為左子樹,根,右子樹}void Inorder(){_Inorder(_root);//調用子函數,外部無法獲取私有成員_root}

(7) erase函數

     bool _Rerase(Node*&root,const T&val){if (root == nullptr)return false;Node* cur = root;if (val > root->_val)//用_val找節點{return _Rerase(root->right, val);}else if (val < root->_val){return _Rerase(root->left, val);}else//找到了{//該節點只有一個或者無子節點if (root->left == nullptr)  由于root是上一節點左指針或者右指針的別名,所以可以直接拿root->right來賦值給root,否則還要 判斷root->right是鏈接在上一節點的left指針還是 right指針。{root = root->right;}else if (root->right == nullptr){root = root->left;}else{刪除有兩個子節點的節點-替換法找該節點左子樹中最大的,或者右子樹中最小的來替換刪除節點Node* leftMax = root->left;while (leftMax->right){leftMax = leftMax->right;}std::swap(leftMax->_val, root->_val);return _Rerase(root->left, val);調用_Rerase去刪除leftMax節點,這里必須要傳root->left,去左子樹刪除值為val的節點,不能傳root例如我們交換leftMax的7和root的8值,如果傳root,8的值比7大,就會去右樹刪,就找不到leftMax節點了,但是root的左子樹仍然滿足二叉搜索的特性,就可以找到leftMax節點并刪除。  }delete cur;  該處統一釋放刪除節點,并返回truereturn true;}}bool erase(const T& val)//刪除某個節點{return _Rerase(_root, val);}

三 kv結構實現

? ?本來我以為kv結構是要將K結構的樹大改,當我實現后才發現,賦值可以直接照搬,,find,insert,erase中大量的if判斷都是用關鍵碼判斷,根本不需要改動,中序遍歷也就多打印一個數據,還有insert和拷貝構造函數要在new一個節點的時候多傳一個參數。

接下來就看看一些比較重要的改動,在這里_key存關鍵碼,而我上面二叉樹K結構中是_val存的關鍵碼,不要搞混了。

1 樹的節點類

     template<class T,class K>struct TreeNode{TreeNode(const T& val,const K&key):_val(val),_key(key){;}             類內可不加模板參數,也就是說TreeNode等價于TreeNode<T,k>TreeNode* left = nullptr; TreeNode* right = nullptr;T _val;//存與關鍵碼對應的數據K _key;//_key存關鍵碼};

2 BinaryTree類

有了先前K結構樹的基礎,這里構造和析構函數我們就很好理解。

(1)構造和析構函數

template<class T, class K>class BinaryTree{public:typedef TreeNode<T,K> Node;BinaryTree(Node* node = nullptr)  默認構造無改變:_root(node){;}void _DestroyR(Node*&root)  遞歸釋放節點,采用后序遍歷的方式delete{if (root == nullptr)return;_DestroyR(root->left);_DestroyR(root->right);delete root;root = nullptr;}~BinaryTree(){_DestroyR(_root);}private:Node* _root = nullptr;   成員變量是不變的,畢竟kv結構的樹用根節點同樣可以管理};
}

(3)erase函數

  bool _Rerase(Node*& root, const T& key){if (root == nullptr)return false;Node* cur = root; //記錄節點,方便后面deleteif (key > root->_key){return _Rerase(root->right,key);}else if (key <root->_key){return _Rerase(root->left, key);}else//找到了{//該節點只有一個或者無子節點if (root->left == nullptr){root = root->right;}else if (root->right == nullptr){root = root->left;}else{Node* leftMax = root->left;while (leftMax->right){leftMax = leftMax->right;}std::swap(leftMax->_val, root->_val); 在交換時要多交換一個值std::swap(leftMax->_key, root->_key);return _Rerase(root->left, key); 并且還是用key值去找leftMax刪}  刪除完leftMax后就直接return了,就不會重復刪除。delete cur;return true;}}bool erase(const T& val)//刪除某個節點{return _Rerase(_root, val);}

二叉搜索樹中最復雜的就是erase函數,大家在此處一定要畫圖理解。

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

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

相關文章

Neo4j之DELETE基礎

在 Neo4j 中&#xff0c;DELETE 語句用于刪除節點、關系或節點屬性。它允許從圖數據庫中移除不再需要的數據。 1】刪除節點及其關系&#xff1a; MATCH (p:Person {name: Alice}) DETACH DELETE p;這個查詢會找到具有 "Person" 標簽且屬性 "name" 為 &qu…

人工智能原理概述 - ChatGPT 背后的故事

大家好&#xff0c;我是比特桃。如果說 2023 年最火的事情是什么&#xff0c;毫無疑問就是由 ChatGPT 所引領的AI浪潮。今年無論是平日的各種媒體、工作中接觸到的項目還是生活中大家討論的熱點&#xff0c;都離不開AI。其實對于互聯網行業來說&#xff0c;自從深度學習出來后就…

進入現代云技術的世界-APIGateway、ServiceMesh、OpenStack、異步化框架、云原生框架、命令式API與聲明式API

目錄 APIGateway Service Mesh OpenStack 異步化框架 云原生框架 命令式API與聲明式API APIGateway API網關&#xff08;API Gateway&#xff09;是一個服務器——充當了客戶端和內部服務之間的中間層。API網關負責處理API請求&#xff0c;將客戶端的請求路由到相應的后端…

StringJoiner

1、為什么要學習StringJoiner&#xff1f; 2、StringJoiner概述 StringJoiner跟StringBuilder一樣&#xff0c;也可以看成一個容器&#xff0c;創建之后里面的內容是可變的。 2.1、作用 提高字符串的操作效率&#xff0c;而且代碼編寫特別簡潔&#xff0c;但是目前市場上很少有…

銀行家算法

1.設計目的與要求 1.1設計目的 了解銀行家算法中使用的數據結構和求安全序列算法&#xff0c;并進一步加深對避免死鎖算法及其實現過程的理解。 1.2設計要求 通過編寫和調試一個系統動態分配資源的簡單模擬程序&#xff0c;觀察死鎖產生的條件&#xff0c;并采用適當的算法&a…

2023.8.7論文閱讀

文章目錄 CMUNeXt: An Efficient Medical Image Segmentation Network based on Large Kernel and Skip Fusion摘要本文方法實驗結果 Boundary Difference Over Union Loss For Medical Image Segmentation&#xff08;損失函數&#xff09;摘要本文方法實驗結果 CMUNeXt: An E…

回歸預測 | MATLAB實現基于PSO-LSSVM-Adaboost粒子群算法優化最小二乘支持向量機結合AdaBoost多輸入單輸出回歸預測

回歸預測 | MATLAB實現基于PSO-LSSVM-Adaboost粒子群算法優化最小二乘支持向量機結合AdaBoost多輸入單輸出回歸預測 目錄 回歸預測 | MATLAB實現基于PSO-LSSVM-Adaboost粒子群算法優化最小二乘支持向量機結合AdaBoost多輸入單輸出回歸預測預測效果基本介紹模型描述程序設計參考…

【基礎操作】Linux打開terminal,Anaconda默認進入的虛擬環境(python版本)設置(自行指定)

為了免除每次打開terminal都要輸入 conda activate … 的麻煩&#xff0c;可以這么設置。 1. 打開terminal&#xff0c;然后輸入命令 vim ~/.bashrc2. 然后在文件末尾添加 conda activate your_envs # your_envs是你的虛擬環境名稱3. 保存退出&#xff0c;重新打開就成功啦…

navicat連接postgresql報錯

navicat連接postgresql報錯 navicat連接postgresql報錯 現象 有小伙伴告訴我 安裝了新的postgresql 使用navicat連接&#xff0c;報錯 ERROR: column "datlastsysoid" does not existLINE 1: SELECT DISTINCT datlastsysoid FROM pg database column “datlastsy…

Go 語言類型轉換的陷阱

1 介紹 Go 語言作為強類型語言&#xff0c;在使用 Golang 開發項目時&#xff0c;經常會遇到類型轉換的場景&#xff0c;整型之間可以直接轉換&#xff0c;字節切片和字符串之間也可以直接轉換。 但是&#xff0c;如果整型和字符串之間做類型轉換&#xff0c;則需要使用 str…

.netcore grpc客戶端流方法詳解

一、客戶端流式處理概述 客戶端流式處理方法在該方法沒有接收消息的情況下啟動。 requestStream 參數用于從客戶端讀取消息。 返回響應消息時&#xff0c;客戶端流式處理調用完成。客戶端可以發送多個消息流到服務端&#xff0c;當所有客戶端消息流發送結束&#xff0c;調用請…

SpringBoot案例-部門管理-修改

目錄 前言 查看頁面原型&#xff0c;明確需求 頁面原型 需求 閱讀接口文件 思路分析 功能接口開發 控制層&#xff08;Controller類&#xff09; 業務層&#xff08;Service類&#xff09; 業務類 業務實現類 持久層&#xff08;Mapper類&#xff09; 接口測試 前…

Day 41

Day 41 343. 整數拆分 一個是j * dp[i - j]&#xff0c;相當于是拆分(i - j)&#xff0c;對這個拆分不理解的話&#xff0c;可以回想dp數組的定義。 dp[i] max({dp[i], (i - j) * j, dp[i - j] * j}); class Solution:def integerBreak(self, n: int) -> int:dp [0] *…

離線環境conda虛擬環境備份遷移--conda pack問題

1.第一步&#xff1a;創建虛擬環境 conda create -n pyenv --clone base 或者 conda create -n pyenv python3.8.5 --offline 命令執行結束&#xff0c;在路徑/xxxx/anaconda/envs 下看到pyenv 或者 conda info --envs 查看羅列虛擬環境 2.第二步&#xff1a;打包環境 conda …

ROS2 學習(一)介紹,環境搭建,以及個人安裝的一些建議

ROS2 學習 學習自b站課程&#xff1a;https://www.bilibili.com/video/BV16B4y1Q7jQ?p1 &#xff08;up主&#xff1a;古月居GYH&#xff09; ROS 介紹 Robot OS&#xff0c;為機器人開發提供了相對完善的 middleware&#xff0c;工具&#xff0c;軟件等。 ROS1 對嵌入式設…

計算機網絡(7) --- UDP協議和TCP協議

計算機網絡&#xff08;6&#xff09; --- https協議_哈里沃克的博客-CSDN博客https協議https://blog.csdn.net/m0_63488627/article/details/132112683?spm1001.2014.3001.5501 目錄 1.補充知識 1.PORT端口號 2.端口號范圍劃分 3.知名端口號 2.UDP協議 1.UDP報頭 2.U…

容器逃逸Docker cp(CVE-2019-14271)漏洞復現與分析

目錄 安裝 原理 EXP 參考 安裝 metarget安裝有點問題&#xff0c;所以我們直接指定安裝 可以用下面命令 查看包 apt-cache madison docker-ce 安裝 apt-get install -y docker-ce5:19.03.0~3-0~ubuntu-bionic 原理 EXP metarget/writeups_cnv/docker-cve-2019-14271 at …

Insert 1, Insert 2, Insert 3, ... 2023牛客暑期多校訓練營8 H

登錄—專業IT筆試面試備考平臺_牛客網 題目大意&#xff1a;給出一個長度為n的數組a&#xff0c;問有多少子串滿足其可以用多個排列穿插構成 1<n<1e6 思路&#xff1a;因為每個排列的起點都是1&#xff0c;所以我們大致的策略就是對于每一個1&#xff0c;記錄它往右最…

BGP小綜合

實驗題目如下&#xff1a; 實驗拓撲如下&#xff1a; 實驗要求如下&#xff1a; 【1】R2-7每臺路由器均存在一個環回接口用于建立鄰居&#xff0c;同時還存在一個環回來代表連接用戶的 接口;最終這些連接用戶的接口網絡需要可以和R1/8的環回通訊 【2】AS2網段地址1…

基于smardaten無代碼開發智能巡檢系統,讓無人機飛得更準

目錄 引言需求背景搭建思路開發過程&#xff08;1&#xff09;無人機設備數據接入&#xff08;2&#xff09;無人機巡檢任務管理&#xff08;3&#xff09;無人機三維防控監視&#xff08;4&#xff09;運防一體化大屏設計&#xff08;5&#xff09;異常告警管理&#xff08;6&…