數據結構--二叉搜索樹

目錄

二叉搜索樹的概念

二叉樹的實現

結點類?

函數接口總覽

實現二叉樹

二叉搜索樹的應用

K模型

KV模型

二叉搜索樹的性能分析

二叉搜索樹的概念

? ? 二叉搜索樹(Binary Search Tree,簡稱BST)是一種特殊的二叉樹,其具有以下幾個性質:

  1. 每個節點至多有兩個子節點:分別稱為左子節點和右子節點。
  2. 左子樹上的所有節點的值都小于根節點的值
  3. 右子樹上的所有節點的值都大于根節點的值
  4. 每個節點的左右子樹也都是二叉搜索樹

這些性質確保了在二叉搜索樹中進行查找、插入和刪除操作具有良好的性能。具體地,這些操作在平均情況下的時間復雜度為 O(logn),其中 n 是樹中節點的數量。不過,在最壞情況下(樹退化成鏈表),時間復雜度可能會降為 O(n)。

下面是一個二叉搜索樹的示例:

在這個二叉搜索樹中:

  • 根節點是 8。
  • 根節點的左子樹包含節點 3、1、6、4 和 7,這些節點的值都小于 8。
  • 根節點的右子樹包含節點 10、14 和 13,這些節點的值都大于 8。

二叉樹的實現

結點類?

要實現二叉搜索樹,我們首先需要實現一個結點類:

  • 結點類當中包含三個成員變量:結點值、左指針、右指針。
  • 結點類當中只需實現一個構造函數即可,用于構造指定結點值的結點。
template<class K>
struct BSTreeNode
{K _key;                 // 結點值BSTreeNode<K>* _left;   // 左指針BSTreeNode<K>* _right;  // 右指針// 構造函數BSTreeNode(const K& key = K()): _key(key), _left(nullptr), _right(nullptr){}
};

函數接口總覽

//二叉搜索樹
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public://構造函數BSTree();//拷貝構造函數BSTree(const BSTree<K>& t);//賦值運算符重載函數BSTree<K>& operator=(BSTree<K> t);//析構函數~BSTree();//插入函數bool Insert(const K& key);//刪除函數bool Erase(const K& key);//查找函數Node* Find(const K& key);//中序遍歷void InOrder();
private:Node* _root; //指向二叉搜索樹的根結點
};

? ? 為了在實現其他接口的過程中方便隨時檢查,最好實現一個二叉搜索樹的中序遍歷接口,當我們對二叉搜索樹進行一次操作后,可以調用中序遍歷接口對二叉搜索樹進行遍歷,若二叉搜索樹進行操作后的遍歷結果仍為升序,則可以初步判斷所實現的接口是正確。

//中序遍歷的子函數
void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left); //遍歷左子樹cout << root->_key << " "; //遍歷根結點_InOrder(root->_right); //遍歷右子樹
}
//中序遍歷
void InOrder()
{_InOrder(_root);cout << endl;
}

實現二叉樹

代碼如下:

