數據結構--樹

一、樹的概念

樹是由n(n≥0)個節點組成的有限集合,它滿足以下條件:
?
1.?當n=0時,稱為空樹
2.?當n>0時,有且僅有一個特定的節點稱為根節點(root)
3.?其余節點可分為m(m≥0)個互不相交的有限集合,每個集合本身又是一棵樹,稱為根的子樹(subtree)
?
二、基本術語
?
- 節點(Node): 樹的基本單位,包含數據項和指向其他節點的指針
- 邊(Edge): 連接兩個節點的線
- 根節點(Root): 樹的最頂層節點,沒有父節點
- 父節點(Parent): 一個節點的直接上層節點
- 子節點(Child): 一個節點的直接下層節點
- 葉子節點(Leaf): 沒有子節點的節點
- 內部節點(Internal Node): 至少有一個子節點的節點
- 度(Degree): 一個節點擁有的子節點數目
- 樹的度: 樹中所有節點度的最大值
- 層次(Level): 根節點為第1層,其子節點為第2層,以此類推
- 高度/深度(Height/Depth): 樹中節點的最大層次數
- 兄弟節點(Sibling): 具有相同父節點的節點
- 祖先節點(Ancestor): 從根到該節點路徑上的所有節點
- 后代節點(Descendant): 某節點子樹中的所有節點
?
三、樹的分類
?
1.?二叉樹(Binary Tree): 每個節點最多有兩個子節點
- 滿二叉樹
- 完全二叉樹
- 二叉搜索樹(BST)
- 平衡二叉樹(AVL樹)
- 紅黑樹
2.?多叉樹: 每個節點可以有多個子節點
- B樹
- B+樹
- Trie樹(字典樹)
3.?其他特殊樹結構
- 堆(Heap)
- 哈夫曼樹
- 線段樹
- 樹狀數組
?
四、樹的存儲表示
?
1.?鏈式存儲
- 每個節點包含數據域和指針域
- 指針指向子節點
2.?順序存儲
- 使用數組存儲
- 對于完全二叉樹特別有效
?3.樹的簡單實現

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;// 定義樹節點類模板
template <typename T>
class TreeNode
{
public:T data;  // 節點存儲的數據TreeNode* firstChild;  // 指向該節點的第一個孩子節點TreeNode* nextSibling; // 指向該節點的下一個兄弟節點// 構造函數,初始化節點數據,將孩子和兄弟指針置為空TreeNode(T val) : data(val), firstChild(nullptr), nextSibling(nullptr) {}
};// 定義樹類模板
template <typename T>
class Tree
{
private:TreeNode<T>* root;  // 樹的根節點// 遞歸清空樹的輔助函數void clear(TreeNode<T>* node){if (node == nullptr) return;  // 如果節點為空,直接返回clear(node->firstChild);  // 遞歸清空當前節點的第一個孩子節點及其子樹clear(node->nextSibling); // 遞歸清空當前節點的下一個兄弟節點及其子樹delete node;  // 釋放當前節點的內存}public:// 構造函數,初始化根節點為空Tree() : root(nullptr) {}// 析構函數,調用 clear 函數清空整棵樹~Tree() { clear(root); }// 創建一個新的樹節點,返回新節點的指針TreeNode<T>* createNode(T data){return new TreeNode<T>(data);}// 設置樹的根節點void setRoot(TreeNode<T>* node){root = node;}// 獲取樹的根節點TreeNode<T>* getRoot() const{return root;}// 向指定父節點插入一個孩子節點void insertChild(TreeNode<T>* parent, TreeNode<T>* child){if (parent == nullptr) return;  // 如果父節點為空,直接返回if (parent->firstChild == nullptr){// 如果父節點沒有第一個孩子節點,將該孩子節點設為第一個孩子parent->firstChild = child;}else{// 否則,找到父節點孩子鏈表的末尾,將該孩子節點插入到末尾TreeNode<T>* sibling = parent->firstChild;while (sibling->nextSibling != nullptr){sibling = sibling->nextSibling;}sibling->nextSibling = child;}}// 前序遍歷樹void preOrderTraversal(TreeNode<T>* node){if (node == nullptr) return;  // 如果節點為空,直接返回cout << node->data << " ";  // 訪問當前節點的數據preOrderTraversal(node->firstChild);  // 遞歸前序遍歷當前節點的第一個孩子節點及其子樹preOrderTraversal(node->nextSibling); // 遞歸前序遍歷當前節點的下一個兄弟節點及其子樹}// 后序遍歷樹void postOrderTraversal(TreeNode<T>* node){if (node == nullptr) return;  // 如果節點為空,直接返回postOrderTraversal(node->firstChild);  // 遞歸后序遍歷當前節點的第一個孩子節點及其子樹cout << node->data << " ";  // 訪問當前節點的數據postOrderTraversal(node->nextSibling); // 遞歸后序遍歷當前節點的下一個兄弟節點及其子樹}// 層序遍歷樹void levelOrderTraversal(){if (root == nullptr) return;  // 如果根節點為空,直接返回queue<TreeNode<T>*> q;  // 定義一個隊列用于層序遍歷q.push(root);  // 將根節點入隊while (!q.empty()){TreeNode<T>* current = q.front();  // 獲取隊首節點q.pop();  // 隊首節點出隊cout << current->data << " ";  // 訪問當前節點的數據// 將當前節點的所有孩子節點依次入隊TreeNode<T>* child = current->firstChild;while (child != nullptr){q.push(child);child = child->nextSibling;}}}// 獲取樹的高度int getHeight(TreeNode<T>* node){if (node == nullptr) return 0;  // 如果節點為空,高度為 0int maxHeight = 0;  // 初始化最大子樹高度為 0TreeNode<T>* child = node->firstChild;while (child != nullptr){// 遞歸計算每個子樹的高度,并更新最大子樹高度maxHeight = max(maxHeight, getHeight(child));child = child->nextSibling;}return maxHeight + 1;  // 當前節點的高度為最大子樹高度加 1}// 計算樹中節點的總數int countNodes(TreeNode<T>* node){if (node == nullptr) return 0;  // 如果節點為空,節點數為 0// 當前節點及其子樹的節點總數為當前節點加上其第一個孩子節點及其子樹的節點數,再加上其兄弟節點及其子樹的節點數return 1 + countNodes(node->firstChild) + countNodes(node->nextSibling);}// 在樹中查找值為 value 的節點TreeNode<T>* findNode(TreeNode<T>* node, T value){if (node == nullptr) return nullptr;  // 如果節點為空,返回空指針if (node->data == value) return node;  // 如果當前節點的值等于要查找的值,返回當前節點的指針// 先在當前節點的第一個孩子節點及其子樹中查找TreeNode<T>* found = findNode(node->firstChild, value);if (found != nullptr) return found;// 若未找到,再在當前節點的下一個兄弟節點及其子樹中查找return findNode(node->nextSibling, value);}// 打印樹的結構,level 表示當前節點的層級void printTree(TreeNode<T>* node, int level = 0){if (node == nullptr) return;  // 如果節點為空,直接返回// 根據當前節點的層級打印相應數量的空格for (int i = 0; i < level; ++i){cout << "  ";}cout << node->data << endl;  // 打印當前節點的數據// 遞歸打印當前節點的第一個孩子節點及其子樹,層級加 1printTree(node->firstChild, level + 1);// 遞歸打印當前節點的下一個兄弟節點及其子樹,層級不變printTree(node->nextSibling, level);}
};int main()
{Tree<char> tree;  // 創建一個存儲字符類型數據的樹對象// 創建樹的各個節點TreeNode<char>* A = tree.createNode('A');TreeNode<char>* B = tree.createNode('B');TreeNode<char>* C = tree.createNode('C');TreeNode<char>* D = tree.createNode('D');TreeNode<char>* E = tree.createNode('E');TreeNode<char>* F = tree.createNode('F');TreeNode<char>* G = tree.createNode('G');// 構建樹的結構tree.setRoot(A);  // 設置 A 為根節點tree.insertChild(A, B);  // 將 B 插入為 A 的孩子節點tree.insertChild(A, C);  // 將 C 插入為 A 的孩子節點tree.insertChild(A, D);  // 將 D 插入為 A 的孩子節點tree.insertChild(B, E);  // 將 E 插入為 B 的孩子節點tree.insertChild(B, F);  // 將 F 插入為 B 的孩子節點tree.insertChild(D, G);  // 將 G 插入為 D 的孩子節點// 打印樹的結構cout << "Tree structure:" << endl;tree.printTree(tree.getRoot());cout << endl;// 進行各種遍歷測試cout << "Pre-order traversal: ";tree.preOrderTraversal(tree.getRoot());cout << endl;cout << "Post-order traversal: ";tree.postOrderTraversal(tree.getRoot());cout << endl;cout << "Level-order traversal: ";tree.levelOrderTraversal();cout << endl;// 測試其他功能cout << "Tree height: " << tree.getHeight(tree.getRoot()) << endl;cout << "Total nodes: " << tree.countNodes(tree.getRoot()) << endl;// 測試查找節點功能char searchValue = 'F';TreeNode<char>* found = tree.findNode(tree.getRoot(), searchValue);if (found){cout << "Found node: " << found->data << endl;}else{cout << "Node " << searchValue << " not found" << endl;}return 0;
}


