1. 程式人生 > >STL原始碼筆記(17)—二叉排序樹BST(C++封裝)

STL原始碼筆記(17)—二叉排序樹BST(C++封裝)

二叉排序樹BST

STL中還有一類非常重要的容器,就是關聯容器,比如map啊set啊等等,這些容器說實話,在應用層上還不能完全得心應手(比如幾種容器效率的考慮等等),更別說原始碼了,因此這一部分打算穩紮穩打,好好做做筆記研究一番。
說到關聯容器,我們想到了什麼AVL樹,紅黑樹等等,但大多時候我們僅僅侷限於知道其名字,或者知道其概念,俗話說“talk is cheap,show me the code”,因此,我打算從他們的祖爺爺二叉排序樹開始下手。(其實,侯老師的書上也是這麼安排的哈)

1.概念

1.任意節點的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
2.任意節點的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
3.任意節點的左、右子樹也分別為二叉查詢樹;
4.沒有鍵值相等的節點。
二叉查詢樹相比於其他資料結構的優勢在於查詢、插入的時間複雜度較低。為O

(logn)。二叉查詢樹是基礎性資料結構,用於構建更為抽象的資料結構,如集合、multiset、關聯陣列等。

2.性質

1.中序遍歷是一個升序的有序序列。
2.搜尋、插入、刪除的複雜度等於樹高,期望O(logn)最壞O(n)(數列有序,樹退化成線性表)。

3.二叉排序樹的實現

既然看到此,不如試著實現一下二叉排序樹,主要需要包含這些操作:構建二叉排序樹輸入一個序列輸出一個二叉排序樹,插入、刪除節點。

寫程式碼之前考慮的問題:

1.二叉排序樹的插入一定是在葉子節點處。

2.二叉排序樹的刪除需要考慮三種情況:
  a)待刪除節點在葉子節點處
    直接另其父節點相應指標制空,並刪除該節點即可。
  b)待刪除節點只含有一個孩子(左子樹為空或者右子樹為空)


    將待刪除節點父節點對應指標指向待刪除節點的孩子節點。
  c)待刪除節點即包含左右孩子都不為空
    找到待刪除節點的右子樹的最小值(右子樹一路向左),並將該值替換待刪除節點的值,最後刪除最小值原本所在位置的節點(葉子節點)。

3.二叉排序樹的中序遍歷是升序。
4.擯棄以前的C語言寫法,我這次想把BST封裝成一個C++類,雖然困難重重,勉強實現了吧。
5.既然是C++類,在解構函式中做所有節點記憶體釋放處理(最後一個根節點需要特殊處理)。

上述5個問題中除了對C++的熟悉程度外,涉及BST演算法部分最麻煩的就是刪除操作了,因為它考慮的情況比較多,這裡貼出侯老師書中的示意圖方便理解:

目標節點只有一個孩子節點

這裡寫圖片描述

目標節點有兩個子節點

這裡寫圖片描述

程式碼

編譯執行環境:Visual Studio 2013,Windows 7 32 bits

(1)二叉排序樹的節點資料結構

//BSTNode.h
#ifndef __BSTNODE_H__
#define __BSTNODE_H__
#include<iostream>
class BSTNode{
public:
    BSTNode();
    BSTNode(int val);
    int value;
    BSTNode *lchild;
    BSTNode *rchild;
};
#endif

//--------------------------------------------
//--------------------------------------------
//--------------------------------------------

//BSTNode.cpp
#include "BSTNode.h"
BSTNode::BSTNode()
{
    value = 0;
    lchild = NULL;
    rchild = NULL;
}
BSTNode::BSTNode(int val)
{
    value = val;
    lchild = NULL;
    rchild = NULL;
}

(2)二叉排序樹的C++類封裝

