C++二叉查詢樹實現過程詳解
什麼是二叉查詢樹
在資料結構中,有一個奇葩的東西,說它奇葩,那是因為它重要,這就是樹。而在樹中,二叉樹又是當中的貴族。二叉樹的一個重要應用是它們在查詢中的應用,於是就有了二叉查詢樹。 使二叉樹成為一顆二叉查詢樹,需要滿足以下兩點:
- 對於樹中的每個節點X,它的左子樹中所有項的值都要小於X中的項;
- 對於樹中的每個節點Y,它的右子樹中所有項的值都要大於X中的項。
二叉查詢樹的基本操作
以下是對於二叉查詢樹的基本操作定義類,然後慢慢分析是如何實現它們的。
template<class T>
class BinarySearchTree{public:// 建構函式,初始化root值BinarySearchTree() : root(NULL){}// 解構函式,預設實現~BinarySearchTree() {}// 查詢最小值,並返回最小值const T &findMin() const;// 查詢最大值,並返回最大值const T &findMax() const;// 判斷二叉樹中是否包含指定值的元素bool contains(const T &x) const;// 判斷二叉查詢樹是否為空bool isEmpty() const { return root ? false : true; }// 列印二叉查詢樹的值void printTree() const;// 向二叉查詢樹中插入指定值void insert(const T &x);// 刪除二叉查詢樹中指定的值void remove(const T &x);// 清空整個二叉查詢樹void makeEmpty() const;private:// 指向根節點BinaryNode<T> *root;void insert(const T &x, BinaryNode<T> *&t) const;void remove(const T &x, BinaryNode<T> *&t) const;BinaryNode<T> *findMin(BinaryNode<T> *t) const;BinaryNode<T> *findMax(BinaryNode<T> *t) const;bool contains(const T &x, BinaryNode<T> *t) const;void printTree(BinaryNode<T> *t) const;void makeEmpty(BinaryNode<T> *&t) const;};
findMin和findMax實現
根據二叉查詢樹的性質:
- 對於樹中的每個節點X,它的左子樹中所有項的值都要小於X中的項;
- 對於樹中的每個節點Y,它的右子樹中所有項的值都要大於X中的項。
我們可以從root
節點開始:
- 一直沿著左節點往下找,直到子節點等於
NULL
為止,這樣就可以找到最小值了; - 一直沿著右節點往下找,直到子節點等於
NULL
為止,這樣就可以找到最大值了。
如下圖所示:
在程式中實現時,有兩種方法:
- 使用遞迴實現;
- 使用非遞迴的方式實現。
對於finMin
的實現,我這裡使用遞迴的方式,程式碼參考如下:
BinaryNode<T> *BinarySearchTree<T>::findMin(BinaryNode<T> *t) const
{if (t == NULL){return NULL;}else if (t->left == NULL){return t;}else{return findMin(t->left);}}
在findMin()
的內部呼叫findMin(BinaryNode<T> *t)
,這樣就防止了客戶端知道了root
根節點的資訊。上面使用遞迴的方式實現了查詢最小值,下面使用迴圈的方式來實現findMax
。
template<class T>
BinaryNode<T> *BinarySearchTree<T>::findMax(BinaryNode<T> *t) const{if (t == NULL){return NULL;}while (t->right){t = t->right;}return t;}
在很多面試的場合下,面試官一般都是讓寫出非遞迴的版本;而在對樹進行的各種操作,很多時候都是使用的遞迴實現的,所以,在平時學習時,在理解遞迴版本的前提下,需要關心一下對應的非遞迴版本。
contains實現
contains
用來判斷二叉查詢樹是否包含指定的元素。程式碼實現如下:
template<class T>
bool BinarySearchTree<T>::contains(const T &x, BinaryNode<T> *t) const{if (t == NULL){return false;}else if (x > t->element){return contains(x, t->right);}else if (x < t->element){return contains(x, t->left);}else{return true;}}
演算法規則如下:
- 首先判斷需要查詢的值與當前節點值的大小關係;
- 當小於當前節點值時,就在左節點中繼續查詢;
- 當大於當前節點值時,就在右節點中繼續查詢;
- 當找到該值時,直接返回true。
insert實現
insert
函式用來向兒茶查詢樹中插入新的元素,演算法處理如下:
- 首先判斷需要插入的值域當前節點值得大小關係;
- 當小於當前節點值時,就在左節點中繼續查詢插入點;
- 當大於當前節點值時,就在右節點中繼續查詢插入點;
- 當等於當前節點值時,什麼也不幹。
程式碼實現如下:
template<class T>
void BinarySearchTree<T>::insert(const T &x, BinaryNode<T> *&t) const{if (t == NULL){t = new BinaryNode<T>(x, NULL, NULL);}else if (x < t->element){insert(x, t->left);}else if (x > t->element){insert(x, t->right);}}
remove實現
remove
函式用來刪除二叉查詢樹中指定的元素值,這個處理起來比較麻煩。在刪除子節點時,需要分以下幾種情況進行考慮(結合下圖進行說明): 如下圖所示:
- 需要刪除的子節點,它沒有任何子節點;例如圖中的節點9、節點17、節點21、節點56和節點88;這些節點它們都沒有子節點;
- 需要刪除的子節點,只有一個子節點(只有左子節點或右子節點);例如圖中的節點16和節點40;這些節點它們都只有一個子節點;
- 需要刪除的子節點,同時擁有兩個子節點;例如圖中的節點66等。
對於情況1,直接刪除對應的節點即可;實現起來時比較簡單的;
對於情況2,直接刪除對應的節點,然後用其子節點佔據刪除掉的位置;
對於情況3,是比較複雜的。首先在需要被刪除節點的右子樹中找到最小值節點,然後使用該最小值替換需要刪除節點的值,然後在右子樹中刪除該最小值節點。
假如現在需要刪除包含值23的節點,步驟如下圖所示:
程式碼實現如下:
template<class T>
void BinarySearchTree<T>::remove(const T &x, BinaryNode<T> *&t) const{if (t == NULL){return;}if (x < t->element){remove(x, t->left);}else if (x > t->element){remove(x, t->right);}else if (t->left != NULL && t->right != NULL){// 擁有兩個子節點t->element = findMin(t->right)->element;remove(t->element, t->right);}else if (t->left == NULL && t->right == NULL){// 沒有子節點,直接幹掉delete t;t = NULL;}else if (t->left == NULL || t->right == NULL){// 擁有一個子節點BinaryNode *pTemp = t;t = (t->left != NULL) ? t->left : t->right;delete pTemp;}}
makeEmpty實現
makeEmpty
函式用來釋放整個二叉查詢樹佔用的記憶體空間,同理,也是使用的遞迴的方式來實現的。具體程式碼請下載文中最後提供的原始碼。
總結
這篇文章對資料結構中非常重要的二叉查詢樹進行了詳細的總結,雖然二叉查詢樹非常重要,但是理解起來還是非常容易的,主要是需要掌握對遞迴的理解。如果對遞迴有非常紮實的理解,那麼對於樹的一些操作,那都是非常好把握的,而理解二叉查詢樹又是後續的AVL平衡樹和紅黑樹的基礎,希望這篇文章對大家有幫助。
相關文章
- 二叉查詢樹的實現——C++C++
- 詳細二叉樹實現c++二叉樹C++
- 轉:C++實現的變種二分查詢法(折半查詢)--二叉查詢樹C++
- 二叉查詢樹概念及實現
- C#實現二叉查詢樹C#
- JavaScript實現簡單二叉查詢樹JavaScript
- 第 34 題:如何實現二叉查詢樹?
- 二叉樹 & 二叉查詢樹二叉樹
- 查詢二叉樹二叉樹
- 二叉查詢樹
- 二叉查詢樹(二叉排序樹)排序
- django 動態查詢實現過程Django
- 5分鐘瞭解二叉樹之二叉查詢樹二叉樹
- 資料結構與演算法——二叉查詢樹類的C++實現資料結構演算法C++
- 手擼二叉樹——二叉查詢樹二叉樹
- 使用JS去實現一個BST(二叉查詢樹)JS
- PHP 協程實現過程詳解PHP
- 最優二叉查詢樹—動態規劃C++動態規劃C++
- 平衡二叉查詢樹:紅黑樹
- 二叉查詢樹和笛卡爾樹
- 二叉查詢樹【二叉排序樹】構建和查詢演算法 PHP 版排序演算法PHP
- 二叉查詢樹的插入刪除查詢
- 二叉樹路徑查詢二叉樹
- 實現二叉搜尋樹的新增,查詢和刪除(JAVA)Java
- 折半查詢(C++實現)C++
- 【Node】詳解模組的實現過程
- 查詢|有序表折半查詢判定樹|二叉排序樹|3階B-樹排序
- oracle儲存過程詳解--遊標 實現增、刪、改、查Oracle儲存過程
- Amazing tree —— 二叉查詢樹
- 二叉查詢樹的個數
- JVM系列(四):java方法的查詢過程實現JVMJava
- 滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹(二叉查詢樹)和最優二叉樹二叉樹
- 二叉查詢樹(查詢、插入、刪除)——C語言C語言
- C/C++編譯過程詳解C++編譯
- PostgreSQL 原始碼解讀(20)- 查詢語句#5(查詢樹Query詳解)SQL原始碼
- 二叉搜尋樹演算法詳解與Java實現演算法Java
- 二叉排序樹查詢,插入,刪除排序
- 五大經典查詢(1)_二叉排序樹查詢排序