五、樹的應用
?
- 文件系統目錄結構
- 數據庫索引
- 網絡路由算法
- 決策樹(機器學習)
- XML/HTML文檔對象模型(DOM)
- 組織結構圖
- 游戲中的場景圖
?
樹結構因其高效的查找、插入和刪除操作(O(log n)時間復雜度)而被廣泛應用于各種算法和系統中。

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

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

相關文章

Linux `ifconfig` 指令深度解析與替代方案指南

Linux `ifconfig` 指令深度解析與替代方案指南 一、核心功能與現狀1. 基礎作用2. 版本適配二、基礎語法與常用操作1. 標準語法2. 常用操作速查顯示所有接口信息啟用/禁用接口配置IPv4地址修改MAC地址(臨時)三、高級配置技巧1. 虛擬接口創建2. MTU調整3. 多播配置4. ARP控制四…

什么是分布式光伏系統?屋頂分布式光伏如何并網?

政策窗口倒計時&#xff01;分布式光伏如何破局而立&#xff1f; 2025年&#xff0c;中國分布式光伏行業迎來關鍵轉折&#xff1a; ? "430"落幕——搶裝潮收官&#xff0c;但考驗才剛開始&#xff1b; ? "531"生死線——新增項目全面市場化交易啟動&…

Cluster Interconnect in Oracle RAC

