十三,數據結構-樹

定義

樹也是基于節點的數據結構,和鏈表不同的是,樹的節點可以指向多個節點。首先對樹的一些常用術語進行說明:

  • 最上面的節點叫做根節點,根位于樹頂,如圖中的節點A;
  • 和族譜一樣,節點有后代和祖先;節點的后代即起源于該節點的全部節點,祖先即可以派生出該節點的節點;
  • 樹有層級,每一層都是樹的一行,如上圖,樹有三層;
  • 樹的一個屬性是它是否平衡,即如果子樹中的節點數量相同,則這棵樹是平衡的,上圖中的樹就是不平衡的;

二叉查找樹

二叉查找樹也是樹的一種,其特征如下:

  • 每個節點最多有一個左子節點和右子節點
  • 一個節點的左子樹中的值都小于節點本身,同時右子樹中的值都大于節點本身;

實現

二叉樹實現如下:

#include <iostream>
#include <memory>// 二叉樹節點類
template <typename T>
class TreeNode {
public:T data;std::shared_ptr<TreeNode<T>> left;std::shared_ptr<TreeNode<T>> right;TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};

二叉樹查找

二叉樹查找步驟如下:

  1. 以根節點為當前節點;
  2. 讀取當前節點數值;
  3. 如果當前數值為要查找的數值,則停止查找并返回;
  4. 如果當前值大于要查找的數值,則在其左子樹中繼續查找;
  5. 如果當前值小于要查找的數值,則在右子樹中繼續查找;
  6. 重復1-5步,直到找到對應數值,或者到達了樹的底端,即數值不在該樹中;

注意觀察可以發現,每次查找都會排除大約一半的節點,因此可以推測二叉查找樹的查找效率為O(log_{2}N),之所以說大約為這個效率,是因為只有二叉查找樹的每一行節點都填滿(即是一棵平衡二叉樹),則上述特性才成立即:如果一棵平衡的二叉樹中有N個節點,則就會有大約logN層。代碼實現如下:

bool searchRecursive(std::shared_ptr<TreeNode<T>> node, T value) const {if (!node) {return false;}if (value == node->data) {return true;} else if (value < node->data) {return searchRecursive(node->left, value);} else {return searchRecursive(node->right, value);}}

二叉樹插入

二叉樹的插入是基于二叉樹的查找,即找到要插入的位置后插入(方法同上述中的二叉樹查找),其效率為O(logN)+1,代碼實現如下:

    // 遞歸插入節點std::shared_ptr<TreeNode<T>> insertRecursive(std::shared_ptr<TreeNode<T>> node, T value) {if (!node) {return std::make_shared<TreeNode<T>>(value);}if (value < node->data) {node->left = insertRecursive(node->left, value);} else if (value > node->data) {node->right = insertRecursive(node->right, value);}return node;}

二叉樹刪除

二叉樹查找樹的刪除是最復雜的部分,其步驟如下:

  • 如果要刪除的節點沒有子節點,那么就可以直接刪除該節點;
  • 如果要刪除的節點有一個子節點,那么就在刪除該節點的同時把子節點插到該節點的位置;
  • 如果要刪除的節點有兩個子節點,則需要把要刪除的節點替換為其后繼節點,后繼節點即大于被刪除節點的所有子節點中最小的那個;
  • 如果需要尋找后繼節點,則需要先移動到被刪除節點的右子節點,然后沿左節點的鏈接移動到左子結點,直到找不到任何左子結點為止,最下的這個值即后繼節點;
  • 如果后繼節點有一個右子節點,則在將后繼節點放置到被刪除節點的位置之后,把這個右子節點變成后繼節點曾經的父節點的左子結點。

刪除的效率和插入以及查找類似,均為O(logN),代碼如下:

    // 查找最小節點std::shared_ptr<TreeNode<T>> findMin(std::shared_ptr<TreeNode<T>> node) const {while (node && node->left) {node = node->left;}return node;}// 遞歸刪除節點std::shared_ptr<TreeNode<T>> deleteRecursive(std::shared_ptr<TreeNode<T>> node, T value) {if (!node) {return nullptr;}if (value < node->data) {node->left = deleteRecursive(node->left, value);} else if (value > node->data) {node->right = deleteRecursive(node->right, value);} else {// 找到要刪除的節點if (!node->left) {return node->right;} else if (!node->right) {return node->left;}// 有兩個子節點的情況:找到右子樹的最小節點替換當前節點std::shared_ptr<TreeNode<T>> temp = findMin(node->right);node->data = temp->data;node->right = deleteRecursive(node->right, temp->data);}return node;}