// 定義二叉搜索樹模板類
template<class K>
class BSTree
{
private:BSTreeNode<K>* _root;  // 樹的根結點// 輔助函數:遞歸拷貝樹BSTreeNode<K>* CopyTree(BSTreeNode<K>* root){if (root == nullptr)return nullptr;BSTreeNode<K>* newNode = new BSTreeNode<K>(root->_key);newNode->_left = CopyTree(root->_left);newNode->_right = CopyTree(root->_right);return newNode;}// 輔助函數:遞歸銷毀樹void DestroyTree(BSTreeNode<K>* root){if (root != nullptr){DestroyTree(root->_left);   // 遞歸銷毀左子樹DestroyTree(root->_right);  // 遞歸銷毀右子樹delete root;                // 刪除當前結點}}// 輔助函數:遞歸插入BSTreeNode<K>* InsertRecursive(BSTreeNode<K>* root, const K& key){if (root == nullptr){return new BSTreeNode<K>(key);  // 找到插入位置后創建新結點}if (key < root->_key){root->_left = InsertRecursive(root->_left, key);  // 遞歸插入左子樹}else if (key > root->_key){root->_right = InsertRecursive(root->_right, key); // 遞歸插入右子樹}return root;}// 輔助函數:遞歸刪除BSTreeNode<K>* DeleteRecursive(BSTreeNode<K>* root, const K& key){if (root == nullptr)return root;if (key < root->_key){root->_left = DeleteRecursive(root->_left, key);  // 在左子樹中刪除}else if (key > root->_key){root->_right = DeleteRecursive(root->_right, key); // 在右子樹中刪除}else{if (root->_left == nullptr){BSTreeNode<K>* temp = root->_right;delete root;  // 刪除當前結點return temp;}else if (root->_right == nullptr){BSTreeNode<K>* temp = root->_left;delete root;  // 刪除當前結點return temp;}BSTreeNode<K>* temp = MinValueNode(root->_right);  // 找到右子樹中最小值結點root->_key = temp->_key;  // 用右子樹中最小值替換當前結點root->_right = DeleteRecursive(root->_right, temp->_key);  // 刪除右子樹中的最小值結點}return root;}// 輔助函數:找到最小值結點BSTreeNode<K>* MinValueNode(BSTreeNode<K>* node){BSTreeNode<K>* current = node;while (current && current->_left != nullptr){current = current->_left;  // 找到最左端的結點即為最小值結點}return current;}// 輔助函數:遞歸查找BSTreeNode<K>* SearchRecursive(BSTreeNode<K>* root, const K& key) const{if (root == nullptr || root->_key == key)return root;if (key < root->_key)return SearchRecursive(root->_left, key);  // 在左子樹中查找return SearchRecursive(root->_right, key); // 在右子樹中查找}public:// 構造函數,初始化空樹BSTree(): _root(nullptr){}// 拷貝構造函數BSTree(const BSTree<K>& other): _root(nullptr){_root = CopyTree(other._root);  // 深拷貝另一棵樹}// 賦值運算符重載函數BSTree<K>& operator=(const BSTree<K>& other){if (this != &other){DestroyTree(_root);  // 銷毀當前樹_root = CopyTree(other._root);  // 深拷貝另一棵樹}return *this;}// 析構函數,銷毀樹~BSTree(){DestroyTree(_root);  // 遞歸銷毀樹中所有結點}// 插入函數(非遞歸)void InsertIterative(const K& key){if (_root == nullptr){_root = new BSTreeNode<K>(key);  // 插入根結點return;}BSTreeNode<K>* parent = nullptr;BSTreeNode<K>* current = _root;while (current != nullptr){parent = current;if (key < current->_key){current = current->_left;  // 移動到左子結點}else if (key > current->_key){current = current->_right; // 移動到右子結點}else{return;  // 不插入重復值}}if (key < parent->_key){parent->_left = new BSTreeNode<K>(key);  // 插入左子結點}else{parent->_right = new BSTreeNode<K>(key); // 插入右子結點}}// 插入函數(遞歸)void Insert(const K& key){_root = InsertRecursive(_root, key);  // 調用遞歸插入函數}// 刪除函數(非遞歸)void DeleteIterative(const K& key){BSTreeNode<K>* parent = nullptr;BSTreeNode<K>* current = _root;while (current != nullptr && current->_key != key){parent = current;if (key < current->_key){current = current->_left;  // 移動到左子結點}else{current = current->_right; // 移動到右子結點}}if (current == nullptr)return;if (current->_left == nullptr || current->_right == nullptr){BSTreeNode<K>* newCurrent;if (current->_left == nullptr){newCurrent = current->_right;}else{newCurrent = current->_left;}if (parent == nullptr){_root = newCurrent;  // 刪除根結點}else if (current == parent->_left){parent->_left = newCurrent;  // 刪除左子結點}else{parent->_right = newCurrent; // 刪除右子結點}delete current;}else{BSTreeNode<K>* p = nullptr;BSTreeNode<K>* temp;temp = current->_right;while (temp->_left != nullptr){p = temp;temp = temp->_left;}if (p != nullptr){p->_left = temp->_right;}else{current->_right = temp->_right;}current->_key = temp->_key;delete temp;}}// 刪除函數(遞歸)void Delete(const K& key){_root = DeleteRecursive(_root, key);  // 調用遞歸刪除函數}// 查找函數(非遞歸)BSTreeNode<K>* SearchIterative(const K& key) const{BSTreeNode<K>* current = _root;while (current != nullptr && current->_key != key){if (key < current->_key){current = current->_left;  // 移動到左子結點}else{current = current->_right; // 移動到右子結點}}return current;  // 返回找到的結點或 nullptr}// 查找函數(遞歸)BSTreeNode<K>* Search(const K& key) const{return SearchRecursive(_root, key);  // 調用遞歸查找函數}
};