Cluster Interconnect in Oracle RAC (文檔 ID 787420.1)?編輯轉到底部 In this Document Purpose Scope Details Physical Layout of the Private Interconnect Why Do We Need a Private Interconnect ? Interconnect Failure Interconnect High Availability Private Inte…

.Net HttpClient 使用準則

HttpClient 使用準則 System.Net.Http.HttpClient 類用于發送 HTTP 請求以及從 URI 所標識的資源接收 HTTP 響應。 HttpClient 實例是應用于該實例執行的所有請求的設置集合&#xff0c;每個實例使用自身的連接池&#xff0c;該池將其請求與其他請求隔離開來。 從 .NET Core …

【PostgreSQL】數據庫主從庫備份與高可用部署

文章目錄 一、架構設計原理二、部署清單示例2.1 StatefulSet配置片段2.2 Service配置三、配置詳解3.1 主節點postgresql.conf3.2 從節點配置四、初始化流程4.1 創建復制用戶4.2 配置pg_hba.conf五、故障轉移示例5.1 自動切換腳本5.2 手動提升從節點六、監控與維護6.1 關鍵監控指…

JavaScript 數組去重:11 種方法對比與實戰指南

文章目錄 前言一、使用 Set 數據結構二、使用 filter indexOf三、使用 reduce 累加器四、雙重 for 循環五、利用對象屬性唯一性六、先排序后去重七、使用 Map 數據結構八、使用 includes 方法九、優化處理 NaN 的 filter 方法十、利用 findIndex十一.利用Set和展開運算符處理多…

ai agent(智能體)開發 python3基礎14:在python 中 總能看到方法里面套方法,那什么時候用這種方式合適呢?

讓人頭疼的方法嵌套還是要去了解的 在 Python 中&#xff0c;方法內部嵌套方法&#xff08;即在類的方法中定義另一個函數&#xff09;是一種常見的代碼組織技巧&#xff0c;它可以在特定場景下帶來以下好處&#xff1a; 1. 代碼復用與邏輯封裝 如果某個方法內部有重復的邏輯…

Yocto項目實戰經驗總結:從入門到高級的全面概覽

本文面向開發者和實際項目經驗者&#xff0c;分享經過大量實戰積累的 Yocto 項目工程經驗和基礎技巧。本文簡明但精彩&#xff0c;應用和觀察相結合&#xff0c;充分適合做為全面進階 Yocto 項目開發的實用指南。 一、入門理解&#xff1a;Yocto 是什么&#xff1f;規劃如何開始…

添加物體.

在cesium中我們可以添加物體進入地圖.我們以廣州塔為例 //生成廣州塔的位置var position2 Cesium.Cartesian3.fromDegrees(113.3191,23.109,100)viewer.camera.setView({//指定相機位置destination: position2, 運行后如圖 我們使用cesium官網提供的代碼為廣州塔在地圖上標點…