遍歷-前、中、后

二叉查詢樹的遍歷,即訪問所有節點,分類為三種:前序、中序、后序。其實這里的前、中、后針對的是調用遍歷函數時的當前節點,前的意思就是先訪問當前節點,再訪問左、右子節點;中的意思就是先訪問左子節點,再訪問當前節點,最后訪問右子節點;后的意思是先訪問左、右子節點,最后訪問本節點,其實現如下:

    // 中序遍歷(遞歸)void inorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;inorderRecursive(node->left);std::cout << node->data << " ";inorderRecursive(node->right);}// 前序遍歷(遞歸)void preorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;std::cout << node->data << " ";preorderRecursive(node->left);preorderRecursive(node->right);}// 后序遍歷(遞歸)void postorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;postorderRecursive(node->left);postorderRecursive(node->right);std::cout << node->data << " ";}

以上就是本節關于二叉查找樹的全部內容,樹還有很多形式,這里挑選比較常用、簡單的樹形結構來進行講解,不過今后有了AI,這些數據結構和算法的實現將會比較簡單,程序員更多地是理解原理以及優化。全部代碼整理如下:

#include <iostream>
#include <memory>// 二叉樹節點類
template <typename T>
class TreeNode {
public:T data;std::shared_ptr<TreeNode<T>> left;std::shared_ptr<TreeNode<T>> right;TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};// 二叉查找樹類
template <typename T>
class BinarySearchTree {
private:std::shared_ptr<TreeNode<T>> root;// 遞歸插入節點std::shared_ptr<TreeNode<T>> insertRecursive(std::shared_ptr<TreeNode<T>> node, T value) {if (!node) {return std::make_shared<TreeNode<T>>(value);}if (value < node->data) {node->left = insertRecursive(node->left, value);} else if (value > node->data) {node->right = insertRecursive(node->right, value);}return node;}// 遞歸查找節點bool searchRecursive(std::shared_ptr<TreeNode<T>> node, T value) const {if (!node) {return false;}if (value == node->data) {return true;} else if (value < node->data) {return searchRecursive(node->left, value);} else {return searchRecursive(node->right, value);}}// 查找最小節點std::shared_ptr<TreeNode<T>> findMin(std::shared_ptr<TreeNode<T>> node) const {while (node && node->left) {node = node->left;}return node;}// 遞歸刪除節點std::shared_ptr<TreeNode<T>> deleteRecursive(std::shared_ptr<TreeNode<T>> node, T value) {if (!node) {return nullptr;}if (value < node->data) {node->left = deleteRecursive(node->left, value);} else if (value > node->data) {node->right = deleteRecursive(node->right, value);} else {// 找到要刪除的節點if (!node->left) {return node->right;} else if (!node->right) {return node->left;}// 有兩個子節點的情況:找到右子樹的最小節點替換當前節點std::shared_ptr<TreeNode<T>> temp = findMin(node->right);node->data = temp->data;node->right = deleteRecursive(node->right, temp->data);}return node;}// 中序遍歷(遞歸)void inorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;inorderRecursive(node->left);std::cout << node->data << " ";inorderRecursive(node->right);}// 前序遍歷(遞歸)void preorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;std::cout << node->data << " ";preorderRecursive(node->left);preorderRecursive(node->right);}// 后序遍歷(遞歸)void postorderRecursive(std::shared_ptr<TreeNode<T>> node) const {if (!node) return;postorderRecursive(node->left);postorderRecursive(node->right);std::cout << node->data << " ";}public:BinarySearchTree() : root(nullptr) {}// 插入值void insert(T value) {root = insertRecursive(root, value);}// 查找值bool search(T value) const {return searchRecursive(root, value);}// 刪除值void remove(T value) {root = deleteRecursive(root, value);}// 中序遍歷void inorder() const {std::cout << "中序遍歷: ";inorderRecursive(root);std::cout << std::endl;}// 前序遍歷void preorder() const {std::cout << "前序遍歷: ";preorderRecursive(root);std::cout << std::endl;}// 后序遍歷void postorder() const {std::cout << "后序遍歷: ";postorderRecursive(root);std::cout << std::endl;}// 判斷樹是否為空bool isEmpty() const {return root == nullptr;}
};// 示例用法
int main() {BinarySearchTree<int> bst;// 插入一些值bst.insert(50);bst.insert(30);bst.insert(20);bst.insert(40);bst.insert(70);bst.insert(60);bst.insert(80);// 遍歷二叉樹bst.inorder();bst.preorder();bst.postorder();// 搜索值std::cout << "搜索 40: " << (bst.search(40) ? "找到" : "未找到") << std::endl;std::cout << "搜索 90: " << (bst.search(90) ? "找到" : "未找到") << std::endl;// 刪除值std::cout << "\n刪除 20\n";bst.remove(20);bst.inorder();std::cout << "刪除 30\n";bst.remove(30);bst.inorder();std::cout << "刪除 50\n";bst.remove(50);bst.inorder();return 0;
}

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

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