//BST.h
#ifndef __BST_H__
#define __BST_H__
#include "BSTNode.h"
#include <vector>
#include <iostream>
class BSTNode;
class BST
{
    //說明:
    //為了資料結構私有化,不為外部訪問,這裡提供一些私有內部函式實現真正的操作以"__"開頭。
    //對於public的介面來說,只需要直接呼叫內部函式即可
private:
    BSTNode * bstroot;//二叉排序樹資料結構
    BSTNode * __search(BSTNode* root,const int& key);//查詢關鍵字
    BSTNode * __treeMin(BSTNode*const root,BSTNode *&parent);//返回當前節點的最小孩子(一路向左)
    BSTNode * __treeMax(BSTNode*const root);//查詢最大值(未實現)
    bool __Insert( const int &key);//插入節點
    bool __Delete(const int &key);//刪除刪除
    bool __isLeaf(BSTNode* const &);//判斷是否是葉子節點
    bool __isNodeWithTwoChild(BSTNode * const &);//判斷是否有兩個孩子
    void __InorderTraversal(BSTNode *root,std::vector<int>&result);//中序遍歷
    void __DeleteAllNodes(BSTNode *root);//刪除所有節點
public:
    //建構函式
    BST();//預設建構函式
    BST(std::vector<int>arr);
    BST(int *arr, int len);
    //解構函式
    ~BST();
    bool isEmpty() const;//判斷樹空
    bool search(const int &key);//查詢關鍵字是否存在的對外介面
    bool Insert(const int &key);//插入節點的外部介面
    bool Delete(const int &key);//刪除節點的外部介面
    void InorderTraversal(std::vector<int>&);//中序遍歷的外部介面
};
#endif

(3)二叉排序樹C++類實現部分

//BST.cpp

#include "BST.h"

//判斷樹空
bool BST::isEmpty() const
{
    return bstroot == NULL;
}

//判斷是否是葉子節點(刪除部分用到)
bool BST::__isLeaf(BSTNode*const & root)
{
    if ((root->lchild == NULL) && (root->rchild == NULL))
        return true;
    else
        return false;
}

//判斷節點是否有兩個孩子(刪除部分用到)
bool BST::__isNodeWithTwoChild(BSTNode * const & root)
{
    if (root->lchild != NULL &&root->rchild != NULL)
        return true;
    else
        return false;
}

//找到當前節點為根的子樹中的最小值(刪除部分用到,因此返回其父節點和當前節點)
BSTNode * BST::__treeMin(BSTNode*const root,BSTNode *&parent)
{
    BSTNode * curr = root;
    while (curr->lchild != NULL)
    {
        parent = curr;
        curr = curr->lchild;
    }
    return curr;
}

//刪除節點內部實現
bool BST::__Delete(const int &key)
{
    bool found = false;//找到待刪除的元素
    if (isEmpty())
    {
        std::cerr << "Binary Search Tree Is Empty" << std::endl;//BST為空
        return false;
    }
    BSTNode * curr = bstroot;
    BSTNode *parent = NULL;
    while (curr != NULL)//查詢待刪除節點
    {

        if (key == curr->value)
        {
            found = true;
            break;
        }
        else 
        {
            parent = curr;
            if (key < curr->value)
                curr = curr->lchild;
            else
                curr = curr->rchild;
        }
    }
    if (!found)
    {
        std::cerr << "KeyValue Not Found" << std::endl;
        return false;
    }
    if (NULL == parent)//刪除最後一個節點(根節點需要特殊處理)
    {
        bstroot = NULL;
        delete curr;
        return true;
    }
    //對於待刪除的節點有三種可能:
    //1.葉子節點
    //2.只包含左子樹或者右子樹(單個孩子)
    //3.既包含左子樹又包含右子樹
    //刪除節點的時候需要分3種情況進行考慮

    if (__isLeaf(curr))//葉子節點
    {
        if (parent->lchild == curr)
            parent->lchild = NULL;
        else
            parent->rchild = NULL;
        delete curr;
        return true;
    }//end if
    else if (__isNodeWithTwoChild(curr))//有兩個孩子的節點
    {
        //以當前節點的右子樹中的最小值取代它
        BSTNode*parent=curr;
        BSTNode *tmp = __treeMin(curr->rchild,parent);
        curr->value = tmp->value;
        if (parent->rchild == tmp)
            parent->rchild = NULL;
        else
            parent->lchild = NULL;
        delete tmp;
        return true;
    }//end else-if
    else//只有一個孩子的節點
    {
        if (curr->lchild != NULL)//只有左孩子
        {
            if (parent->lchild == curr)
            {
                parent->lchild = curr->lchild;
                delete curr;
                return true;
            }
            else
            {
                parent->rchild = curr->lchild;
                delete  curr;
                return true;
            }
        }
        if (curr->rchild != NULL)//只有右孩子
        {
            if (parent->lchild == curr)
            {
                parent->lchild = curr->rchild;
                delete curr;
                return true;
            }
            else
            {
                parent->rchild = curr->rchild;
                delete  curr;
                return true;
            }
        }
    }//end else
    return false;
}
//刪除操作的外部介面
bool BST::Delete(const int &key)
{
    return __Delete(key);
}

