C/C++ 高階數據結構 —— 二叉搜索樹(二叉排序樹)

在這里插入圖片描述


在這里插入圖片描述


? 🎁個人主頁:工藤新一1

? 🔍系列專欄:C++面向對象(類和對象篇)

? 🌟心中的天空之城,終會照亮我前方的路

? 🎉歡迎大家點贊👍評論📝收藏?文章


文章目錄

  • 二叉查找樹 OR 二叉排序樹(BST)
    • 一、核心概念:一句話定義
    • 二、二叉查找樹的刪除
      • 2.1刪除葉子節點
      • 2.2刪除度為1的節點
      • 2.3刪除分支節點
        • 2.3.1普通分支節點
        • 2.3.2根節點
    • 三、核心邏輯實現
      • 3.1二叉搜索樹查找邏輯
      • 3.2二叉搜索樹插入邏輯
      • 3.3二叉搜索樹刪除邏輯
      • 3.4丟棄遞歸返回值問題

二叉查找樹 OR 二叉排序樹(BST)

二叉查找樹和二叉排序樹是完全同一個概念,指的是同一種數據結構,其也是面試常客

二叉樹具體的呈現:

在這里插入圖片描述

刪除與插入的處理方式都需以查找為基礎

標準的二叉搜索樹(BST)定義中:

  • ? 通常不允許重復元素
  • ? 每個節點的鍵值(key)都是唯一的
  • ? 遵循嚴格的大小關系:左子樹 < 根節點 < 右子樹

一、核心概念:一句話定義

在這里插入圖片描述


? 二叉查找樹是一種特殊的二叉樹,他給它的節點立下了嚴格的“規矩”:對于樹中的任意一節點,其左子樹中所有 節點值都必須小于根節點,其右子樹中所有 節點值都必須大于其根節點

這個“規矩”就是二叉查找樹的靈魂,它的一切神奇特性都源于此


在這里插入圖片描述


二、二叉查找樹的刪除

重點問題:如何填補空缺(節點),才能保持整個二叉樹的結構不發生變化,且不違反其定義?

在這里插入圖片描述

在這里插入圖片描述


2.1刪除葉子節點

刪除node3:

在這里插入圖片描述


2.2刪除度為1的節點

刪除node6:

在這里插入圖片描述

我們觀察node6只有左子樹,那么可以直接將 node6->left(node6的前驅節點:node5)放入node6所在的位置


在這里插入圖片描述

因此,刪除度為1的節點,我們可以直接將其 左 or 右子樹,放在(占用)當前節點所處的位置上即可(其余節點不需調整)


2.3刪除分支節點


2.3.1普通分支節點

刪除node7:

在這里插入圖片描述

我們可以參考刪除度為1節點的邏輯:將 node7的 前驅/后繼節點 占用到node7的位置上


在這里插入圖片描述


將 node7的前驅節點(node->left)node6放置node7 所處位置上

在這里插入圖片描述


2.3.2根節點

O(logN)[查找刪除根節點] * O(1)[刪除] * O(logN)[查找替換根節點的根節點的后繼/前驅節點] * O(1)[刪除]

在這里插入圖片描述


在這里插入圖片描述


最終形成的邏輯結果:

在這里插入圖片描述


問題:既然 node14 的后繼節點是 node15,那么為什么不是:

在這里插入圖片描述


這是因為,我們已知 node14 只有右子樹(即node14度為1),那么我們就可以直接利用 “節點度為1” 的情況的處理方式來進行節點的替換。正常對于二叉樹的樹形結構不清楚時,一定是要判斷對應刪除的節點的前驅后繼節點(處理方式:遞歸查找),進行對應刪除替換邏輯


? 刪除度為2的節點,其實是對整個樹的遞歸式的刪除(將對應節點的前驅/后繼指針刪除后再替換至當前刪除位置,并且可能會產生二次替換:將當前節點的前驅/后繼節點的前驅/后繼替換到當前前驅/后繼節點的位置上;第三次替換:…)


但實際上,搜索時間復雜度最差的情況為:O(N)

從根節點–>最終的葉子節點 == 樹的高度

樹形結構中樹的高度:logN