相關文章

JVM-默背版

1.JVM對sychronized的優化&#xff1a;鎖膨脹、鎖消除、鎖粗化、自適應自旋鎖 &#xff08;1&#xff09;鎖膨脹&#xff1a;從無鎖、偏向鎖、輕量級鎖、重量級鎖的過程叫做鎖膨脹。在JDK1.6以前&#xff0c;sychronized是由重量級鎖實現的&#xff0c;加鎖和解鎖的過程需要從用…

Mac M 系列芯片 YOLOv8 部署教程(CPU/Metal 后端一鍵安裝)

在 Mac M 系列芯片&#xff08;Apple Silicon/ARM 架構&#xff09;上部署 YOLOv8&#xff0c;有一些注意事項&#xff1a;PyTorch 需要安裝 ARM 原生版本&#xff0c;推理可利用 Metal 后端加速 CPU。本文教你一步步完成環境配置、模型下載、依賴安裝和驗證推理。1?? 環境準…

Python爬蟲實戰:研究Units模塊,構建氣象數據采集和分析系統

1. 引言 1.1 研究背景 隨著信息技術的飛速發展,互聯網已成為全球最大的信息庫,涵蓋氣象、金融、醫療、農業等多個領域的海量數據。這些數據蘊含著巨大的潛在價值,如何有效獲取并深入分析這些數據成為當下研究的熱點。Python 作為一種功能強大的編程語言,憑借其豐富的庫資…

網頁設計模板 HTML源碼網站模板下載

互聯網已成為現代社會不可或缺的一部分&#xff0c;網站則是連接線上與線下世界的橋梁。無論是用于展示個人作品集、推廣商業產品還是提供公共服務信息&#xff0c;一個設計精良且功能完善的網站都能發揮巨大作用。然而&#xff0c;傳統的手工編碼方式不僅耗時費力&#xff0c;…

Flink KeyedProcessFunction為什么能為每個key定義State和Timer?

問題描述 一個常見的開窗邏輯&#xff08;12H 或者 500條&#xff09;&#xff1a; import org.apache.flink.api.common.state.ValueState; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.common.typeinfo.Types; import or…

【C++】模版初階---函數模版、類模版

&#x1f31f;個人主頁&#xff1a;第七序章 &#x1f308;專欄系列&#xff1a;C&#xff0b;&#xff0b; 目錄 ??前言&#xff1a; &#x1f308;1.泛型編程&#xff1a; &#x1f308;2.函數模板 &#x1f36d;2.1函數模板概念 &#x1f36d;2.2函數模板格式 &am…

查找算法(Java)

目錄 一.定義 二.分類 三.線性查找 原理&#xff1a; 思路分析 代碼實現 例題實踐 1.兩數之和 方法一&#xff1a;暴力窮舉法 思路分析 代碼實現 方法二&#xff1a;創建哈希表 思路分析 代碼實現 2.移動零 思路分析 代碼實現 四.二分查找 原理&#xff1a; …

計算機網絡--四層模型,IP地址和MAC地址

四層模型&#xff1a;分別是應用層&#xff0c;傳輸層&#xff0c;網絡層和鏈路層。應用層&#xff1a;提供了應用程序之間相互通信的接口&#xff0c;允許用戶訪問網絡服務。這一層定義了應用程序如何與底層網絡進行交互。例如HTTP協議。傳輸層&#xff1a;它處理數據的分段、…

解析、創建Excel文件的開源庫OpenXLSX介紹

OpenXLSX是一個C庫&#xff0c;用于讀取、寫入、創建和修改.xlsx格式的Microsoft Excel文件&#xff0c;源碼地址&#xff1a;https://github.com/troldal/OpenXLSX &#xff0c;License為BSD-3-Clause&#xff0c;可在Windows、Linux、MaCOS平臺上使用。最新發布版本為v0.3.2&…

【C++】C++11 篇二

