1.定義
二叉排序樹(Binary Sort Tree),又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。

2.代碼實現
2.1 插入元素:
假設我們要為數組 a[] = {10 , 5 , 15 , 6 , 4 , 16 }構建一個二叉排序樹,我們按順序逐個插入元素。
插入過程是這樣的:
- 如果是空樹,則創建一個新節點,新節點作為根,因此以元素10構建的節點為該二叉查找樹的根。
- 插入5,5比10小,與10的左孩子節點進行比較,10的左孩子節點為空,進行插入。
- 插入15,15比10大,與10的右孩子節點進行比較,10的右孩子節點為空,進行插入。
- 插入6,6比10小,與10的左孩子節點5比較;6比5大,與5的右孩子節點進行比較,5的右孩子為空,進行插入。
- 插入4,4比10小,與10的左孩子節點5比較;4比5小,與5的左孩子節點進行比較,5的左孩子為空,進行插入。
- 插入16,16比10大,與10的右孩子節點15比較;16比15大,與15的右孩子節點進行比較,15的右孩子為空,進行插入。
從這個過程我們可以總結出插入新元素的步驟:
- 尋找元素合適的插入位置:新元素與當前結點進行比較,若值大於當前結點,則從右子樹進行尋找;否則從左子樹進行尋找.
- 找到插入位置之后,以元素的值構建新節點,插入二叉排序樹中
/*插入函數*/ template <typename T> void BSTree<T>::insert(T key) { BSNode<T>* pparent = nullptr; BSNode<T>* pnode = root; while (pnode != nullptr) //尋找合適的插入位置 { pparent = pnode; if (key > pnode->value) pnode = pnode->rchild; else if (key < pnode->value) pnode = pnode->lchild; else break; } pnode = new BSNode<T>(key); if (pparent == nullptr) //如果是空樹 { root = pnode; //則新節點為根 } else { if (key > pparent->value) { pparent->rchild = pnode;//新節點為其父節點的右孩子 } else pparent->lchild = pnode;//新節點為其父節點左孩子 } pnode->parent = pparent; //指明新節點的父節點 };
2.2 刪除元素:
刪除二叉排序樹的某個節點有三種情況:
- 被刪除節點同時有左子樹與右子樹。
- 被刪除節點只有左子樹或只有右子樹。
- 被刪除節點沒有子樹。
對於第一種情況,我們的處理方式是將前驅節點的值保存在當前結點,繼而刪除前驅節點。
對於第二種情況,我們直接用子樹替換被刪節點。
對於第三種情況,我們可以直接刪除節點。
template <typename T>
void BSTree<T>::remove(BSNode<T>* pnode, T key) { if (pnode != nullptr) { if (pnode->value == key) { BSNode<T>* pdel = nullptr; if (pnode->lchild == nullptr || pnode->rchild == nullptr) pdel = pnode; //情況二、三:被刪節點只有左子樹或右子樹,或沒有孩子 else pdel = predecessor(pnode); //情況一:被刪節點同時有左右子樹,則刪除該節點的前驅 //此時,被刪節點只有一個孩子(或沒有孩子).保存該孩子指針 BSNode<T>* pchild = nullptr; if (pdel->lchild != nullptr) pchild = pdel->lchild; else pchild = pdel->rchild; //讓孩子指向被刪除節點的父節點 if (pchild != nullptr) pchild->parent = pdel->parent; //如果要刪除的節點是頭節點,注意更改root的值 if (pdel->parent == nullptr) root = pchild; //如果要刪除的節點不是頭節點,要注意更改它的雙親節點指向新的孩子節點 else if (pdel->parent->lchild == pdel) { pdel->parent->lchild = pchild; } else { pdel->parent->rchild = pchild; } if (pnode->value != pdel->value) pnode->value = pdel->value; delete pdel; } //進行遞歸刪除 else if (key > pnode->value) { remove(pnode->rchild, key); } else remove(pnode->lchild, key); } };
2.3 查找指定元素的節點(While循環):
判斷當前結點是否為空指針,不是空指針進入
key直接和當前結點value相等,return當前結點
key大於當前結點value,將右子節點賦予當前結點
key小於當前結點value,將左子節點賦予當前結點
直到key等於當前結點或者當前結點為空停止while循環
/*查找指定元素的節點(非遞歸)*/ template <typename T> BSNode<T>* BSTree<T>::search_Iterator(T key) { BSNode<T> * pnode = root; while (pnode != nullptr) { if (key == pnode->value) //找到 return pnode; if (key > pnode->value) //關鍵字比節點值大,在節點右子樹查找 pnode = pnode->rchild; else pnode = pnode->lchild; //關鍵字比節點值小,在節點左子樹查找 } return nullptr; };
2.4 查找指定元素的節點(遞歸):
邏輯同上,將pnode替換為左或者右子節點,遞歸調用之
/*查找指定元素的節點(遞歸)*/ template <typename T> BSNode<T>* BSTree<T>::search_recursion(T key) { return search(root, key); }; /*private:search()*/ /*遞歸查找的類內部實現*/ template <typename T> BSNode<T>* BSTree<T>::search(BSNode<T>* & pnode, T key) { if (pnode == nullptr) return nullptr; if (pnode->value == key) return pnode; //cout << "-->" << pnode->value << endl; //可以輸出查找路徑 if (key > pnode->value) return search(pnode->rchild, key); return search(pnode->lchild, key); };
2.5 尋找其前驅節點:
前驅和后繼結點說明:
對於一棵二叉排序樹,中序遍歷時剛好可以輸出一個非遞減的序列。例如中序遍歷圖九樹a:3 4 5 6 10 15 16,則可稱:
- 4是5 前驅節點,6是5的后繼節點
- 6是10的前驅節點,15是10的后繼節點
前驅結點:
- 它有左子樹,則左子樹的最右結點為其前驅節點
- 它沒有左子樹,且它本身為右子樹,則其父節點為其前驅節點
- 它沒有左子樹,且它本身為左子樹,則它的前驅節點為“第一個擁有右子樹的父節點”
判斷當前結點左子樹是否為空,不為空進入
左孩子賦予當前結點,與情況1對應
判斷父節點是否是空指針並且當前結點本身是否是左子樹
本身是左子樹,進入循環,是第三種情況。不是左子樹,是第二種情況
/*尋找其前驅節點*/ /* 一個節點的前驅節點有3種情況: 1. 它有左子樹,則左子樹最右結點為其前驅節點 2. 它沒有左子樹,且它本身為右子樹,則其父節點為其前驅節點 3. 它沒有左子樹,且它本身為左子樹,則它的前驅節點為“第一個擁有右子樹的父節點” */ template <typename T> BSNode<T>* BSTree<T>::predecessor(BSNode<T>* pnode) { if (pnode->lchild != nullptr) { pnode = pnode->lchild; while (pnode->rchild != nullptr) { pnode = pnode->rchild; } return pnode; } BSNode<T>* pparent = pnode->parent; while (pparent != nullptr && pparent->lchild == pnode)//如果進入循環,則是第三種情況;否則為第二種情況 { pnode = pparent; pparent = pparent->parent; } return pparent; };
2.6 尋找后繼結點
- 它有右子樹;則其后繼節點為其右子樹的最左節點
- 它沒有右子樹,但它本身是一個左孩子,則后繼節點為它的雙親
- 它沒有右子樹,但它本身是一個右孩子,則其后繼節點為“具有左孩子的最近父節點”
判斷當前結點右子樹是否是空指針,不為空則進入
右子樹賦予當前結點,當左子樹不為空,將左子樹賦予當前結點,對應情況1
如果當前結點沒有右子樹
判斷父節點是否是空指針並且當前結點本身是否是右子樹
本身是右子樹,進入循環,對應情況3
本身不是右子樹,不進入循環,對應情況2
/*尋找其后繼節點*/ /* 一個點有后繼節點的情況: 1. 它有右子樹;則其后繼節點為其右子樹的最左節點 2. 它沒有右子樹,但它本身是一個左孩子,則后繼節點為它的雙親 3. 它沒有右子樹,但它本身是一個右孩子,則其后繼節點為“具有左孩子的最近父節點” */ template <typename T> BSNode<T>* BSTree<T>::successor(BSNode<T>* pnode) { if (pnode->rchild != nullptr) { pnode = pnode->rchild; while (pnode->lchild != nullptr) { pnode = pnode->lchild; } return pnode; } BSNode<T>* pparent = pnode->parent; while (pparent != nullptr&& pparent->rchild == pnode) { pnode = pparent; pparent = pparent->parent; } return pparent; };
2.7 尋找最大和最小元素數
這個比較好理解,根據二叉排序樹定義,尋找最大就是不停地遞歸尋找右子樹,直到右子樹為空結束
尋找最小就是不停地遞歸尋找左子樹,直到左子樹為空
/*尋找最小元素*/ template <typename T> T BSTree<T>::search_minimun() { return search_minimun(root); }; template <typename T> T BSTree<T>::search_minimun(BSNode<T>* p) { if (p->lchild != nullptr) return search_minimun(p->lchild); return p->value; }; /*尋找最大元素*/ template <typename T> T BSTree<T>::search_maximum() { return search_maximum(root); }; template <typename T> T BSTree<T>::search_maximum(BSNode<T>*p) { if (p->rchild != nullptr) return search_maximum(p->rchild); return p->value; };
參考:https://www.cnblogs.com/QG-whz/p/5168620.html#_label3_2