線性樹形結構中樹的高度:N(即整個樹的節點個數)

在這里插入圖片描述


二叉樹

  • 邏輯結構上:樹形結構
  • 存儲結構上:線性的單鏈表結構,因此就有了O(N)的時間復雜度

三、核心邏輯實現

二叉搜索樹(或:二叉排序樹),其最主要的特點:中序遍歷會得到一個遞增序列,無論是 查找/排序 都可以非常高效的實現。在實際生產過程中 二叉排序樹 是所運用到的最主要的樹形結構


創建搜索二叉樹:

// 定義樹節點存儲的數據類型
typedef int ElemType;// 定義樹的節點類型
typedef struct BinarySearchTree
{ElemType data;struct BinarySearchTree* left, * right;// 構造函數 - 初始化字段BinarySearchTree(int val) :data(val), left(nullptr), right(nullptr) { }
} TreeNode;

3.1二叉搜索樹查找邏輯

  • 1.對比待查找關鍵字值與當前節點的數據域

  • 2.關鍵字值 < 數據域:向左子樹遞歸查找

  • 3.關鍵字值 > 數據域:向右子樹遞歸查找

  • 復雜度分析:
    空間復雜度:O(1) - 無需額外定義其他空間

    ? 時間復雜度(樹的所有動作都是基于查找來執行):查找次數與樹的高度(層次)等價 - O(logN)


二叉樹中序遍歷(遞歸)– 算法分離

時間復雜度:O(N)

  • 遍歷函數:
void InOrderByRec(TreeNode* node)
{if (!node) return;InOrderByRec(node->left);cout << node->data << "->";InOrderByRec(node->right);
}

  • 查找函數:
TreeNode* SearchByRec(TreeNode* tree, int key)
{TreeNode* cursor = tree;if (!cursor) return nullptr;if (key == cursor->data) return cursor;if(key < cursor->data)return SearchByRec(cursor->left, key); // 只在左子樹搜索if(key > cursor->data)return SearchByRec(cursor->right, key); // 只在右子樹搜索
}

簡潔代碼(三目表達式):

C++return (key < cursor->data) ? SearchByRec(cursor->left, key) : SearchByRec(cursor->right, key); 

二叉樹中序遍歷(循環):

時間復雜度:O(logN)

TreeNode* SearchByFor(TreeNode* tree, int key)
{TreeNode* cursor = tree;while (cursor){if (key == cursor->data) return cursor;if (key < cursor->data) cursor = cursor->left;if (key > cursor->data) cursor = cursor->right;}return nullptr;
}

3.2二叉搜索樹插入邏輯

  • 1.查找插入位置
  • 2.新建節點,執行插入動作
  • 3.返回操作標志

迭代邏輯:

bool Insert(TreeNode* tree, int key)
{if (!tree){tree = new TreeNode(key);return true;}// 1.查找插入的位置TreeNode* pos = tree, * parent = nullptr;// while循環結束意味著整個樹的高度查找結束while (pos){// 迭代更新 parent 成為 pos 父節點parent = pos;if (key == pos->data) return false;if (key < pos->data) pos = pos->left;if (key > pos->data) pos = pos->right;}// 2.新建節點,執行插入動作if (key < parent->data) parent->left = new TreeNode(key);if (key > parent->data) parent->right = new TreeNode(key);return true;
}

🚫🚫🚫嚴重錯誤遞歸代碼:

在這里插入圖片描述


在這里插入圖片描述


現在的疑問希望未來可以解答:

為什么需要返回遞歸結果?并且 A如何使最后那一節點指向 TreeNode(key)?


在這里插入圖片描述


🚫🚫🚫我懂了!!!!為什么我的代碼出現了巨大錯誤!!

在這里插入圖片描述


在這里插入圖片描述


3.3二叉搜索樹刪除邏輯