【C】C11 篇二前言移動構造函數移動賦值運算符重載類成員變量初始化 &#xff08;缺省值出自C11強制生成默認函數的關鍵字default:禁止生成默認函數的關鍵字delete:繼承和多態中的final與override關鍵字&#xff08;出自C11可變參數模板遞歸函數方式展開參數包逗號表達式展開參…

構建Python環境的幾種工具

本文主要介紹如何構建Python環境來處理不同的工作。 1.常用的構建Python環境的工具 ①venv(內置模塊):Python 3.3 內置標準庫模塊&#xff0c;無需額外安裝。 ②virtualenv:venv的前身&#xff0c;功能更強大且支持舊版Python。 ③conda:來自 Anaconda 或 Miniconda。不僅能…

c#項目編譯時外部依賴文件的同步問題

很多場景因為資源文件太多或太大無法放到資源里面或者是依賴的dll文件&#xff0c;需要編譯時同步到bin\debug或bin\release下的&#xff0c;這里面要修改工程文件代碼實現。 比如&#xff0c;我把這個項目依賴的dll和附加文件放到ref_dll文件夾里面&#xff0c;希望編譯的時候…

數學建模常用算法-模擬退火算法

一、模擬退火算法模擬退火的靈感來源于物理中的 “退火過程”—— 將金屬加熱到高溫后&#xff0c;緩慢冷卻&#xff0c;金屬原子會在熱能作用下自由運動&#xff0c;逐漸形成能量最低的穩定結構。算法將這一過程抽象為數學模型&#xff1a;“溫度 T”&#xff1a;對應物理中的…

架構很簡單:業務架構圖

緣起業務架構是一個復雜的體系&#xff0c;如何更簡單的表達&#xff0c;并能使用起來呢&#xff1f;所謂&#xff1a;大道至簡。基于此&#xff0c;這篇文章就開始了。業務是一切架構的開始&#xff0c;如果沒有業務&#xff0c;架構又有什么作用呢&#xff1f;所以做架構首先…

【前端埋點】純前端實現 A/B Test

“純前端實現 A/B Test”&#xff0c;意思就是 沒有后端分流、也不依賴流量網關&#xff0c;那么只能靠前端邏輯來做“流量切分”。 &#x1f3af; 目標 80% 的用戶 → A 頁面20% 的用戶 → B 頁面且要保證 同一個用戶每次訪問結果一致&#xff08;否則用戶刷新頁面時 A/B 會跳…

Day22_【機器學習—集成學習(3)—Boosting—Adaboost算法】

Adaptive Boosting(自適應提升)是基于 Boosting思想實現的一種集成學習算法&#xff0c;核心思想是通過逐步提高那些被前一步分類錯誤的樣本的權重來訓練一個強分類器。一、Adaboost算法直線相當于一個弱學習器&#xff0c;正確的數據權重減小&#xff0c;錯誤的數據權重增加二…

C#語言入門詳解(18)傳值、輸出、引用、數組、具名、可選參數、擴展方法

C#語言入門詳解&#xff08;18&#xff09;傳值、輸出、引用、數組、具名、可選參數、擴展方法一、傳值參數1. 值類型2. 引用類型&#xff0c;并且創建對象3. 引用類型&#xff0c;不創建對象二、引用參數1. 值類型2. 引用類型&#xff0c;創建新對象3. 引用類型&#xff0c;不…

模型部署:(二)安卓端部署Yolov5-v6.0目標檢測項目全流程記錄

模型部署&#xff1a;&#xff08;二&#xff09;安卓端部署Yolov5-v6.0目標檢測項目全流程記錄1、下載NCNN項目2、下載基于安卓的yolov5項目3、修改相應的配置文件4、將項目打包成apk包5、部署自己訓練的yolov5模型6、相關資料下載&#xff1a;1、下載NCNN項目 # 鏈接地址 ht…

圖形基礎算法:如何將點與帶曲線邊的多邊形位置關系算法做穩定

簡介判斷點與多邊形位置關系算法是幾何算法中最基礎的算法之一&#xff0c;包括布爾運算在內的非常非常多的算法都會用到它。它的穩定是算法庫穩定的關鍵。下面我們從一個邊都是直線的多邊形開始了解射線法的原理。然后看看引入曲線后會帶來哪些問題&#xff0c;以及在實際應用…

react的filber架構

好問題 &#x1f44d; 你問的是 React Fiber 架構。這是 React 16 引入的核心機制&#xff0c;用來解決 React 在大規模更新時的性能問題。下面我給你從 背景 → Fiber 是什么 → 原理 → 優點 → 流程 來系統講。一、為什么需要 Fiber&#xff1f;在 React 15 及以前&#xff…