C++數據結構: 二叉搜索樹 (非遞歸)

理想情況下BST的插入刪除查找花費的時間都是對數級別的,但我們肯定不能依賴“理想情況”。基於下面3個理由:
1. 很多時候輸入得序列都是有序或基本有序的
2. 訪問一個節點過後很可能再次訪問這個節點,或者這個節點附近的節點。
3. 多次刪除過後會破壞樹的平衡性(可以改變一下刪除方式來解決,但是這樣的代價比較大)

而BST在最差的請況下會退化成一個鏈表,這顯然不是我們想要的結果。所以感覺BST的實際效用不大!

之前寫BST是用的Java的遞歸算法,這次用C++實現,我就選擇了非遞歸算法。非遞歸實現的原理不難,但實現起來總有很多細節需要注意。特別是對於C++!

實現簡述:
1. 先說說插入吧,插入的關鍵(至少我認爲)是找到插入位置的父節點。就和鏈表一樣,不是嗎?所以,要插入一個節點,實際上是要操作該節點的父節點。
2. 刪除實現起來比插入麻煩多了,3種情況:

a. 要刪除的節點沒有子節點。
b. 要刪除的節點只有一個子節點
c. 要刪除的節點有兩個子節點

想象一下前兩種情況,這不就是鏈表的刪除操作嗎?!!!
同插入操作一樣,要刪除一個節點,我們實際上是要操作該節點的父節點。需要注意的是,根節點沒有父親(鏈表頭結點也沒有前驅),所以實現的時候要考慮根節點的情況。

最後一種情況,要刪除一個擁有2個子樹的節點比較複雜。轉換一下思路,用該節點的後繼(或前驅)替換該節點,然後刪除該節點的後繼(或前驅)。

3.拷貝構造函數,這是我覺得我寫的最好的一個函數。用的是遞歸的方式。註釋裏有說明。

#ifndef BST_H
#define BST_H

#include <iostream>
using namespace std;

template <typename K, typename V>
class BSTNode//節點類
{
public:
    K key;
    V value;
    BSTNode *left;
    BSTNode *right;

    BSTNode(const K &k, const V &v, BSTNode *lft = nullptr, BSTNode *rht = nullptr) :
        key(k), value(v), left(lft), right(rht) {}

};



template <typename K, typename V>
class BST
{
public:
    BST() :size(0), root(nullptr) {}
    BST(const BST &b);
    //析構函數,逆序刪除BST
    ~BST()
    {
        postOrder(deleteNode);
        root = nullptr;
        size = 0;
    }

    bool isEmpty() const { return size == 0; };
    unsigned getSize() const { return size; }

    void insert(const K &k, const V &v);
    bool remove(const K &k);
    BSTNode<K, V> *get(const K &k);
    bool set(const K &k, const V &v);

    BSTNode<K, V>* findMin(BSTNode<K, V> *t);//返回以t爲根節點的BST中的最小元素

    void postPrint();//後序遍歷打印
    void postOrder(void(*visit)(BSTNode<K, V>*))//以visit方式後序遍歷BST,是另一個postOrder的驅動程序
    {
        this->visit = visit;
        postOrder(root);
    }

protected:
    BSTNode<K, V> *root;//根節點
    unsigned int size;//BST中的節點數量

    void postOrder(BSTNode<K, V> *t);//後序遍歷,實現函數

    void(*visit)(BSTNode<K, V>*);      //函數指針,結合遍歷對樹進行操作。

    static void deleteNode(BSTNode<K, V> *t)
    {delete t;}

    static void output(BSTNode<K, V>* t)
    {cout << t->value << ' ';}

    BSTNode<K, V> *createNode(BSTNode<K, V> *t);//配合下一個函數使用,創建新節點
    void copyNode(BSTNode<K, V> *father, BSTNode<K, V> *t);//拷貝構造函數用來拷貝一個節點

};



template<typename K, typename V>
BST<K, V>::BST(const BST &b):size(0), root(nullptr)
{
    if (!b.isEmpty())
    {
        size = b.size;
        root = new BSTNode<K, V>(b.root->key, b.root->value, nullptr, nullptr);
        copyNode(root, b.root);
    }
}