用法和預期效果:

  1. 構造函數

    • 用法:BSTree<int> tree;
    • 預期效果:創建一個空的二叉搜索樹。
  2. 拷貝構造函數

    • 用法:BSTree<int> tree2 = tree1;
    • 預期效果:深拷貝tree1,創建一個新的二叉搜索樹tree2,其結構和tree1相同。
  3. 賦值運算符重載函數

    • 用法:tree2 = tree1;
    • 預期效果:深拷貝tree1tree2,覆蓋tree2原來的內容。
  4. 析構函數

    • 用法:當樹對象生命周期結束時自動調用。
    • 預期效果:遞歸銷毀樹中所有結點,釋放內存。
  5. 插入函數(非遞歸)

    • 用法:tree.InsertIterative(10);
    • 預期效果:在樹中插入值為10的結點。
  6. 插入函數(遞歸)

    • 用法:tree.Insert(10);
    • 預期效果:在樹中插入值為10的結點。
  7. 刪除函數(非遞歸)

    • 用法:tree.DeleteIterative(10);
    • 預期效果:在樹中刪除值為10的結點。
  8. 刪除函數(遞歸)

    • 用法:tree.Delete(10);
    • 預期效果:在樹中刪除值為10的結點。
  9. 查找函數(非遞歸)

    • 用法:BSTreeNode<int>* node = tree.SearchIterative(10);
    • 預期效果:在樹中查找值為10的結點,返回指向該結點的指針,如果未找到則返回nullptr
  10. 查找函數(遞歸)

    • 用法:BSTreeNode<int>* node = tree.Search(10);
    • 預期效果:在樹中查找值為10的結點,返回指向該結點的指針,如果未找到則返回nullptr

二叉搜索樹的應用

二叉搜索樹(BST)是一種重要的數據結構,廣泛應用于各種算法和系統中。以下是一些常見的應用:

  1. 符號表:在編譯器中,二叉搜索樹可以用來實現符號表,用于存儲變量和函數的名稱及其屬性。
  2. 字典:二叉搜索樹可以用來實現字典(例如鍵值對存儲),支持高效的插入、刪除和查找操作。
  3. 優先隊列:可以使用二叉搜索樹來實現優先隊列,其中元素按照優先級排列,支持快速的插入和刪除操作。
  4. 數據庫索引:在數據庫系統中,二叉搜索樹可以用作索引結構,以加速查詢操作。
  5. 排序和搜索:二叉搜索樹天然地支持中序遍歷,從而可以對元素進行排序和高效搜索。

K模型

? ? K模型是基于二叉搜索樹的一種簡化形式,主要用于處理單個鍵(key)的存儲和查詢。每個結點只包含一個鍵,不涉及值(value)。?

比如:給定一個單詞,判斷該單詞是否拼寫正確。具體方式如下:

  1. 以單詞集合中的每個單詞作為key,構建一棵二叉搜索樹。
  2. 在二叉搜索樹中檢索該單詞是否存在,存在則拼寫正確,不存在則拼寫錯誤。

KV模型

? ? KV模型是二叉搜索樹的擴展形式,用于處理鍵值對(key-value)的存儲和查詢。每個結點包含一個鍵和一個值。?

比如:英漢詞典就是英文與中文的對應關系,即<word, Chinese>就構成一種鍵值對。具體方式如下:

以<單詞, 中文含義>為鍵值對,構建一棵二叉搜索樹。注意:二叉搜索樹需要進行比較,鍵值對比較時只比較key。
查詢英文單詞時,只需給出英文單詞就可以快速找到與其對應的中文含義。

二叉搜索樹的性能分析

時間復雜度

  1. 查找、插入和刪除

    • 最優情況:當樹是平衡的(完全平衡二叉樹),時間復雜度為O(log n)。
    • 最壞情況:當樹退化成鏈表(每個結點只有一個子結點),時間復雜度為O(n)。
  2. 遍歷

    • 中序遍歷、先序遍歷、后序遍歷的時間復雜度均為O(n),因為需要訪問每個結點。

空間復雜度

  1. 空間使用

    • 空間復雜度為O(n),n為樹中的結點數。
  2. 遞歸調用棧

    • 在最壞情況下(樹退化成鏈表),遞歸調用棧的空間復雜度為O(n)。

平衡性

  1. 平衡二叉樹:如AVL樹、紅黑樹等,保證在最壞情況下也能達到O(log n)的時間復雜度。
  2. 普通二叉搜索樹:如果輸入數據是隨機的,樹大概率接近平衡。但如果輸入數據是有序的(或接近有序),樹可能退化為鏈表,導致性能下降。