指定刪除二叉搜索樹中的節點:

  • 1.查找指定關鍵字值的節點,及其父節點

  • 2.檢查待刪除節點是否符合條件:

    • 缺少左子樹,用右子樹補位;反之;(左右子樹不共存)

    • 缺少左右子樹(即葉子節點)直接刪除(斷開與父節點的千絲萬縷)

    • 左右子樹都存在,按中序遍歷查找當前節點的補位(替代)節點(前驅/后繼)

      直接前驅:即左子樹的最右節點(中序遍歷節點前后指針規律)

      直接后繼:即右子樹的最左節點

  • 3.遞歸刪除操作:刪除替代節點

    • 刪除當前節點 + 刪除補位節點 + 刪除補位節點的補位節點…
    • 注意1:需保留當前待刪節點(遞歸刪除:優先刪除下層待刪節點(遍歷節點[地推] + 從下至上刪除補位節點[回歸])- 因此再次證明遞歸是一個棧結構)
    • 注意2:遞歸刪除替代節點時,需將父節點作為樹的新的樹指針需保留
  • 4.使用補位節點替換原本的待刪節點

    • 修改/斷開鏈接:父節點/左/右子樹

在這里插入圖片描述


在這里插入圖片描述


bool Delete(TreeNode* tree, int key)
{if (!tree) return false;//1.查找指定關鍵字值的節點,及其對應父節點TreeNode* pos = tree, *child = nullptr, *parent = nullptr;while (pos){if (key == pos->data){child = pos;// 結束最內層循環break; // 貌似不可使用 return; - 結束整個函數運行}// 若根節點為目標節點 parent == nullptr,反之迭代更新 parentparent = child; // 當前節點為父節點,向其子節點查找if (key < pos->data) pos = pos->left;if (key > pos->data) pos = pos->right;}// 開發技巧:對child進行為空判定,因為 key 可能不存在于樹中,child 仍然為 nullptrif (!child) return false;// 2.檢查待刪除節點是否符合條件(進行遞歸刪除):// a.左/右子樹不共存 + 左/右子樹同時不存在(葉子節點)if (!child->left || !child->right){
/*// 補位節點TreeNode* subNode = nullptr;if (!child->left) subNode = child->right;else subNode = child->left;subNode = child->left == nullptr ? child->right : child->left;
*/TreeNode* subNode = child->left == nullptr ? child->right : child->left;// parent 判空(刪除根節點時)if (parent){// 將補位節點掛載到 parent下方// 若child 原本掛載到 parent的左子樹上,就讓補位節點替換到 parent左子樹上(child的位置)if (parent->left == child) parent->left = subNode;if (parent->right == child) parent->right = subNode;}else // 父節點不存在 - 即刪除根節點 - 直接將子樹節點向上提升一個層次{// 直接將下級子樹的根節點作為整個樹的根節點 - 為什么可以這么做?因為左右子樹不共存tree = subNode;}return true;}// b.左右子樹都存在,按中序遍歷查找當前節點的補位(替代)節點(前驅/后繼)TreeNode* replacer = child, * replacer_parent = parent;// 查找直接前驅 - 左子樹的最右節點pos = child->left;// 查找直接后繼 - 右子樹的最左節點:pos = child->right;while (pos){// 補位節點的父節點就是上一次循環的 replacerreplacer_parent = replacer;// 記錄補位節點replacer = pos;pos = pos->right;// pos = pos->left; 直接后繼(直接前驅/后繼無法共存)}// 3.遞歸刪除操作:刪除替代節點// 樹根 - replacer_parent; 待刪除內容 - replacer->dataDelete(replacer_parent, replacer->data);// 4.使用補位節點替換原本的待刪節點if (parent){// parent->left 指向待刪節點if (parent->left == child)// 那么就使其指向補位節點parent->left = replacer;elseparent->right = replacer;}replacer->left = child->left;replacer->right = child->right;// 斷開已刪除節點與子樹的聯系(與父節點的聯系已斷開)child->left = nullptr, child->right = nullptr;
}

分段邏輯剖析:

在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


3.4丟棄遞歸返回值問題

黃金法則:每個遞歸調用都應有對應的return語句來傳遞結果,確保返回值沿著調用鏈正確傳遞


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述
🌟 各位看官好,我是工藤新一1呀~

🌈 愿各位心中所想,終有所致!

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

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

相關文章

stm32F4掛載emmc以及重定義printf

