一.特性
1.順序性
二分搜尋樹可以當做查詢表的一種實現。
我們使用二分搜尋樹的目的是透過查詢 key 馬上得到 value。minimum、maximum、successor(後繼)、predecessor(前驅)、floor(地板)、ceil(天花板、rank(排名第幾的元素)、select(排名第n的元素是誰)這些都是二分搜尋樹順序性的表現。
2.侷限性
二分搜尋樹在時間效能上是具有侷限性的。
如下圖所示,元素節點一樣,組成兩種不同的二分搜尋樹,都是滿足定義的:
二叉搜尋樹可能退化成連結串列,相應的,二叉搜尋樹的查詢操作是和這棵樹的高度相關的,而此時這顆樹的高度就是這顆樹的節點數 n,同時二叉搜尋樹相應的演算法全部退化成 O(n) 級別。
二.完整程式碼
前面我們將了二分搜尋樹元素的插入、查詢、遍歷刪除等,我將完整的原始碼放在這裡了:
#include <iostream>
#include <queue>
#include <cassert>
using namespace std;
//套用模板函式
template <typename Key, typename Value>
class BST {
private:
//構造節點Node
struct Node {
Key key;
Value value;
Node *left;
Node *right;
Node(Key key, Value value) {
this->key = key;
this->value = value;
//初始值為空
this->left = this->right = NULL;
}
Node(Node *node){
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
}
};
//根節點
Node *root;
//節點數量
int count;
public:
BST() {
//初始值為空
root = NULL;
count = 0;
}
~BST() {
distroy(root);
}
int size() {
return count;
}
bool isEmpty() {
return count == 0;
}
//插入操作
void insert(Key key, Value value) {
//向根節點中插入key, value
root = insert(root, key, value);
}
//在樹中尋找是否存在key
bool contain(Key key) {
return contain(root, key);
}
//找到key相應的節點並且返回value的地址
Node *seacher(Key key, Value value) {
return seacher(root, key, value);
}
//前序遍歷,傳入節點,列印節點相應資訊
void preOrder() {
return preOrder(root);
}
//中序遍歷,以節點為node的節點為根節點
void inOrder() {
return inOrder(root);
}
//後序遍歷,以node為根節點的二分搜尋樹進行前序遍歷,列印節點相應資訊
void postOrder() {
return postOrder(root);
}
//層序遍歷
void levelOrder(){
queue<Node*> q;
q.push(root);
//佇列不為空的情況
while(!q.empty()){
Node *node = q.front();
q.pop();
cout<<node->key<<endl;
if(node->left)
q.push(node->left);
if(node->right)
q.push(node->right);
}
}
// 尋找二分搜尋樹的最小的鍵值
Node* minmum(){
assert(count != 0);
Node* minnode = minmum(root);
return minnode->left;
}
// 尋找二分搜尋樹的最大的鍵值
Node* maxmum(){
assert(count != 0);
Node* maxnode = maxmum(root);
return maxnode ->right;
}
//刪除最小值的node
void removeMin(){
if(root)
root = removeMin(root);
}
//刪除最大值的node
void removeMax(){
if(root)
root = removeMax(root);
}
//刪除二分搜尋樹中值的任意節點
void remove( Key key){
root = remove(root, key);
}
private:
//插入操作
//向以node為根節點的二分搜尋樹中,插入節點(key,value),使用遞迴演算法 //返回插入新節點後的二分搜尋樹的根
Node *insert(Node *node, Key key, Value value) {
if (node == NULL) {
count++;
return new Node(key, value);
}
if (key == node->key) {
node->value = value;
}
else if (key > node->key) {
node->right = insert(node->right, key, value);
}
else //key < node->key
node->left = insert(node->left, key, value);
}
//在二分搜尋樹中查詢key,存在返回trun不存在返回false
bool contain(Node *node, Key key) {
//元素不存在
if (key == NULL)
return false;
//元素存在
if (key == node->key)
return true;
else if (key > node->key)
return contain(node->right, key);
else return contain(node->left, key);
}
//在二分搜尋樹中找到相應元素並返回該元素的地址
Value *seacher(Node *node, Key key) {
if (key == NULL)
return NULL;
//找到key 返回value的地址
if (key == node->key)
return &(node->value);
else if (key > node->key)
return seacher(node->right, key);
else
return seacher(node->left, key);
}
//前序遍歷,以node為根節點的二分搜尋樹進行前序遍歷,列印節點相應資訊
void preOrder(Node *node) {
if (node != NULL) {
//不一定用列印,還可以對node->key和node->value進行操作
cout << node->key << endl;
preOrder(node->left);
preOrder(node->right);
}
}
//中序遍歷,以node為根節點的二分搜尋樹進行前序遍歷,列印節點相應資訊
void inOrder(Node *node) {
if (node != NULL) {
inOrder(node->left);
cout << node->key << endl;
inOrder(node->right);
}
}
//後序遍歷,以node為根節點的二分搜尋樹進行前序遍歷,列印節點相應資訊
void postOrder(Node *node) {
if (node != NULL) {
postOrder(node->left);
postOrder(node->right);
cout << node->key << endl;
}
}
//解構函式的實現,其本質是後序遍歷
void distroy(Node *node) {
if (node != NULL) {
distroy(node->left);
distroy(node->right);
delete node;
count--;
}
}
// 尋找二分搜尋樹的最小的鍵值
Node* minmum(Node* node){
if(node != NULL){
minmum(node->left);
}
return node;
}
// 尋找二分搜尋樹的最大的鍵值
Node* maxmum(Node* node){
if(node != NULL){
maxmum(node->right);
}
return node;
}
// 從二分搜尋樹中刪除最小值所在的節點
Node* removeMin(Node* node){
if(node->left == NULL){
Node* NODE = node->right;
delete node;
count--;
return NODE;
}
node->left = removeMax(node->left);
return node;
}
//從二分搜尋樹中刪除最大值所在的節點
Node* removeMax(Node* node){
if(node->right == NULL){
Node* NODE = node->left;
delete node;
count--;
return NODE;
}
node->right = removeMax(node->right);
return node;
}
//刪除二分搜尋樹中值的任意節點
Node* remove(Node* node, Key key){
//判斷node是否為空
if(node == NULL) {
return NULL;
}
//先找到需要刪除的值的node
else if(key < node->key) {
node->left = remove(node->left, key);
return node;
}
else if(key > node->key) {
node->right = remove(node->right, key);
return node;
}
//這裡就找到了需要delete的node
else { //key == node->key)
// 待刪除節點左子樹為空的情況
if(node->left == NULL){
Node* rightNode = node->right;
delete node;
count--;
return rightNode;
}
// 待刪除節點右子樹為空的情況
if(node->right == NULL){
Node* leftNode = node->left;
delete node;
count--;
return leftNode;
}
// 待刪除節點左右子樹都不為為空的情況
Node *succeer =new Node(minmum(node->right)); //找到最小key值的節點返回給succeer
count ++;
succeer->right = removeMin(node->right); //將最小key值的node刪除,並將返回值給succeer的右孩子
succeer->left = node->left;
delete node;
count--;
return succeer;
}
}
};
void shuffle( int arr[], int n ){
srand( time(NULL) );
for( int i = n-1 ; i >= 0 ; i -- ){
int x = rand()%(i+1);
swap( arr[i] , arr[x] );
}
}
測試也寫在這裡了:
// 測試 remove
int main() {
srand(time(NULL));
BST<int,int> bst = BST<int,int>();
// 取n個取值範圍在[0...n)的隨機整數放進二分搜尋樹中
int n = 10000;
for( int i = 0 ; i < n ; i ++ ){
int key = rand()%n;
// 為了後續測試方便,這裡value值取和key值一樣
int value = key;
bst.insert(key,value);
}
// 注意, 由於隨機生成的資料有重複, 所以bst中的資料數量大機率是小於n的
// order陣列中存放[0...n)的所有元素 int order[n];
for( int i = 0 ; i < n ; i ++ )
order[i] = i;
// 打亂order陣列的順序
shuffle( order , n );
// 亂序刪除[0...n)範圍裡的所有元素
for( int i = 0 ; i < n ; i ++ )
if( bst.contain( order[i] )){
bst.remove( order[i] );
cout<<"After remove "<<order[i]<<" size = "<<bst.size()<<endl;
}
// 最終整個二分搜尋樹應該為空
cout << bst.size() << endl;
return 0;
}
(圖片來源:慕課網bobo老師)
本作品採用《CC 協議》,轉載必須註明作者和本文連結