性能優化

  1. 自平衡二叉搜索樹:如AVL樹、紅黑樹、Splay樹等,通過自動調整樹的結構,保證樹的平衡,從而提升性能。
  2. B樹和B+樹:特別適用于數據庫索引,支持高效的磁盤存取操作。
  3. 散列:對于一些應用,哈希表(Hash Table)可以提供更快的查找、插入和刪除操作,但不適用于需要排序的場景。

總結

二叉搜索樹(BST)是一種基礎而重要的數據結構,廣泛應用于符號表、字典、優先隊列和數據庫索引等場景。K模型和KV模型分別處理集合和字典的需求。BST的性能在很大程度上取決于樹的平衡性,使用自平衡樹可以保證最壞情況下的性能。此外,對于特定應用場景,選擇合適的數據結構(如B樹或哈希表)也非常重要。

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

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

相關文章

數據庫(6)——數據類型

SQL標準常用的數據類型有&#xff1a; 數據類型含義CHAR(n),CHARACTER(n)長度為n的定長字符串VARCHAR&#xff08;n&#xff09;最大長度為n的變長字符串CLOB字符串大對象BLOB二進制大對象SMALLINT2字節 短整數INT , INTEGER4字節 整數BIGINT8字節 大整數FLOAT(n)精度為n的浮點…

6818 android 修改開機 logo, 編譯腳本分析

問題&#xff1a; 客戶需要去掉 android5.1 的開機logo. 說明&#xff1a; 對于Android5.1 來說&#xff0c;uboot 與kernel 的logo 是一個。 過程&#xff1a; 其實對于開機logo 的修改很簡單&#xff0c;直接參考廠家手冊就可以了。 這是 android4.4 的開機logo 的修改&…

設計一個代辦功能模塊

目錄 1. 需求分析2. 數據庫設計用戶表&#xff08;Users Table&#xff09;代辦任務表&#xff08;Tasks Table&#xff09;訂單表&#xff08;Orders Table&#xff09;評價表&#xff08;Reviews Table&#xff09; 3. 功能實現創建代辦任務前端部分后端部分 接受代辦任務前端…

產品經理-需求收集(二)

1. 什么是需求 指在一定的時期中&#xff0c;一定場景中&#xff0c;無論是心理上還是生理上的&#xff0c;用戶有著某種“需要”&#xff0c;這種“需要”用戶自己不一定知道的&#xff0c;有了這種“需要”后用戶就有做某件事情的動機并促使達到其某種目的&#xff0c;這也就…

FPGA實現多路并行dds

目錄 基本原理 verilog代碼 仿真結果? 基本原理 多路并行dds&#xff0c;傳統DDS的局限性在于輸出頻率有限。根據奈奎斯特采樣定理&#xff0c;單路DDS的輸出頻率應小于系統時鐘頻率的一半。但是在很多地方&#xff0c;要使采樣率保持一致&#xff0c;所以&#xff0c;為了…

【CTF Web】CTFShow web7 Writeup(SQL注入+PHP+進制轉換)

web7 1 阿呆得到最高指示&#xff0c;如果還出問題&#xff0c;就卷鋪蓋滾蛋&#xff0c;阿呆心在流血。 解法 注意到&#xff1a; <!-- flag in id 1000 -->攔截很多種字符&#xff0c;連 select 也不給用了。 if(preg_match("/\|\"|or|\||\-|\\\|\/|\\*|\…

路徑規劃算法的復雜度

通常通過以下指標來衡量&#xff1a; 時間復雜度&#xff1a;這是評估算法執行所需時間的量度。它通常用大O符號表示&#xff0c;給出了算法運行時間隨著輸入規模增長的增長率。例如&#xff0c;一個時間復雜度為O(n^2)的算法在處理大規模輸入時會比時間復雜度為O(n log n)的算…

PostgreSQL的擴展(extensions)-常用的擴展之pg_plan_advsr

PostgreSQL的擴展&#xff08;extensions&#xff09;-常用的擴展之pg_plan_advsr pg_plan_advsr 是 PostgreSQL 社區中的一個擴展&#xff0c;用于分析和改進查詢執行計劃。它能夠自動識別哪些查詢執行緩慢&#xff0c;并提供優化建議&#xff0c;以提高查詢性能。pg_plan_ad…

AI時代存儲大戰,NAND閃存市場風云再起!

