理想情況下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