//插入節點的內部實現,插入操作一定都在葉子節點處。
bool BST::__Insert(const int & key)
{
    BSTNode* t = new BSTNode(key);//臨時節點
    BSTNode*parent = NULL;
    if (isEmpty())//新樹
    {
        bstroot = t;
        return true;
    }
    else
    {
        BSTNode* curr;
        curr = bstroot;
        while (curr)
        {
            //插入位置都位於葉子節點處
            parent = curr;
            if (t->value > curr->value)
                curr = curr->rchild;
            else
                curr = curr->lchild;
        }
        if (t->value < parent->value)
        {
            parent->lchild = t;
            return true;
        }
        else
        {
            parent->rchild = t;
            return true;
        }
    }
    return false;
}
//插入節點的外部介面
bool BST::Insert(const int &key)
{
    return __Insert(key);
}

//建構函式
BST::BST()//預設建構函式
{
    bstroot = NULL;
}
BST::BST(int*arr, int len)//陣列構造
{
    bstroot = NULL;
    for (int i = 0; i < len; i++)
    {
        __Insert(*(arr + i));
    }
}

BST::BST(std::vector<int>arr)//容器構造
{
    bstroot = NULL;
    for (int i = 0; i < (int)arr.size(); i++)
    {
        __Insert(arr[i]);
    }
}

//內部查詢函式
//遞迴呼叫
BSTNode* BST::__search(BSTNode*root,const int& key)
{
    if (NULL == root)
        return NULL;
    if (key == root->value)
        return root;
    else if (key < root->value)
        return __search(root->lchild, key);
    else
        return __search(root->rchild, key);
}
//查詢函式介面
bool BST::search(const int& key)
{
    BSTNode*t = __search(bstroot, key);
    return t == NULL ? false : true; 
}

//中序遍歷內部實現
void BST::__InorderTraversal(BSTNode *root,std::vector<int>&result)
{
    if (NULL == root)
        return;
    __InorderTraversal(root->lchild, result);
    std::cout << root->value << " ";
    result.push_back(root->value);
    __InorderTraversal(root->rchild, result);
}
//中序遍歷介面,vector儲存遍歷結果
void BST::InorderTraversal(std::vector<int>&result)
{
    __InorderTraversal(bstroot, result);
}

//刪除所有節點(析構用)
void BST::__DeleteAllNodes(BSTNode *root)
{
    if (root == NULL)
    {
        return;
    }
    __DeleteAllNodes(root->lchild);
    __DeleteAllNodes(root->rchild);
    __Delete(root->value);
}

//解構函式
BST::~BST()
{
    BSTNode*curr = bstroot;
    __DeleteAllNodes(curr);
}

(4)二叉排序樹的測試程式碼

//main.cpp
#include "BST.h"
int main()
{
    std::vector<int>vec = { 8,6,2,5,1,3,7 };
    BST bst(vec);
    bst.Delete(9);//Not found

    bst.Insert(4);
    bool found=bst.search(4);
    if (!found)
        std::cout << "not found" << std::endl;
    else
        std::cout << "found!" << std::endl;
    std::vector<int>result;
    bst.InorderTraversal(result);
    std::cout << std::endl;
    for (int i = 0; i < result.size(); i++)
    {
        std::cout << result[i] << " ";
    }
    std::cout << std::endl;
    system("pause");
    return 0;
}

4.參考