概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3.它的左右子树也分别为二叉搜索树
搜索二叉树是不允许数据冗余的,意思就是插入相同的值只会存在一个该值
总结说就是每一个的左子树比父亲小,每一个右子树比父亲大。
在我们要查找数据时通过key值进行比较则可以找到想要的数据,所以叫做二叉搜索树
图如下:
结构
1.节点结构
template<class K> //模板 K=key关键词的意思 key值可以是任何类型(自定义类型/内置类型)
struct BSTreeNode //struct创建类默认所有的成员是public的
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
//对该节点初始化 调用K()无参构造
BSTreeNode(const K& key = K())
:_left(nullptr)
,_right(nullptr)
,_key(key)
{
}
};
2.搜索二叉树结构
template<class K>
struct BSTree
{
private:
typedef BSTreeNode<K> Node;//搜索二叉树中直接套用上面单个节点
Node* _root;//指向根节点的一个指针
public:
BSTree()
:_root(nullptr)
{}
};
搜索二叉树实现(增删查)
不能修改
增/插入
bool Insert(const K& key)
{
//如果没有数据创建新节点插入
if (_root == nullptr)
{
_root = new Node(key);//new会调用构造函数初始化
return true;
}
//如果前面有了数据,插入数据比key小去左边找,比key大去右边找
//1.比某节点大,这个节点的父节点指向插入节点,插入节点指向该节点
//2.一直找到最后了都没找到
Node* prev = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)//这里是cur去走节点
{
prev = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
prev = cur;
cur = cur->_right;
}
else
{
return false;//如果等于的话就false
}
}
//找了要插入的位置
cur = new Node(key);
if (prev->_key > key)
{
prev->_left = cur;
}
else
{
prev->_right = cur;
}
return true;
}
删
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
1.删除节点没有任何子树
2.删除节点有左子树
3.删除节点有右子树
4.删除节点有左孩子和右孩子
其中1可以和2、3一起处理,处理方法:
情况2:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
情况3:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
情况4:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
bool Erase(const K& key)
{
//先进行遍历,找到要删除的值的地址,还有其父亲节点的地址
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key>key)
{
//key小于root去左边去找
parent = cur;
cur = cur->_left;
}
else if (cur->_key<key)
{
//key值大于root的key值,去右边找
parent = cur;
cur = cur->_right;
}
else
{
//找到和key值相等的key的地址,开始删除
//parent是该节点的父节点,cur是该节点
if (cur->_left==nullptr)
{
//如果cur就是root根节点需要特殊处理
if(cur==_root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
//cur是父亲的左边那他的右子树变成父亲的左子树
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right==nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
else
{
//有两个孩子
Node* minparent = cur;
Node* minright = cur->_right;
//找要删除的节点的右子树的最小值也就是左子树
while (minright->_left)
{
minparent = minright;
minright = minright->_left;
}
//找到最小值
swap(minright->_key, cur->_key);
//到这说明要删除的值已经是最左边的值了,他的左边是nullptr了,但是右边有可能有值
//所以要托管给父节点
if (minparent->_left==minright)
{
minparent->_left = minright->_right;
}
else
{
minparent->_right = minright->_right;
}
delete minright;
}
return true;
}
}
//没有找到
return false;
}
查
根据特性,查找的key值小于root去左子树进行比较,大于再去右子树进行比较
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
{
return true;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
cur = cur->_left;
}
}
return false;
}
递归写法
//以下为递归式实现
bool InsertR(const K&key)
{
return insertr(_root,key);
}
bool SearchR(const K& key)
{
return searchr(_root,key);
}
bool EraseR(const K& key)
{
return eraser(_root,key);
}
private:
void destory(Node*& root)
{
if(root==nullptr)
return ;
destory(root->_left);
destory(root->_right);
delete(root);
}
Node* Copy(Node* root)
{
if(root==nullptr)
return nullptr;
Node* newnode = new Node(root->_key);
newnode->_left=Copy(root->_left);
newnode->_right=Copy(root->_right);
return newnode;
}
void inorder(const Node* _root)
{
if (_root==nullptr)
return ;
inorder(_root->_left);
cout << _root->_key << " ";
inorder(_root->_right);
return;
}
bool eraser(Node*& root ,const K& key)
{
if(root==nullptr)
return false;
if(root->_key<key)
{
return eraser(root->_right,key);
}else if(root->_key>key)
{
return eraser(root->_left,key);
}else
{
//等于key做处理
Node* del =root;
if(root->_left==nullptr)
{
root=root->_right;
}else if(root->_right==nullptr)
{
root=_root->_left;
}else
{
//该节点两边都有子树
Node* minleft = root->_right;
while(minleft->_left)
{
minleft = minleft->_left;
}
swap(minleft->_key,root->_key);
//转换成去处理子树删除
return eraser(root->_right,key);
}
delete(del);
return true;
}
}
bool insertr(Node*& root,const K&key)
{
if(root==nullptr)
{
root = new Node(key);
return true;
}
if(root->_key<key)
{
return insertr(root->_right,key);
}else if(root->_key>key)
{
return insertr(root->_left,key);
}else
{
return false;
}
}
bool searchr(Node*& root,const K&key)
{
if(root==nullptr)
return false;
if(root->_key>key)
return searchr(root->_left,key);
else if(root->_key<key)
return searchr(root->_right,key);
else
return true;
}
补充特性:
如果中序遍历搜索二叉树,则会是一个顺序的结果,所以我们也叫其为排序二叉树
中序遍历代码:
void InOrder()
{
_InOrder(_root);
}
void _InOrder(Node* root)
{
if (root==nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
时间复杂度以及缺陷
当我们查找一个数的时候,如果是左右均衡的我们最多查找高度次即可找到数据
但是如果插入的数据是顺序的,则会导致搜索二叉树变成一个单链表结构
时间复杂度就会变成O(N),所以接下来我们会再将AVL平衡二叉树,防止这种情况发生