一.二叉搜索树
1.性质
二叉搜索树或是一颗空树,或者满足以下性质
1)若左子树不为空,则左子树上所有节点的值小于等于根节点的值
2)若右子树不为空,则右子树上所有节点的值大于等于根节点的值
3)左右子树都是二叉搜索树
注:二叉搜索树可以插入相同的值,也可以插入不同的值,需要根据使用场景来定
2.时间复杂度:O(N)
最优情况下:O(logN),最坏情况下:O(N)
二.二叉搜索树的模拟实现
注:小编在这里实现的是插入不同的值
1.使用场景1:key
1)节点创建
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
//初始化
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
2)插入
(1)若树为空,直接将新节点做为根节点
(2)若树不为空,key>当前节点的值向右走;key<当前节点的值向左走;当找到合适的空位置后,插入。注意此过程需要用一个指针来标记当前节点的父节点,以保证最后插入时可以知道插在哪里
(3)代码实现
bool insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
}
Node* cur = _root;
Node* parent = cur;//记录cur的父节点位置
while (cur)//找出要出入的位置的父节点
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//判断新节点该插在父节点的左侧还是右侧
if (parent->_key < key)
{
parent->_right = new Node(key);
}
else
{
parent->_left = new Node(key);
}
return true;
}
3)遍历
(1)采用中序遍历的形式,二叉搜索树遍历完将会是升序数组的形式
(2)代码实现
public:
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
cout << root->_key <<" ";
_InOrder(root->_right);
}
4)查找
bool Find(const K& key)
{
if (_root == nullptr) return false;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
5)删除
(1)判断待删除元素是否在二叉搜索树内,若不存在直接返回false,若是存在进行下面的操作
(2)若是待删除节点N左右孩子均为空,将N节点的父节点对应的孩子指针置为空,直接删除N节点
(3)若是待删除节点N左孩子为空,让N节点的父节点对应的孩子指针指向N的右孩子,直接删除N节点
(4)若是待删除节点N右孩子为空,让N节点的父节点对应的孩子指针指向N的左孩子,直接删除N节点
(5)若是待删除节点N左右孩子均不为空,找一个替代节点(右子树中的最左节点或是左子树的最右节点)放在N的位置,直接删除N节点
(6)代码实现
bool Erase(const K& key)
{
if (!Find(key)) return false;
Node* cur = _root;
Node* parent = cur;//记录cur的父节点位置
while (cur)//找出要出入的位置的父节点
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
if(cur->_left == nullptr)//左为空
{
//确定cur是parent的左孩子还是右孩子
if (cur->_key > parent->_key) parent->_right = cur->_right;
else parent->_left = cur->_right;
delete cur;
}
else if(cur->_right == nullptr)//右为空
{
//确定cur是parent的左孩子还是右孩子
if (cur->_key > parent->_key) parent->_right = cur->_left;
else parent->_left = cur->_left;
delete cur;
}
else//左右均不为空
{
//找一个替代节点
Node* replace = cur->_right;
Node* replaceparent = cur;
while (replace->_left)
{
replaceparent = replace;
replace = replace->_left;
}
cur->_key = replace->_key;
//删除replace指向的节点
//判断代替指针是他父亲的左孩子还是右孩子
if (replaceparent->_right==replace)
{
replaceparent->_right = replace->_right;
}
else
{
replaceparent->_left = replace->_right;
}
delete replace;
}
return true;
}
}
return false;
}
6)拷贝构造:利用前序遍历的思想
BSTree(const BSTree<K>& t)
{
_root = copy(t._root);
}
Node* copy(Node* root)
{
if (root == nullptr) return nullptr;
Node* newroot = new Node(root->_key);
newroot->_left = copy(root->_left);
newroot->_right = copy(root->_right);
return newroot;
}
7)析构:采用后序遍历的方法逐个删除节点,以保证不会出现找不到孩子的情况
~BSTree()
{
destory(_root);
_root = nullptr;
}
void destory(Node* root)
{
if (root == nullptr) return;
destory(root->_left);
destory(root->_right);
delete root;
}
8)构造
//强制构造
BSTree() = default;
9) 赋值
BSTree<K>& opreator = (const BSTree<K>&t)
{
swap(_root,t._root);
return *this;
}
2.使用场景2:key/value
1)使用规则:
仍是根据key的大小来查,增,删节点,key的值不可改动,但是value的值可以改动
注:此情况下的二叉搜索树与key场景下的二叉搜索树大体相同,只有部分细节不同,下面小编之就不同部分给出说明
2)节点
template<class K,class V>
struct BSTNode
{
K _key;
V _val;
BSTNode<K,V>* _left;
BSTNode<K,V>* _right;
//初始化
BSTNode(const K& key,const V& val)
:_key(key)
,_val(val)
, _left(nullptr)
, _right(nullptr)
{}
};
3)插入
bool Insert(const K& key,const V& val)
{
if (_root == nullptr)
{
_root = new Node(key,val);
}
Node* cur = _root;
Node* parent = cur;//记录cur的父节点位置
while (cur)//找出要出入的位置的父节点
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//判断新节点该插在父节点的左侧还是右侧
if (parent->_key < key)
{
parent->_right = new Node(key,val);
}
else
{
parent->_left = new Node(key,val);
}
return true;
}
4)遍历数组
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
cout << root->_key << ":"<<root->_val<<" ";
_InOrder(root->_right);
}