1.Cubemx SDIO USART 使用串口輸出調試信息 FATFS Clock Configuration 防止堆棧溢出 2.Keil5 新建自定義文件夾及文件 將文件夾添加進工程 新建.c與.h文件&#xff0c;保存到自定義的文件夾&#xff0c;并添加到工程中 bsp_emmc.c #include "bsp_emmc.h" #include…

基于AI的大模型在S2B2C商城小程序中的應用與定價策略自我評估

摘要&#xff1a;本文聚焦電商行業&#xff0c;結合開源AI大模型與AI智能名片S2B2C商城小程序的技術特性&#xff0c;提出基于行業數據挖掘與自我評估的定價策略。通過分析行業價格分布與銷量占比&#xff0c;結合商品設計、品牌創意度、商品豐富度及內功等評估指標&#xff0c…

中國移動云電腦一體機-創維LB2004_瑞芯微RK3566_2G+32G_開ADB安裝軟件教程

中國移動云電腦一體機-創維LB2004_瑞芯微RK3566_2G32G_開ADB安裝軟件教程簡介&#xff1a;中國移動云電腦一體機-創維LB2004&#xff0c;顯示器是23.8英寸1920x1080分辨率&#xff0c;安卓盒子配置是瑞芯微RK3566-四核-1.8GHz處理器-2G32G&#xff0c;預裝Android11系統。具體操…

普藍自研AutoTrack-4X導航套件平臺適配高校機器人實操應用

在當前高校機器人工程、人工智能、自動化等專業的教學與科研中&#xff0c;師生們常常面臨一個核心痛點&#xff1a;缺乏一套 “開箱即用、可深研、能落地” 的自主移動導航平臺 —— 要么是純仿真環境脫離實際硬件&#xff0c;要么是硬件零散需大量時間搭建&#xff0c;要么是…

2025年工會證考試題庫及答案

一、單選題1.工會法人資格審查登記機關自收到申請登記表之日起(??)日內對有關申請文件進行審查&#xff0c;對審查合格者&#xff0c;辦理登記手續&#xff0c;發放《工會法人資格證書》及其副本和《工會法人法定代表人證書》。A.二十B.十五C.六十D.三十答案:D 解析:第七條基…

【OpenGL】LearnOpenGL學習筆記17 - Cubemap、Skybox、環境映射(反射、折射)

上接&#xff1a;https://blog.csdn.net/weixin_44506615/article/details/150935025?spm1001.2014.3001.5501 完整代碼&#xff1a;https://gitee.com/Duo1J/learn-open-gl | https://github.com/Duo1J/LearnOpenGL 一、立方體貼圖 (Cubemap) 立方體貼圖就是一個包含了6張2…

第十七章 ESP32S3 SW_PWM 實驗

本章將介紹使用 ESP32-S3 LED 控制器(LEDC)。 LEDC 主要用于控制 LED&#xff0c;也可產生PWM信號用于其他設備的控制。該控制器有 8 路通道&#xff0c;可以產生獨立的波形&#xff0c;驅動 RGB LED 等設備。 LED PWM 控制器可在無需 CPU 干預的情況下自動改變占空比&#xff…

Flink CDC如何保障數據的一致性

Flink CDC如何保障數據的一致性 前言 在大規模流處理中&#xff0c;故障是無可避免的。機器會宕機&#xff0c;網絡會抖動。一個可靠的流處理引擎不僅要能高效地處理數據&#xff0c;更要在遇到這些故障時&#xff0c;保證計算結果的正確性。Apache Flink 正是因其強大的容錯機…

Spring Boot 定時任務入門

1. 概述 在產品的色彩斑斕的黑的需求中&#xff0c;有存在一類需求&#xff0c;是需要去定時執行的&#xff0c;此時就需要使用到定時任務。例如說&#xff0c;每分鐘掃描超時支付的訂單&#xff0c;每小時清理一次日志文件&#xff0c;每天統計前一天的數據并生成報表&#x…

學習:uniapp全棧微信小程序vue3后臺(6)

26.實現描述評分標簽的雙向數據綁定 /pages/wallpaper/picadd Array.prototype.splice() splice() 方法就地移除或者替換已存在的元素和/或添加新的元素。 二次確認 展現 確認標簽 刪除標簽 溫故知新&#xff1a; 標簽&#xff1a; 關閉標簽 27.uni-data-select調用云端分類…

