C++(进阶) 第3章 二叉搜索树
前言
之前在数据结构篇简单的介绍了二叉搜索树,本篇博客会详细的介绍
一、什么是二叉搜索树?
⼆叉搜索树的概念
⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:
• 若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值
• 若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点的值
• 它的左右⼦树也分别为⼆叉搜索树
下面就是一颗比较经典的二叉搜索树
搜索二叉树也叫排序二叉树,因为它走中序遍历的时候刚好就是有序的
二、搜索二叉树的实现
0.搜索二叉树的时间复杂度
搜索二叉树的做好情况下时间复杂度就是log N,但是会出现极端情况
所以时间复杂度就是N
但是后面会有AVL树,红黑树,可以优化差不多 log N
1.基本框架
#include<bits/stdc++.h>
using namespace std;
template<class K>
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{
}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
private:
Node* _root = nullptr;
};
2.Find
普通的二叉树其实没有什么意义,如果只是普通的二叉树它存东西的效率其实还不如链表,所以在原来的二叉树基础上添加了一些规则,左孩子比根小,右孩子比根大
假如就用下面的例子,现在我要在这颗树里面搜索看看有没有16这个数字
如果我们查找的值比根大那么就可以直接往右走,比根小只需要往左走就可以了那么最多只需要找这棵树的高度次就可以了
如果找到null了那么就说明找不到
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if(cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
3.Insert
注意:这里有一个规定,树里面的数据是不允许重复的,也,假如树里面已经有了10,那么这里就不允许再次插入,所以这里insert返回值用bool
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left= cur;
}
return true;
}
4.InOder
这里我用的是子函数的形式因为如果不这样的话,外面调用就要传入参数(_root)但是这里我_root是保护调用不了,
void InOder()
{
_InOder(_root);
}
private:
void _InOder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOder(root->_left);
cout << root->_key << ' ';
_InOder(root->_right);
}
写完中序遍历就可以看下程序有没有写错了
这里可以看到输出的是有序的,就说明这里是没有写错的
到这里的完整代码就长这样
#pragma once
#include<bits/stdc++.h>
using namespace std;
template<class K>
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{
}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if(cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left= cur;
}
return true;
}
void InOder(Node* root)
{
if (root == nullptr)
{
return;
}
InOder(root->_left);
cout << root->_key << ' ';
InOder(root->_right);
}
void InOder()
{
_InOder(_root);
}
private:
void _InOder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOder(root->_left);
cout << root->_key << ' ';
_InOder(root->_right);
}
private:
Node* _root = nullptr;
};
#define _CRT_SECURE_NO_WARNINGS
#include"bstree.h"
int main()
{
int arr[] = { 8,3,1,10,6,4,7,14,13 };
BSTree<int> t;
for (auto e : arr)
{
t.Insert(e);
}
t.InOder();
return 0;
}
5.Erase
这里删除的话就比较复杂了,需要先找到要删的那个节点,然后删除,但是删除的那个节点可能会还有孩子并且删除了以后搜索二叉树不能乱,大致可以分成以下三种情况
- 没有孩子
- 只要一个孩子
- 有俩个孩子
假如这里我要删除3,那么我需要找到一个人替代3的位置,这个时候左子树最右边的节点,或者右子树最左边的节点都可以,所以上面的1,2种情况可以归类成一种情况,因为最左边或者最左边的一定是只有一个孩子或者没有孩子的情况
假如上面我要删掉3,那么我去找右子树的最左边的值也就是4,那么3替换成4,然后下面的4就可以直接删掉了
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
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;
return true;
}
else if(cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
Node* RightMin = cur->_right;
Node* RightMinP = cur;
while (RightMin ->_left)
{
RightMinP = RightMin;
RightMin = RightMin->_left;
}
cur->_key = RightMin->_key;
if (RightMinP->_left == RightMin )
{
RightMinP->_left = RightMin->_right;
}
else
{
RightMinP->_right = RightMin->_right;
}
delete RightMin;
return true;
}
}
}
return false;
}
三 、完整代码
#pragma once
#include<bits/stdc++.h>
using namespace std;
template<class K>
struct BSTreeNode
{
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
BSTreeNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{
}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
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;
return true;
}
else if(cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
Node* RightMin = cur->_right;
Node* RightMinP = cur;
while (RightMin ->_left)
{
RightMinP = RightMin;
RightMin = RightMin->_left;
}
cur->_key = RightMin->_key;
if (RightMinP->_left == RightMin )
{
RightMinP->_left = RightMin->_right;
}
else
{
RightMinP->_right = RightMin->_right;
}
delete RightMin;
return true;
}
}
}
return false;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ' ';
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
#define _CRT_SECURE_NO_WARNINGS
#include"BStree.h"
using namespace std;
int main()
{
int arr[] = { 8,3,1,10,6,4,7,14,13 };
BSTree<int> t;
for (auto e : arr)
{
t.Insert(e);
}
t.Insert(16);
t.Insert(4);
t.InOrder();
for (auto e : arr)
{
t.Erase(e);
t.InOrder();
}
return 0;
}
总结
删除有点复杂还是要花时间去理解