在數據結構中,有一個奇葩的東西,說它奇葩,那是由於它重要,這就是樹。而在樹中,二叉樹又是當中的貴族。二叉樹的一個重要應用是它們在查找中的應用,因而就有了二叉查找樹。 使二叉樹成爲一顆二叉查找樹,須要知足如下兩點:面試
如下是對於二叉查找樹的基本操做定義類,而後慢慢分析是如何實現它們的。算法
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;};
根據二叉查找樹的性質:函數
咱們能夠從root
節點開始:學習
NULL
爲止,這樣就能夠找到最小值了;NULL
爲止,這樣就能夠找到最大值了。以下圖所示:spa
在程序中實現時,有兩種方法:code
對於finMin
的實現,我這裏使用遞歸的方式,代碼參考以下:blog
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
用來判斷二叉查找樹是否包含指定的元素。代碼實現以下:
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;}}
算法規則以下:
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
函數用來刪除二叉查找樹中指定的元素值,這個處理起來比較麻煩。在刪除子節點時,須要分如下幾種狀況進行考慮(結合下圖進行說明): 以下圖所示:
對於狀況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
函數用來釋放整個二叉查找樹佔用的內存空間,同理,也是使用的遞歸的方式來實現的。具體代碼請下載文中最後提供的源碼。
這篇文章對數據結構中很是重要的二叉查找樹進行了詳細的總結,雖然二叉查找樹很是重要,可是理解起來仍是很是容易的,主要是須要掌握對遞歸的理解。若是對遞歸有很是紮實的理解,那麼對於樹的一些操做,那都是很是好把握的,而理解二叉查找樹又是後續的AVL平衡樹和紅黑樹的基礎,但願這篇文章對你們有幫助。