C++實現二叉排序樹


1.定義

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

二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹:
(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
(3)左、右子樹也分別為二叉排序樹;
(4)沒有鍵值相等的節點。

 

簡言之,左子樹小於父節點,右子樹大於父節點的二叉樹就是二叉排序樹
 

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. 它有左子樹,則左子樹的最右結點為其前驅節點
  2. 它沒有左子樹,且它本身為右子樹,則其父節點為其前驅節點
  3. 它沒有左子樹,且它本身為左子樹,則它的前驅節點為“第一個擁有右子樹的父節點”

判斷當前結點左子樹是否為空,不為空進入

左孩子賦予當前結點,與情況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. 它有右子樹;則其后繼節點為其右子樹的最左節點
  2. 它沒有右子樹,但它本身是一個左孩子,則后繼節點為它的雙親
  3. 它沒有右子樹,但它本身是一個右孩子,則其后繼節點為“具有左孩子的最近父節點”

判斷當前結點右子樹是否是空指針,不為空則進入

右子樹賦予當前結點,當左子樹不為空,將左子樹賦予當前結點,對應情況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

代碼:https://github.com/cjy513203427/C_Program_Base/tree/master/60.%E4%BA%8C%E5%8F%89%E6%8E%92%E5%BA%8F%E6%A0%91


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM