二分搜索树这种数据结构主要是用于查找功能的,在介绍二分搜索树之前先简单介绍下二分搜索,对于一个有序数组,首先找到中间元素v,如果等于那就太好了,如果比v小,那么就从小于v的部分继续查找,反之亦然。那么这个过程的时间复杂度就是 O ( l o g n ) O(logn) O(logn)。
二分搜索树主要是用于查找表的实现,也称为字典数据结构。就是每个数据有Key值和Value值,通过对Key值得搜索得到value值。如果使用数组实现查找表,首先可能存在key值非数字,无法排序的情况,其次如果查找表元素比较稀疏,使用数组可能并不经济。而且对比数组,二分搜索树在查找、插入、删除元素动态操作时有更大的优势。
二叉搜索树的定义
二叉搜索树仍然是一棵树,其中每个节点的key值大于左节点,小于右节点。而且左右节点为根的二叉树依然为二叉搜索树。由此可以看出,二叉搜索树具有一种递归的天然属性,后面也会利用这种特性实现相关功能。同时二叉搜索树不要求是完全二叉树。
#include <iostream>
using namespace std;
template <typename Key, typename Value>
class BST{
private:
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* root; //搜索树的根节点
int count; //搜索树中元素数
public:
BST() {root = NULL; count = 0;}
~BST() {}
int size() {return count;}
bool isEmpty() {return count==0;}
}
插入元素
插入元素时,如果key值大于根节点的key值,那么应该将新元素插入到根节点右侧的子树中,此时继续跟子树中的根节点比较,一直到最后一个节点,将元素插入到合适位置。如果相等的话则更新value值。
public:
void insert(Key key, Value value){
root = insert(root,key,value);
}
private:
Node* insert(Node* node, Key key, Value value){
if(node==NULL){
return new Node(key, value);
count++;
}
if(key > node->key)
node->right = insert(node->right, key, value);
else if(key == node->key)
node->value = value;
else
node->left = insert(node->left, key, value);
return node;
}
查找元素
原理和插入元素一样,仍然采用递归的方法。
public:
Value* search(Key key){ //使用value*主要是为了如果元素不存在可以返回空
return search(root, key);
}
private:
Value* search(Node* node, Key key){
if(node == NULL)
return NULL;
if(key == node->key)
return &(node->value);
else if(key > node->key)
return search(node->right, key);
else
return search(node->left, key);
}
前中后序遍历
前序遍历:先访问当前节点,再依次递归访问左右子树;
中序遍历:先递归访问左子树,再访问自身,再递归访问右子树;
后序遍历:先递归访问左右子树,再访问自身。
public:
void preOrder(){
preOrder(root);
}
void inOrder(){
inOrder(root);
}
void postOrder(){
postOrder(root);
}
private:
void preOrder(Node* node){
if(node!=NULL){
cout<<node->value<<endl;
preOrder(node->left);
preOrder(node->right);
}
void inOrder(Node* node){
if(node!=NULL){
inOrder(node->left);
cout<<node->value<<endl;
inOrder(node->right);
}
void postOrder(Node* node){
if(node!=NULL){
postOrder(node->left);
postOrder(node->right);
cout<<node->value<<endl;
}
}
层序遍历
层序遍历是将二叉树按照每层的顺序依次遍历,这边需要采用队列结构,先将根节点推入。如果队列不为空,那就将节点依次取出,同时推入取出节点的左右子节点。
#include <queue>
public:
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);
}
}
最大值、最小值的查找和删除
根据二叉搜索树的性质可以发现,一直沿着左节点向下查找,直至没有左节点,那么对应节点就是二叉搜索树的最小值。对于最大值则需要沿着右节点查找。
如果想要删除最小值,可以分为两种情况,如果最小值对应节点没有子节点,那么直接删除就好了。而如果有右节点,那么只要将右节点覆盖原来的最小值节点即可。但是其实这两种情况可以看做同一种情况,因为如果没有子节点,那么使用右节点覆盖也是一个空指针,所以两种情况可以统一起来。
public:
Key minimum(){
Node* minNode = minimum(root);
return minNode->key;
}
Key maximum(){
Node* maxNode = maximum(root);
return maxNode->key;
}
void removeMin(){
if(root)
root = removeMin(root);
}
void removeMax(){
if(root)
root = removeMax(root);
}
private:
//以node为根的二叉搜索树中返回最小键值节点
Node* minimum(Node* node){
if(node->left == NULL)
return node;
return minimum(node->left);
}
Node* maximum(Node* node){
if(node->right == NULL)
return node;
return maximum(node->right);
}
//删除以node为根的键值最小节点
//返回删除节点后的新的二分搜索树的根
Node* removeMin(Node* node){
if(node->left == NULL){
rightNode = node->right;
delete node;
count--;
return rightNode;
}
node->left = removeMin(node->left);
return node;
}
Node* removeMax(Node* node){
if(node->right == NULL){
leftNode = node->left;
delete node;
count--;
return leftNode;
}
node->right = removeMax(node->right);
return node;
}
删除节点
删除节点应该是二叉搜索树操作中最为复杂的操作了。对比删除最大值最小值的情况,如果删除节点只有一个子节点,那么只需要将子节点覆盖删除节点即可。但是如果有两个子节点,就稍微麻烦一点。因为为了满足二叉搜索树的规定,必须要找到一个满足大于左子树所有元素同时小于右子树所有元素的节点,那么观察可以发现删除节点右子树中的最小值和左子树中的最大值是满足这个情况的。所以只要将其中一个覆盖删除节点并成功挂接起原来的左右子树就行了。
public:
void remove(Key key){
root = remove(root, key);
}
private:
Node* remove(Node* node, Key key){
if(node == NULL)
return NULL;
if(key > node->key){
node->right = remove(node->right, key);
return node;
}
else if(key < node->key){
node->left = remove(node->left, key);
return node;
}
else{
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 left;
}
Node* successor = new Node minimum(node->right);
successor->right = removeMin(node->right);
//因为在删除时successor指向就不存在了,
//所以在创建successor时使用了复制构造函数重新建立节点
successor->left = node->left;
delete node;
return successor;
}
}
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;
}
}