Azure Marketplace 和 Microsoft AppSource的區別

微軟的商業應用生態中&#xff0c;Azure Marketplace 和 Microsoft AppSource 是微軟并行的兩個主要“應用市場”&#xff08;Marketplace&#xff09;&#xff0c;它們共同構成了微軟的“商業市場”&#xff08;Commercial Marketplace&#xff09;計劃&#xff0c;但服務的目…

完整實驗命令解析:從集群搭建到負載均衡配置(2)

一、環境準備與基礎網絡配置1.1 節點角色與網絡規劃節點角色主機名所屬網段IP 地址網關核心功能Web 服務器web110.1.8.0/2410.1.8.1110.1.8.10&#xff08;后期調整為 10.1.8.20&#xff09;部署 Nginx/HTTPD&#xff0c;提供 Web 服務Web 服務器web210.1.8.0/2410.1.8.1210.1.…

uniapp H5禁止微信瀏覽器長按出菜單,只針對圖片

一、問題描述 如圖&#xff1a;uni-image>img,img {pointer-events: none;-webkit-pointer-events: none;-ms-pointer-events: none;-moz-pointer-events: none; }uni-image::before {content: ;position: absolute;top: 0;bottom: 0;left: 0;right: 0;background: transpa…

【機器學習】 15 Gaussian processes

本章目錄 15 Gaussian processes 515 15.1 Introduction 515 15.2 GPs for regression 516 15.2.1 Predictions using noise-free observations 517 15.2.2 Predictions using noisy observations 518 15.2.3 Effect of the kernel parameters 519 15.2.4 Estimating the kern…

Vue加載速度優化,verder.js和element.js加載速度慢解決方法

1. 使用CDN 這里把常用的vue、vuex、elementui、echarts、axios都引入成cdn的方式 1、在index.html引入CDN 找到public/index.html在上方引入下邊的cdn。 [!NOTE] 引入script的時候&#xff0c;一定要把vue.js放到最上邊&#xff0c;最先引入&#xff0c;不然后邊的js加載會…

49.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--擴展功能--集成網關--Refit跨服務調用

Refit是一個用于.NET平臺的REST庫,它可以將REST API轉換為實時類型安全的接口。通過Refit,我們可以輕松實現微服務之間的跨服務調用,使服務間通信變得更加簡單和類型安全。本文將介紹如何在我們的項目中使用Refit來實現微服務間的通信。 一、什么是Refit Refit是一個強大的REST…

日志ELK、ELFK、EFK

一.ELK架構Elasticsearch Logstash Kibana 數據庫日志處理日志顯示1.logstash的使用&#xff08;1&#xff09;input&#xff1a;輸入&#xff08;2&#xff09;filter&#xff1a;處理&#xff08;3&#xff09;output&#xff1a;輸出2.ELFK架構Filebeat-->Elasticsearc…

【CUDA進階】MMA分析Bank Conflict與Swizzle(下)

目錄前言1. bank conflict 分析2. 通過 padding 解決 bank conflict3. mma 搭配 wmma 實現矩陣乘法計算3.1 代碼實現3.2 補充&#xff1a;stmatrix_sync 函數分析3.3 補充&#xff1a;__shfl_sync 函數詳解4. swizzle 原理講解5. swizzle 實現思路講解結語下載鏈接參考前言 學習…

天氣查詢系統

項目要求 項目知識點 問題與解決 代碼分部 結果展示 項目要求 1.顯示天氣預報系統界面 2.系統可以通過選擇城市配置獲取不同城市天氣信息 3.查看實時的天氣信息 &#xff08;當前溫度、最高溫度、最低溫度、當前濕度、最高濕度、最低濕度、風向、風力、風級等信息&#x…

三重積分的對稱性

文章目錄前言柱面球面前言 規律作息 柱面 太牛了。我完全看不懂。實際上就類似于極坐標系。 球面 看到這么多東西&#xff0c;我真害怕。今天是 8.30 &#xff0c;不管 9.10 有沒有復習完概率的強化&#xff0c;我都一定要開始套卷&#xff0c;還有專業課的復習。?\phi?…