隨著人工智能&#xff08;AI&#xff09;相關半導體對高帶寬存儲&#xff08;HBM&#xff09;需求的推動&#xff0c;NAND閃存市場也感受到了這一趨勢的影響。 據《Business Korea》援引行業消息來源稱&#xff0c;NAND閃存市場的競爭正在加劇&#xff0c;而存儲巨頭三星和SK海…

CSP俄羅斯方塊(簡單易懂)

開始將題目理解成了&#xff0c;開始的列應該是從輸入圖案的最左端開始計算&#xff0c;將前面所有的空列都刪掉&#xff0c;代碼如下&#xff1a; #include<bits/stdc.h> using namespace std; const int N 1e410; const int M 1e510; int a[20][20]; int b[5][5];int…

Redis的持久化方式:

Redis提供了兩種數據持久化的方式&#xff1a; RDB 該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。 AOF 該機制將以日志的形式記錄服務器所處理的每一個寫操作。 在Redis服務器啟動之初會讀取文件來重新構建數據庫&#xff0c;以保證啟動后數據庫中的數據是完…

leedcode【203】. 移除鏈表元素——Java解法

Problem: 203. 移除鏈表元素 題目思路解題方法復雜度Code效果 題目 給你一個鏈表的頭節點 head 和一個整數 val &#xff0c;請你刪除鏈表中所有滿足 Node.val val 的節點&#xff0c;并返回 新的頭節點 。 示例 1&#xff1a; 輸入&#xff1a;head [1,2,6,3,4,5,6], val…

OS復習筆記ch6-1

死鎖的原理 定義 一組進程中&#xff0c;其中每個進程因等待事件而阻塞&#xff0c;且所等待的事件只能被這組進程中的另一阻塞進程激發稱之為死鎖。 舉例如下 四個車輛希望緊迫的希望能很快通過&#xff0c;每輛車需要兩個象限的資源&#xff0c;然而四個車都只得到一個象…

golang調用aliyun的語音通話服務,復制直接使用

golang調用aliyun的語音通話服務 通過API使用語音通知/語音驗證碼——阿里云官方文檔SingleCallByTts - 發送語音驗證碼或文本轉語音類型的語音通知入門流程主要參數引入阿里云語音官方SDK-go版本完整代碼通過API使用語音通知/語音驗證碼——阿里云官方文檔 https://help.aliy…

電子電器架構 - AUTOSAR軟件架構介紹

電子電器架構 - AUTOSAR軟件架構介紹 我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 屏蔽力是信息過載時代一個人的特殊競爭力,任何消耗你的人和事,多看一眼都是你的不對。非必要不費力證明自己…

MFC Winsock 類:MFC 中的網絡編程

目錄 概述 一.MFC Winsock 類簡介 1.MFC Winsock 類的主要功能 2.MFC Winsock 類的主要優點 3.MFC Winsock 類的主要缺點 4.MFC Winsock 類的主要類 5.MFC Winsock 類示例 二.CAsyncSocket 類 1.主要功能 異步通信 事件驅動 數據傳輸 套接字選項 2.常用函數 創建…

Maven多環境打包配置

一、啟動時指定環境配置文件 在啟動springboot應用的jar包時&#xff0c;我們可以指定配置文件&#xff0c;通常把配置文件上傳到linux服務器對應jar包的同級目錄&#xff0c;或者統一的配置文件存放目錄 java -jar your-app.jar --spring.config.location/opt/softs/applicat…

matlab 圖像的中值濾波

目錄 一、功能概述1、算法概述2、主要函數3、計算公式二、代碼實現三、結果展示四、參考鏈接本文由CSDN點云俠翻譯,放入付費專欄只為防不要臉的爬蟲。專欄值錢的不是本文,切勿因本文而訂閱。 一、功能概述 1、算法概述 中值濾波是圖像處理中一種常用的非線性運算,用于減少…

間接平差——以水準網平差為例 (python詳細過程版)

目錄 一、原理概述二、案例分析三、代碼實現四、結果展示本文由CSDN點云俠原創,間接平差——以水準網平差為例 (python詳細過程版),爬蟲自重。如果你不是在點云俠的博客中看到該文章,那么此處便是不要臉的爬蟲與GPT生成的文章。 一、原理概述 間接平差的函數模型和隨機模型…

openai api的初次嘗試

不懂已經不去百度了&#xff0c;現在直接問chatgpt就解決絕大多數問題了。 OpenAI API目前還沒有官方支持的npm庫&#xff0c;但是您可以使用現有的第三方npm庫進行OpenAI API的訪問和使用。這里提供一個npm庫 openai-node 的安裝和使用方法&#xff1a; 在命令行或終端中使用…