- 二叉搜索树的定义:
二叉搜索树(英语:Binary Search Tree),也称为二叉查找树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree)。一棵空树或者具有下列性质的二叉树就是二叉搜索树:
1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3. 任意节点的左、右子树也分别为二叉查找树;
4. 没有键值相等的节点。
- 二叉搜索树的操作:
1. 查找
例如要查找 key 为 6 的结点,那么这棵二叉搜索树的根节点不为空,且根节点的 key 值为 8;大于要查找的 key 值, 则去树的左子树查找,此时当前结点的 key 值为 3, 小于要查找的结点的 key 值,则去以当前结点的右子树去查找,此时则找到了需要查找的 key 值结点。
二叉搜索树的查找方式就像是在一张一个方向对折了 N 次的纸张上面寻找某一块区域,此时这张纸被分为了 n 个小区域,也就是2 ^ N 次方个小区域,那么寻找某一块的情况平均就是求解这个 N 的次数,也就是 O(logN)。也就是查找的时间复杂度。
但是如果这颗二叉搜索树树退化的严重,那么它就相当于是链表,那么查找某个结点事件复杂度就变为了O(n)。
2. 插入结点
例如插入结点key值为16。此时的根节点不为空,且16大于根节点的key,则直接去右子树去继续寻找插入位置,此时比对发现任然大于结点key值10, 继续同样的操作,发现大于14,则插入14的右子树,并且返回true。
3. 删除
二叉搜索树的删除分了很多场景,因为删除完某一个结点之后,还要保持二叉搜索树的性质,所以二叉搜索树的删除比较繁琐。
删除的步骤:(难点)
(1)找
首先利用二叉搜索树的查找,找到要删除的结点。
(2)删
当删除的cur结点左子树为空时:
情景一:
当前要删除的结点就是这棵树的根节点,且当前结点的左子树为空,右子树不为空时。那么删除之后它的右子树直接变为根节点。
if (cur->_left == nullptr){
if (parent == nullptr){
_root = cur->_right;
}
}
delete cur;
情景二:
当前要删除的结点非根节点,但仍然是左子树为空。
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
delete cur;
当删除的cur结点右子树为空时:
情景一:
当前要删除的结点就是这棵树的根节点,且当前结点的右子树为空,左子树不为空时。那么删除之后它的左子树直接变为根节点。
if (parent == nullptr){
_root = cur->_left;
}
delete cur;
情景二:
当前要删除的结点非根节点,但仍然是右子树为空。
if (parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
delete cur;
当要删除的结点左右结点都不为空时:
此时的情况,就是寻找右子树的最左边的那个结点,然后交换。因为此时的右子树都比cur大,但是右子树的最左子树结点是右子树中最小的,那么这么以交换刚好满足二叉搜索树的特征。最后将要删除的结点删掉。
其实这种情况也可以寻找左子树的最右节点,因为左子树比cur小,则左子树的最右节点大于任何的左子树的结点。最后将要删除的结点删掉。
{ // 如果左右都不为空,寻找cur的右子树的最左节点,与之交换,然后删除
Node* tmp = cur->_right;
Node* prev = cur;
while (tmp->_left){
prev = tmp;
tmp = tmp->_left;
}
swap(cur->_key, tmp->_key);// 交换两个结点的值
if (prev->_left == tmp)
prev->_left = nullptr;
else
prev->_right = nullptr;
delete tmp;
}
return true;
二叉搜索树的实现:
#pragma once
#include <iostream>
using namespace std;
template<class T>
struct SearchTreeNode{
SearchTreeNode<T>* _left;
SearchTreeNode<T>* _right;
T _key;
SearchTreeNode(const T& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class T>
class BinarySearchTree{
typedef SearchTreeNode<T> Node;
public:
BinarySearchTree()
:_root(nullptr)
{}
// 遍历二叉树
void _PreOrder(Node* tree){
if (tree == nullptr)
return;
cout << tree->_key << " ";
_PreOrder(tree->_left);
_PreOrder(tree->_right);
}
void preOrder(){
_PreOrder(_root);
}
bool Insert(const T& x){
if (_root == nullptr){
_root = new Node(x);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
//寻找 x 应该插入的位置
while (cur){
parent = cur;
if (x > cur->_key){
cur = cur->_right;
}
else if (x < cur->_key){
cur = cur->_left;
}
else{
return false;
}
}
// 与插入结点的父节比较点开始插入
cur = new Node(x);
if (x > parent->_key){
parent->_right = cur;
}
else{
parent->_left = cur;
}
return true;
}
void InOrder(){
_InOrder(_root);
}
void _InOrder(Node* root){
if (root == nullptr){
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
const Node* Find(const T& x){
// ??? 找的时候要不要判断空???
if (_root == nullptr){
return nullptr;
}
Node* cur = _root;
while (cur){
if (x > cur->_key){
cur = cur->_right;
}
else if (x < cur->_key){
cur = cur->_left;
}
else{
return cur;
}
}
return cur;
}
bool Remove(const T& x){
Node* cur = _root;
Node* parent = nullptr;
while (cur){
if (cur->_key > x){
parent = cur;
cur = cur->_left;
}
else if (cur->_key < x){
parent = cur;
cur = cur->_right;
}
else{
if (cur->_left == nullptr){
if (parent == nullptr){
_root = cur->_right;
}
else{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr){ // 如果cur的右孩子为空
if (parent == nullptr){
_root = cur->_left;
}
else{
if (parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
delete cur;
}
else{ // 如果左右都不为空,寻找cur的右子树的最左节点,与之交换
Node* tmp = cur->_right;
Node* prev = cur;
while (tmp->_left){
prev = tmp;
tmp = tmp->_left;
}
swap(cur->_key, tmp->_key);// 交换两个结点的值
if (prev->_left == tmp)
prev->_left = nullptr;
else
prev->_right = nullptr;
delete tmp;
}
return true;
}
}
return false;
}
private:
Node* _root;
};
//测试用例
void Test(){
BinarySearchTree<int> tree;
tree.Insert(1);
tree.Insert(5);
tree.Insert(7);
tree.Insert(3);
tree.Insert(2);
/*tree.Remove(1);
tree.Remove(5);
tree.Remove(7);
tree.Remove(3);
tree.Remove(2);
*/
tree.InOrder();
//tree.preOrder();
}