template<typename K, typename V>//O(N)
void BST<K, V>::insert(const K & k, const V & v)
{
    if (root == nullptr)
        root = new BSTNode<K, V>(k, v, nullptr, nullptr);
    else {//尋找正確的位置,以及該位置的父節點
        BSTNode<K, V> *t = root, *father = nullptr;
        while (t != nullptr)
        {
            father = t;//t節點的父節點
            if (k < t->key)
                t = t->left;
            else if (k > t->key)
                t = t->right;
            else {
                t->value = v;//覆蓋
                return;//小心別陷入死循環,並且這裏size不遞增
            }
        }
        BSTNode<K, V> *newNode = new BSTNode<K, V>(k, v, nullptr, nullptr);
        if (k < father->key)//添加節點
            father->left = newNode;
        else
            father->right = newNode;
    }
    ++size;
}

template<typename K, typename V>//O(N)
BSTNode<K, V>* BST<K, V>::findMin(BSTNode<K, V>* t)
{
    if (t == nullptr)
        return nullptr;

    while (t->left != nullptr)
        t = t->left;

    return t;
}

template<typename K, typename V>//O(N)
bool BST<K, V>::remove(const K & k)
{
    BSTNode<K, V> *t = root, *father = nullptr;
    while (t != nullptr)
    {
        father = t;//t節點的父節點
        if (k < t->key)
            t = t->left;
        else if (k > t->key)
            t = t->right;

        if (t != nullptr && k == t->key)//注意這裏t可能爲nullptr
            if (t->left == nullptr || t->right == nullptr)
            {
                //newSon爲t的非空子樹,father爲t的父親。newSon將接替t成爲father的子樹。
                BSTNode<K, V> *newSon = (t->left == nullptr ? t->right : t->left);
                if (k == root->key)
                    root = newSon;//root特殊,相當於鏈表的頭結點,此時它的father就是它。
                else if (t->key > father->key)
                    father->right = newSon;
                else if (t->key < father->key)
                    father->left = newSon;
                delete t;
                --size;
                return true;//刪除成功
            }
            else {
                K minNodeKey = findMin(t->right)->key;
                V minNodeVal = findMin(t->right)->value;
                bool flag = remove(minNodeKey);//先刪除,再賦值
                t->key = minNodeKey;
                t->value = minNodeVal;
                return flag;//刪除成功
            }
    }
    return false;//沒有元素k,刪除失敗
}

template<typename K, typename V>//O(N)
BSTNode<K, V> *BST<K, V>::get(const K & k)
{
    BSTNode<K, V> *t = root;
    while (t != nullptr && t->key != k)
    {
        if (k < t->key)
            t = t->left;
        else if (k > t->key)
            t = t->right;
    }
    return t;
}



template<typename K, typename V>//O(N)設置成功返回true,否則false.
bool BST<K, V>::set(const K &k, const V &v)
{
    BSTNode<K, V> *t = root;
    while (t != nullptr && t->key != k)
    {
        if (k < t->key)
            t = t->left;
        else if (k > t->key)
            t = t->right;
    }

    if (t != nullptr)
    {
        t->value = v;
        return true;
    }
    return false;
}



template<typename K, typename V>//O(N)
void BST<K, V>::postPrint()
{
    if (!isEmpty())
    {
        postOrder(output);
        cout << endl;
    }
    else
        cout << "Empty Tree!" << endl;
}

template<typename K, typename V>//O(N)逆序以visit方式訪問
void BST<K, V>::postOrder(BSTNode<K, V> *t)
{
    if (t != nullptr)
    {
        postOrder(t->left);
        postOrder(t->right);
        visit(t);
    }
}

template<typename K, typename V>//創建一個沒有子樹的新節點。
BSTNode<K, V> *BST<K, V>::createNode(BSTNode<K, V> *t)
{
    if (t == nullptr)
        return nullptr;

    return new BSTNode<K, V>(t->key, t->value, nullptr, nullptr);
}

template<typename K, typename V>//O(N)遞歸拷貝
void BST<K, V>::copyNode(BSTNode<K, V> *father, BSTNode<K, V> *t)
{
    if (t == nullptr)//基準情況,確保拷貝空樹或者訪問空節點的情況下能返回。
        return;

    BSTNode<K, V> *leftChild = createNode(t->left);//1.創建子節點的數據域
    BSTNode<K, V> *rightChild = createNode(t->right);

    father->left = leftChild;//2.創建父節點的子節點
    father->right = rightChild;

    copyNode(leftChild, t->left);//3.遞歸。本輪中的子節點將成爲下一輪的父節點,然後重複步驟1、2
    copyNode(rightChild, t->right);
}




#endif

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章