专注前端与算法的系列干货分享,欢迎关注(¬‿¬):
「微信公众号:心谭博客」| xin-tan.com | GitHub
1. 为什么需要二叉搜索树?
选择数据结构的核心在于解决问题,而不是为了使用而使用。
由于二叉搜索树的定义和特性,它可以高效解决以下问题:
- 查找问题:二分查找
- 高级结构:字典结构实现
- 数据变动:节点的插入、删除
- 遍历问题:前序、中序、后序和层次遍历
- 数值运算:
ceil
、floor
、找到第 n 大的元素、找到指定元素在排序好的数组的位置 等等
值得一提的是,除了遍历算法,上述各种问题的算法时间复杂度都是 : O ( log 2 n ) O(\log_2 n) O(log2n)
2. 二叉搜索树的定义和性质
二叉搜索树是一颗空树,或者具有以下性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值
- 任意节点的左、右子树也分别为二叉查找树
- 没有键值相等的节点
需要注意的是,二叉搜索树不一定是一颗完全二叉树,因此,二叉搜索树不能用数组来存储。
3. 二叉搜索树的实现
第 3 部分实现的测试代码地址:https://gist.github.com/dongyuanxin/d0803a8821c6797e9ce8522a676cf44b。
这是 Github 的 GIST,请自备梯子。
3.1 树结构实现
借助struct
和指针模拟树的结构,并且将其封装到BST
这个类之中:
// BST.h
// Created by godbmw.com on 2018/9/27.
//
#ifndef BINARYSEARCH_BST_H
#define BINARYSEARCH_BST_H
#include <iostream>
#include <queue>
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 = NULL;
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() {
this->root = NULL;
this->count = 0;
}
~BST() {
this->destroy(this->root);
}
int size() {
return this->count;
}
bool isEmpty() {
return this->root == NULL;
}
};
#endif //BINARYSEARCH_BST_H
3.2 实现节点插入
插入采取递归的写法,思路如下:
- 递归到底层情况:新建节点,并且返回
- 非底层情况:如果当前键等于插入键,则更新当前节点的值;小于,进入当前节点的左子树;大于,进入当前节点的右子树。
private:
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->left = insert(node->left, key, value);
} else {
node->right =