1:二叉搜索树的定义
二叉搜索树的底层是一个二叉链表
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
举个例子,我们要把数组[50,87,64,100,24,35,1,77]插入到二叉搜索树中,对应的树应该是这个样子
2.二叉搜索树的特点
- 既然以搜索树为名字,显然搜索功能是很强大的(相较于数组的搜索),对于二叉树的每一个节点来说其左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,如果我们要查找一个值,该值比根节点小的话去左子树找,比根节点大的话去右子树找,如果二叉搜索树是一颗满二叉树的话,搜索的时间复杂度将为log(N),相当于从全世界70亿人中找一个人仅用了30次操作.
- 因为二叉树的左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,所以其中序遍历即为升序数组,搜索树理论上不可以同时存在多个相同的元素,因为这是没有意义的,所以严格来说,这是一个无重复元素的升序数组
3.二叉搜索树的底层原理
插入
显然原树在执行完插入操作后仍应该是二叉搜索树,为了不破坏原树的结构,对于根节点来说,如果插入值大于根节点,应该往右插入,插入值小于根节点,应该往左插入.直到最后1.找到的节点为空,代表应该向空节点处插入2.存在节点值 == 插入值,这样的插入是无意义的,插入失败!
以上图为例,如果要插入一个76
模拟实现
/* template <class T>
//二叉搜索的节点
struct TreeNode {
typedef TreeNode<T> Node;
Node* _left;
Node* _right;
T _val;
//构造函数
TreeNode(T val = T())
:_left(nullptr),
_right(nullptr),
_val(val)
{}
};
*/
bool insert(T val) {
if (_root == nullptr)
{
_root = new Node(val);
}
Node* child = _root;
Node* parent = _root;
while (child != nullptr) {
parent = child;
if (val < child->_val) //val比根节点小,往左走
{
child = child->_left;
}
else if (val > child->_val) //val比根节点大,往右走
{
child = child->_right;
}
else {
return false; //val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
}
}
if (parent->_val < val)
parent->_right = new Node(val);
else if (parent->_val > val)
parent->_left = new Node(val);
return true; //插入成功
}
查找
查找与插入很类似,大于的话去右边找,小于的话去左边找,找到了返回节点,没找到返回nullptr
以找64为例
模拟实现
/* template <class T>
//二叉搜索的节点
struct TreeNode {
typedef TreeNode<T> Node;
Node* _left;
Node* _right;
T _val;
//构造函数
TreeNode(T val = T())
:_left(nullptr),
_right(nullptr),
_val(val)
{}
};
*/
Node* find(T val) {
Node* tmp = _root;
while (tmp != nullptr) {
if (val == tmp->_val)
return tmp; //找到啦
else if (val > tmp->val) {
tmp = tmp->_right; //val大于该节点的值,去右子树找
}
else {
tmp = tmp->_left; //val小于该节点的值,去左子树找
}
}
return nullptr; //树里没有val,不存在该节点
}
删除
这边我们重画一个比较大一些的搜索二叉树以便于举例,数据很简单,1到16
首先,当删去8时,其余的所有节点都仍旧符合搜索二叉树的定义,删去其他叶子节点有相同的效果,同理,在删去2节点时,理论上来说仅有2的子树受到影响,搜索树的其他部分仍旧符合定义.
再以删去九号节点为例,由于二叉树的定义,删去九号节点后新节点的值必须满足除自身外,左子树都小于其,右子树都大于其.
由上图为例,对二叉树进行分析可得:除8外的左树<8<9<10<除10外的右树,也就是说:
左子树的右边的右边的右边的......的叶节点和右子树左边的左边的.......的叶节点最适合做根节点
特殊情况1:左子树没有右节点
使用左子树自身的根节点即可.
特殊情况2:没有左子树&&只有一个孩子节点
上图为删除节点4的示意图,可以看出,4号节点没有左子树,此时4号节点仅有一个子节点,直接让子节点代替自己即可
模拟实现
bool earse(const T& val) {
Node* parent = _root;
Node* child = _root;
while (child!= nullptr) {
if (val > child->_val)
{
parent = child;
child = child->_right;
}
else if (val < child->_val){
parent = child;
child = child->_left;
}
else //相等
{
if (child->_left == nullptr)
{
if (child == _root)
_root = child->_right;
else {
if (parent->_left == child) parent->_left = child->_right;
else if (parent->_right == child) parent->_right = child->_right;
}
delete child;
}
else if (child->_right == nullptr) {
if (child == _root)
_root = child->_left;
else {
if (parent->_left == child) parent->_left = child->_left;
else if (parent->_right == child) parent->_right = child->_left;
}
delete child;
}
else {
Node* tmp = child->_left;
Node* pp = tmp;
while (tmp->_right) {
pp = tmp;
tmp = tmp->_right;
}
child->_val = tmp->_val;
if (pp != tmp)
pp->_right = tmp->_left;
delete tmp;
}
return true;
}
}
return false;
}
高度(不重要)
public:
size_t height() {
return _height(_root);
}
private:
size_t _height(Node* root) {
if (root == nullptr)
return 0;
else
return _height(root->_left) + _height(root->_right) + 1;
}
节点个数(不重要)
public:
size_t size() {
return _size(_root);
}
private:
size_t _size(Node* root) {
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
打印(不重要)
void print() {
std::queue<Node*> q1,q2;
q1.push(_root);
while (!q1.empty() || !q2.empty()) {
while (!q1.empty()) {
if (q1.front() == nullptr)
std::cout << '#' << ' ';
else {
q2.push(q1.front()->_left);
q2.push(q1.front()->_right);
std::cout << q1.front()->_val << ' ';
}
q1.pop();
}
std::swap(q1,q2);
std::cout << std::endl;
}
}
搜索二叉树模拟实现
#include <queue>
#include <iostream>
namespace SearchTree {
template <class T>
//二叉搜索的节点
struct TreeNode {
typedef TreeNode<T> Node;
Node* _left;
Node* _right;
T _val;
//构造函数
TreeNode(T val = T())
:_left(nullptr),
_right(nullptr),
_val(val)
{}
};
template <class T>
class SearchTree {
typedef TreeNode<T> Node;
Node* _root; //根节点
public:
SearchTree():_root(nullptr){} //构造函数
bool insert(T val) {
if (_root == nullptr)
{
_root = new Node(val);
}
Node* child = _root;
Node* parent = _root;
while (child != nullptr) {
parent = child;
if (val < child->_val) //val比根节点小,往左走
{
child = child->_left;
}
else if (val > child->_val) //val比根节点大,往右走
{
child = child->_right;
}
else {
return false; //val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
}
}
if (parent->_val < val)
parent->_right = new Node(val);
else if (parent->_val > val)
parent->_left = new Node(val);
return true; //插入成功
}
bool earse(const T& val) {
Node* parent = _root;
Node* child = _root;
while (child!= nullptr) {
if (val > child->_val)
{
parent = child;
child = child->_right;
}
else if (val < child->_val){
parent = child;
child = child->_left;
}
else //相等
{
if (child->_left == nullptr)
{
if (child == _root)
_root = child->_right;
else {
if (parent->_left == child) parent->_left = child->_right;
else if (parent->_right == child) parent->_right = child->_right;
}
delete child;
}
else if (child->_right == nullptr) {
if (child == _root)
_root = child->_left;
else {
if (parent->_left == child) parent->_left = child->_left;
else if (parent->_right == child) parent->_right = child->_left;
}
delete child;
}
else {
Node* tmp = child->_left;
Node* pp = tmp;
while (tmp->_right) {
pp = tmp;
tmp = tmp->_right;
}
child->_val = tmp->_val;
if (pp != tmp)
pp->_right = tmp->_left;
delete tmp;
}
return true;
}
}
return false;
}
Node* find(T val) {
Node* tmp = _root;
while (tmp != nullptr) {
if (val == tmp->_val)
return tmp; //找到啦
else if (val > tmp->val) {
tmp = tmp->_right; //val大于该节点的值,去右子树找
}
else {
tmp = tmp->_left; //val小于该节点的值,去左子树找
}
}
return nullptr; //树里没有val,不存在该节点
}
void print() {
std::queue<Node*> q1,q2;
q1.push(_root);
while (!q1.empty() || !q2.empty()) {
while (!q1.empty()) {
if (q1.front() == nullptr)
std::cout << '#' << ' ';
else {
q2.push(q1.front()->_left);
q2.push(q1.front()->_right);
std::cout << q1.front()->_val << ' ';
}
q1.pop();
}
std::swap(q1,q2);
std::cout << std::endl;
}
}
size_t size() {
return _size(_root);
}
size_t height() {
return _height(_root);
}
private:
size_t _size(Node* root) {
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
size_t _height(Node* root) {
if (root == nullptr)
return 0;
else
return std::max(_height(root->_left) , _height(root->_right)) + 1;
}
};
}
对搜索二叉树的分析及AVL树红黑树的优势
参考我在前文查找处的分析可得,查找的时间复杂度与树的高度,空间复杂度恒为o(1),当插入从a到b的值是,优先输入[a,b]中间的值,后输入接近a,b的极端值,此时树较为平衡,查找的时间复杂度接近o(logn),而当数据有序的进行插入时,树相当于一个链表,时间复杂度较高,为(o(n))
举个例子,当把数据[1,2,3,4,5,6,7]插入二叉树时
以[4,2,1,3,6,5,7]插入时,如果我们要查找7,需要进行三次判断即可,
以[1,2,3,4,5,6,7]插入时,如果我们要查找7,需要进行七次判断!!!!时间复杂度为o(n),再加上额外的空间开销,还不如直接在原数组中查找
如果有一种改进的插入方式可以在插入时,依据原树的高度差进行动态的重构树结构,便可大大加快查找速度
附:AVL树模拟实现(供参考)
#include <queue>
#include <iostream>
#include <assert.h>
namespace AVL {
template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data = T())
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _bf(0)
{}
AVLTreeNode<T>* _pLeft;
AVLTreeNode<T>* _pRight;
AVLTreeNode<T>* _pParent;
T _data;
int _bf; // 节点的平衡因子
};
// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{
typedef AVLTreeNode<T> Node;
public:
AVLTree()
: _pRoot(nullptr)
{}
void print() {
std::queue<Node*>q1;
std::queue<Node*>q2;
q1.push(_pRoot);
while (!q1.empty() || !q2.empty()) {
while (!q1.empty())
{
if (q1.front() != nullptr) {
std::cout << q1.front()->_data << ' ';
q2.push(q1.front()->_pLeft);
q2.push(q1.front()->_pRight);
}
else
std::cout << '#' << ' ';
q1.pop();
}
swap(q1, q2);
std::cout << std::endl;
}
}
// 在AVL树中插入值为data的节点
bool Insert(const T& data) {
Node* node = new Node(data);
if (_pRoot == nullptr) {
_pRoot = node;
return true;
}
Node* parent = _pRoot,*child = _pRoot;
while (child) {
parent = child;
if (child->_data > data) child = child->_pLeft;
else if (child->_data < data) child = child->_pRight;
else return false;
}
if (parent->_data < data)
{
parent->_pRight = node;
}
else
{
parent->_pLeft = node;
}
node->_pParent = parent;
while (parent) {
if (node == parent->_pLeft)
parent->_bf--;
else
parent->_bf++;
if (parent->_bf == 0)
break;
else if (parent->_bf == 1 || parent->_bf == -1)
node = parent, parent = parent->_pParent;
else { //出问题了
if (parent->_bf == -2 && node->_bf == -1) {
RotateR(node);
}
else if (parent->_bf == -2 && node->_bf == 1)
{
RotateLR(node);
}
else if (parent->_bf == 2 && node->_bf == 1) {
RotateL(parent);
}
else if (parent->_bf == -2 && node->_bf == -1) {
RotateRL(node);
}
else {
// 树损坏
}
}
}
return true;
}
// AVL树的验证
bool IsAVLTree()
{
return _IsAVLTree(_pRoot);
}
private:
// 根据AVL树的概念验证pRoot是否为有效的AVL树
bool _IsAVLTree(Node* pRoot) {
std::queue<Node*> q;
q.push(pRoot);
while (!q.empty()) {
if (q.front() == nullptr)
{
q.pop();
continue;
}
if (q.front()->_bf >= 2 || q.front()->_bf <= -2)
{
return false;
}
else {
q.push(q.front()->_pLeft);
q.push(q.front()->_pRight);
q.pop();
}
}
return true;
}
size_t _Height(Node* pRoot) {
size_t h = 0;
std::queue<Node*> q1;
std::queue<Node*> q2;
q2.push(pRoot);
while (!q1.empty()&&!q2.empty()) {
while (!q1.empty())
{
if (q1.front() != nullptr)
{
q2.push(q1.front()->_pLeft);
q2.push(q1.front()->_pRight);
}
q1.pop();
}
std::swap(q1, q2);
h++;
}
q1.empty();
q2.empty();
return h - 1;
}
// 左单旋
void RotateL(Node* pParent) {
//新的头节点
Node* new_Parent = pParent->_pRight;
//旧头结点的父节点
Node* grand = pParent->_pParent;
//修改pParent的父节点
if (grand != nullptr)
{
if (grand->_pLeft == pParent)
grand->_pLeft = new_Parent;
else
grand->_pRight = new_Parent;
}
else {
_pRoot = new_Parent;
}
// 修改pParent节点
pParent->_pParent = new_Parent;
pParent->_pRight = new_Parent->_pLeft;
//修改new_Parent节点
if(new_Parent->_pLeft!=nullptr)
new_Parent->_pLeft->_pParent = pParent;
new_Parent->_pLeft = pParent;
new_Parent->_pParent = grand;
pParent->_pParent = new_Parent;
pParent->_bf = new_Parent->_bf = 0;
}
// 右单旋
void RotateR(Node* pParent) {
//新的头节点
Node* new_Parent = pParent->_pLeft;
//旧头结点的父节点
Node* grand = pParent->_pParent;
//修改pParent的父节点
if (grand != nullptr)
{
if (grand->_pLeft == pParent)
grand->_pLeft = new_Parent;
else
grand->_pRight = new_Parent;
}
else {
_pRoot = new_Parent;
}
// 修改pParent节点
pParent->_pParent = new_Parent;
pParent->_pLeft = new_Parent->_pRight;
//修改new_Parent节点
if(new_Parent->_pRight!=nullptr)
new_Parent->_pRight->_pParent = pParent;
new_Parent->_pRight = pParent;
new_Parent->_pParent = grand;
pParent->_pParent = new_Parent;
pParent->_bf = new_Parent->_bf = 0;
}
// 右左双旋
void RotateRL(Node* pParent) {
RotateR(pParent);
RotateL(pParent->_pParent->_pParent);
}
// 左右双旋
void RotateLR(Node* pParent) {
RotateL(pParent);
RotateR(pParent->_pParent->_pParent);
}
protected:
Node* _pRoot;
};
}
结语
截止至结语,本文已有接近1万字,制作不易可以留个免费的赞吗