二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
性质:它的中序遍历是有序的。
二叉搜索树的模拟实现
1.先创建节点
template<class K>
struct BSTreeNode
{
typedef BSTreeNode<K> Node;
Node* left = nullptr;
Node* right = nullptr;
K _key;
};
template<class K>
class BSTree
{
public:
typedef BSTreeNode<K> Node;
2.查找
bool Find(const K& k)
{
Node* cur = cur;
while (cur)
{
if (k < cur->_key)
{
cur = cur->left;
}
else if (k > cur->_key)
{
cur = cur->right;
}
else
{
return true;
}
}
return false;
}
/递归版本(因为传参问题,把他写进private里,外部封装它)
bool FindR(const K& k)
{
return _FindR(_root, k);
}
bool _FindR(Node* root, const K& k)
{
if (root == nullptr)
{
return false;
}
else if (root->_key > k)
{
return _FindR(root->left, k);
}
else if (root->right < k)
{
return _FindR(root->right, k);
}
else
{
return true;
}
}
3.插入
bool Insert(const K& k)
{
Node* cur = _root;
Node* parent = nullptr;
if (_root == nullptr)
{
_root = new Node(k);
return true;
}
while (cur)
{
if (k < cur->_key)
{
parent = cur;
cur = cur->left;
}
else if (k > cur->_key)
{
parent = cur;
cur = cur->right;
}
else
{
return false;
}
}
cur = new Node(k);
if (parent->_key < k)
{
parent->right = cur;
}
else if (parent->_key > k)
{
parent->left = cur;
}
return true;
}
/递归版本(因为传参问题,把他写进private里,外部封装它)
bool InsertR(const K& k)
{
return _InsertR(_root, k);
}
//这里引用传参很关键
bool _InsertR(Node*& root, const K& k)
{
if (root == nullptr)
{
root = new Node(k);
return true;
}
else if (root->_key > k)
{
return _InsertR(root->left, k);
}
else if (root->_key < k)
{
return _InsertR(root->right, k);
}
else
{
return false;
}
}
需要注意的是:必须多来个节点能记录当前节点的上一个节点,否则插入的节点无法链接到树里。
4.中序遍历
这样写的话,外面调不到,因为InOrder必须得传参,而根节点是private的。
解决:
- 写一个getroot(),再传参
- 封装,如下:可以做到在内部把参数传了,在外面调用无参的就行。
void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->left); cout << root->_key << " "; _InOrder(root->right); } void InOrder() { _InOrder(_root); cout << endl; }
5.删除
小心,有多种情况在内,图解在pdf文件中。
bool Erase(const K& k)
{
if (_root == nullptr)
{
return false;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (k < cur->_key)
{
parent = cur;
cur = cur->left;
}
else if (k > cur->_key)
{
parent = cur;
cur = cur->right;
}
else
{
//找到了,现在删除它,并保持二叉搜索树的结构
if (cur->left == nullptr && cur->right == nullptr)
{
delete cur;
}
else if (cur->left && cur->right == nullptr)
{
if (cur == _root)
{
_root = cur->left;
delete cur;
}
else
{
if (cur == parent->right)
{
parent->right = cur->left;
}
else if (cur == parent->left)
{
parent->left = cur->left;
}
}
delete cur;
return true;
}
else if (cur->left == nullptr && cur->right)
{
if (cur == _root)
{
_root = cur->right;
delete cur;
}
else
{
if (cur == parent->right)
{
parent->right = cur->right;
}
else if (cur == parent->left)
{
parent->left = cur->right;
}
}
delete cur;
return true;
}
else
{
//cur的左右都不为空,替换法删除
Node* Rightmin = cur->right;
Node* Rightminparent = cur;//不能置nullptr,如果cur是根节点呢?
while (Rightmin->left)
{
Rightminparent = Rightmin;
Rightmin = Rightmin->left;
}
cur->_key = Rightmin->_key;
//现在删Rightmin
if (Rightmin == Rightminparent->left)
{
Rightminparent->left = Rightmin->right;
}
else if (Rightmin == Rightminparent->right)
{
Rightminparent->right = Rightmin->right;
}
delete Rightmin;
return true;
}
}
}
}
/递归版本(因为传参问题,把他写进private里,外部封装它)
bool EraseR(const K& k)
{
return _EraseR(_root, k);
}
//这里引用传参很关键
bool _EraseR(Node*& root, const K& k)
{
if (root == nullptr)
{
return false;
}
else if (root->_key > k)
{
return _EraseR(root->left, k);
}
else if (root->_key < k)
{
return _EraseR(root->right, k);
}
else
{
//要删了
Node* del = root;
if (root->left == nullptr)
{
root = root->right;
}
else if (root->right == nullptr)
{
root = root->left;
}
else
{
Node* Rightmin = root->right;
while (Rightmin->left)
{
Rightmin = Rightmin->left;
}
swap(Rightmin->_key, root->_key);
return _EraseR(root->right, k);
}
delete del;
return true;
}
}
6.KV模型(通过一个值查找另一个值)
就不写在笔记里了,有需要自己看。
二叉搜索树的性能
1.最好
二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(logn)
2.最坏
二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(n)
总的来说就是因为插入次序不同,导致的,有没有什么办法解决这个问题呢?
AVL树和红黑树在后续就可以上场了。
OJ题
1.已知前序和中序,构建二叉树
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int ibegin, int iend)
{
if (ibegin > iend)
{
return nullptr;
}
TreeNode* root = new TreeNode(preorder[prei++]);
//分割中序左右区间(关键:找到中序中根的位置rooti,不能用prei的位置)
int rooti = ibegin;
while (rooti <= iend)
{
if (inorder[rooti] == root->val)
break;
else
rooti++;
}
//rooti就是中序中根的下标了。
//[ibegin,rooti-1] rooti [rooti+1,iend]
root->left = _buildTree(preorder, inorder, prei, ibegin, rooti - 1);
root->right = _buildTree(preorder, inorder, prei, rooti + 1, iend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int i = 0;
TreeNode* root = _buildTree(preorder, inorder, i, 0, preorder.size() - 1);
return root;
}
};
2.已知后序和中序,构建二叉树
class Solution {
public:
TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& prei, int ibegin, int iend)
{
if (ibegin > iend)
{
return nullptr;
}
TreeNode* root = new TreeNode(postorder[prei--]);
//在中序中分左右区间(关键:找到中序中根的位置)
int rooti = iend;
while (rooti >= ibegin)
{
if (inorder[rooti] == root->val)
break;
else
rooti--;
}
//此时的rooti为中序根的位置
//[ibegin,rooti-1] rooti [rooti+1,iend]
root->right = _buildTree(inorder, postorder, prei, rooti + 1, iend);
root->left = _buildTree(inorder, postorder, prei, ibegin, rooti - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int i = postorder.size() - 1;
TreeNode* root = _buildTree(inorder, postorder, i, 0, postorder.size() - 1);
return root;
}
};
3.二叉树前序遍历(非递归)
class Solution
{
public:
vector<int> preorderTraversal(TreeNode* root)
{
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
while (cur || !s.empty())
{
while (cur)
{
v.push_back(cur->val);
s.push(cur);
cur = cur->left;
}
//走到这里,左路节点操作完,操作左路节点的右子树
TreeNode* top = s.top();
s.pop();
cur = top->right;
}
return v;
}
};
4.二叉树中序遍历(非递归)
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->left;
}
//走到这里,左子树走完了,就可以入vector了
TreeNode* top = s.top();
s.pop();
v.push_back(top->val);
cur = top->right;
}
return v;
}
};
5.二叉树后序遍历(非递归)
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root)
{
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
TreeNode* prev = nullptr;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->left;
}
//走到这里,左路节点入完栈,就该看节点的右子树了
//1.如果有,操作右子树
//2.如果没有,把该节点入vector
TreeNode* top = s.top();
if (top->right == nullptr || top->right == prev)
{
v.push_back(top->val);
s.pop();
prev = top;
}
else
{
//如果有右子树,才会操作
cur = top->right;
}
}
return v;
}
};
【有道云笔记】二叉搜索树
https://note.youdao.com/s/sE5mtWh