正則表達式非捕獲分組?:

一個使用 Java 正則表達式的具體例子&#xff0c;展示了 (ab) 和 (?:ab) 的不同&#xff1a; 示例 1&#xff1a;使用 (ab)&#xff08;捕獲分組&#xff09; import java.util.regex.*; public class RegexExample { public static void main(String[] args) { …

ragflow報錯:KeyError: ‘\n “序號“‘

環境&#xff1a; ragflowv 0.17.2 問題描述&#xff1a; ragflow報錯&#xff1a;KeyError: ‘\n “序號”’ **1. 推薦表&#xff08;輸出json格式&#xff09;** [{"},{},{"},{} ]raceback (most recent call last): May 08 20:06:09 VM-0-2-ubuntu ragflow-s…

Spring Boot-8啟動涉及的監聽器(擴展點)

從出現時間上看&#xff1a; org.springframework.context.ApplicationListener&#xff0c;Spring 1.0開始出現 org.springframework.context.ApplicationContextInitializer&#xff0c;Spring 3.1開始出現 org.springframework.boot.SpringApplicationRunListener&#x…

如何啟動vue項目及vue語法組件化不同標簽應對的作用說明

如何啟動vue項目及vue語法組件化不同標簽應對的作用說明 提示&#xff1a;幫幫志會陸續更新非常多的IT技術知識&#xff0c;希望分享的內容對您有用。本章分享的是node.js和vue的使用。前后每一小節的內容是存在的有&#xff1a;學習and理解的關聯性。【幫幫志系列文章】&…

思考:(linux) tmux 超級終端快速入門的宏觀思維

tmux 工具集合 GitHub - rothgar/awesome-tmux: A list of awesome resources for tmux 要點&#xff1a; 習慣性思維的變換與宿主機之間的雙向復制、粘貼手動備份全部窗口&#xff0c;以及還原自定義窗格提示信息TPM 插件的安裝思想別名 在有些場景里&#xff0c;可能無法…

Python實例題:Python協程詳解公開課

目錄 Python實例題 題目 課程目標 課程內容規劃 1. 課程開場&#xff08;5 分鐘&#xff09; 2. 基礎概念講解&#xff08;15 分鐘&#xff09; 并發與并行&#xff1a; 線程與進程&#xff1a; 3. Python 協程的實現方式&#xff08;20 分鐘&#xff09; 生成器實現…

AI時代的數據可視化:未來已來

你有沒有想過&#xff0c;數據可視化在未來會變成什么樣&#xff1f;隨著人工智能&#xff08;AI&#xff09;的飛速發展&#xff0c;數據可視化已經不再是簡單的圖表和圖形&#xff0c;而是一個充滿無限可能的智能領域。AI時代的可視化不僅能自動解讀數據&#xff0c;還能預測…

強化學習PPO算法學習記錄

1. 四個模型&#xff1a; Policy Model&#xff1a;我們想要訓練的目標語言模型。我們一般用SFT階段產出的SFT模型來對它做初始化。Reference Model&#xff1a;一般也用SFT階段得到的SFT模型做初始化&#xff0c;在訓練過程中&#xff0c;它的參數是凍結的。Ref模型的主要作用…

邊緣計算從專家到小白

“云-邊-端”架構 “云” &#xff1a;傳統云計算的中心節點&#xff0c;是邊緣計算的管控端。匯集所有邊緣的感知數據、業務數據以及互聯網數據&#xff0c;完成對行業以及跨行業的態勢感知和分析。 “邊” &#xff1a;云計算的邊緣側&#xff0c;分為基礎設施邊緣和設備邊緣…

Windows:Powershell的使用

文章目錄 零、格式化輸出命令1、Format-List&#xff08;別名&#xff1a;fl&#xff09; 一、服務管理SC命令二、軟件管理命令三、權限管理命令1、Get-Acl2、Set-Acl 總結 零、格式化輸出命令 1、Format-List&#xff08;別名&#xff1a;fl&#xff09; 可通過管道符傳遞對象…

實現在h5中添加日歷提醒:safari喚起系統日歷,其它瀏覽器跳轉google日歷

需求&#xff1a;點擊按鈕后&#xff0c;將設定的一些信息插入到系統日歷的日程安排中。 調研過程 先google了一段時間&#xff0c;了解該需求大概的實現方式。可以創建日歷文件&#xff0c;在點擊的時候下載該日歷文件&#xff0c;看起來還比較復雜&#xff0c;并且由于不具…