我之前的博客已经介绍过了二叉树的基本概念和简单实现,具体参考数据结构-树(C语言实现篇)。
二叉搜索树又称二叉排序树,它可以是一颗空树或者满足以下性质的二叉树:
二叉搜索树的操作基本上是查找、插入、删除。
#ifndef BINARYSEARCHTREE_HPP
#define BINARYSEARCHTREE_HPP
#include
namespace Key {
// 二叉树的结点
template<class K>
struct BSTreeNode {
BSTreeNode(const K &key)
: _key(key), _left(nullptr), _right(nullptr) {}
BSTreeNode<K> *_left; // 左节点
BSTreeNode<K> *_right; // 右节点
K _key; // 值
};
// 二叉搜索树
template<class K> // key
class BSTree {
using Node = BSTreeNode<K>;
public:
BSTree() = default;
BSTree(const BSTree<K> &t) {
_root = _Copy(t._root);
}
BSTree<K> &operator=(BSTree<K> t) {
swap(_root, t._root);
return *this;
}
~BSTree() {
_Destory(_root);
}
public:
// 插入
bool Insert(const K &key) {
// 1. 根节点为空的时候
if (_root == nullptr) {
_root = new Node(key);
return true;
}
Node *cur = _root;
Node *parent = nullptr;
while (cur) {
parent = cur;
// 如果当前结点比查找的结点小,那么就往右子树查找
if (cur->_key < key)
cur = cur->_right;
// 如果当前结点比查找的结点小,那么就往左子树查找
else if (cur->_key > key)
cur = cur->_left;
else
return false; // 这个值在这个树已经存在
}
cur = new Node(key);
// 通过双亲去插入
if (parent->_key < key)
parent->_right = cur;
else
parent->_left = cur;
return true;
}
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 Erase(const K &key) {
Node *parent = nullptr;
Node *cur = _root;
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 (cur == _root) {
_root = cur->_right;
} else {
// 判断当前结点是左节点还是右节点
if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
cur = nullptr;
}
// 右子树是否为空
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;
cur = nullptr;
} else // 左右都不为空
{
// 替换法删除 右子树的最左节点
Node *min = cur->_right;
Node *min_parent = cur;
while (min->_left) {
min_parent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
if (min_parent->_left == min)
min_parent->_left = min->_right;
else
min_parent->_right = min->_right;
delete min;
}
}
}
return false;
}
void Inorder() {
_Inorder(_root);
cout << endl;
}
// 递归版本的查找
bool FindR(const K &key) {
return _FindR(_root, key);
}
// 递归版本的插入
bool InsertR(const K &key) {
return _InsertR(_root, key);
}
// 递归版本的删除
bool EraseR(const K &key) {
return _EraseR(_root, key);
}
private:
Node *_Copy(Node *root) {
if (root == nullptr)
return nullptr;
Node *copyRoot = new Node(root->_key);
copyRoot->_left = _Copy(root->_left);
copyRoot->_right = _Copy(root->_right);
return copyRoot;
}
void _Destory(Node *root) {
if (root == nullptr)
return;
_Destory(root->_left);
_Destory(root->_right);
delete root;
}
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 {
Node *cur = root;
if (root->_left == nullptr) {
root = root->_right;
} else if (root->_right == nullptr) {
root = root->_left;
} else {
// 找右树的最左节点
Node *min = root->_right;
while (min->_left) {
min = min->_left;
}
swap(root->_key, min->_key);
return _EraseR(root->_right, key);
}
delete cur;
}
}
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 _FindR(Node *root, const K &key) {
if (root == nullptr)
return false;
if (root->_key < key)
return _FindR(root->_right, key);
else if (root->_key > key)
return _FindR(root->_left, key);
else
return true;
}
// 中序遍历
void _Inorder(Node *root) {
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_key << " ";
_Inorder(root->_right);
}
private:
// 根节点
Node *_root = nullptr;
};
}
#endif // BINARYSEARCHTREE_HPP
的键值对。具体场景如下:
构成键值对,单词对应的值就是该单词在一篇文章中的个数。// 改造二叉树为KV结构
namespace KeyValue {
template<class K, class V>
struct BSTreeNode {
BSTreeNode(const K &key, const V &value)
: _key(key), _value(value), _left(nullptr), _right(nullptr) {}
BSTreeNode<K, V> *_left; // 左节点
BSTreeNode<K, V> *_right; // 右节点
K _key; // 值
V _value;
};
// key,value型
template<class K, class V> // key
class BSTree {
using Node = BSTreeNode<K, V>;
public:
bool Insert(const K &key, const V &value) {
if (_root == nullptr) {
_root = new Node(key,value);
return true;
}
Node *cur = _root;
Node *parent = nullptr;
while (cur) {
parent = cur;
if (cur->_key < key)
cur = cur->_right;
else if (cur->_key > key)
cur = cur->_left;
else
return false; // 这个值在这个树已经存在
}
cur = new Node(key,value);
if (parent->_key < key)
parent->_right = cur;
else
parent->_left = cur;
return true;
}
Node *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 cur;
}
return nullptr;
}
bool Erase(const K &key) {
return true;
}
void Inorder() {
_Inorder(_root);
cout << endl;
}
private:
// 中序遍历
void _Inorder(Node *root) {
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_key << " : " << root->_value << " ";
_Inorder(root->_right);
}
private:
Node *_root = nullptr;
};
}
插入和删除操作都必须先查找对应的位置,才能够进行插入和删除。所以查找是二叉搜索树的关键。
对于有n个结点的二叉搜索树,通常查找的次数为高度次,那么也就是说该结点的深度越深,比较的次数越多。
同样的数据,插入情况不同,可能得到不同的结果,不同的形状会导致查找效率不同。
最优情况下,二叉搜索树是完全二叉树,平均比较次数为log_2(n)
。
最差情况下,二叉搜索树退化为单支数,比较次数为n
。
那么,一旦退化成单支树,效率就会大大减少,所以我们可以用AVL树或者红黑树进行优化。
二叉树创建字符串
class Solution {
public:
string tree2str(TreeNode* root) {
if(root == nullptr)
return string();
string str;
str += to_string(root->val);
if(root->left || root->right)
{
str += '(';
str += tree2str(root->left);
str += ')';
}
if(root->right)
{
str += '(';
str += tree2str(root->right);
str += ')';
}
return str;
}
};
二叉树的分层遍历1
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> q;
vector<vector<int>> vv;
if(root == nullptr)
return vv;
q.push(root);
size_t levelSize = 1; // 用来控制每层的个数
while(!q.empty())
{
// 控制一层一层出
vector<int> v;
for(size_t i = 0;i < levelSize;++i)
{
TreeNode* front = q.front();
q.pop();
v.push_back(front->val);
if(front->left)
q.push(front->left);
if(front->right)
q.push(front->right);
}
vv.push_back(v);
levelSize = q.size();
}
return vv;
}
};
二叉树的分层遍历2
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> v;
if(root == nullptr)
return v;
stack<vector<int>> st;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
int size = q.size();
vector<int> tmp;
for(int i = 0;i < size;++i)
{
TreeNode* cur = q.front();
q.pop();
tmp.push_back(cur->val);
if(cur->left)
q.push(cur->left);
if(cur->right)
q.push(cur->right);
}
st.push(tmp);
}
while(!st.empty())
{
vector<int> tmp = st.top();
st.pop();
v.push_back(tmp);
}
return v;
}
};
二叉树的最近公共祖先
class Solution {
public:
bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
{
if(root == nullptr)
return false;
path.push(root);
if(root == x)
return true;
if(FindPath(root->left,x,path))
return true;
if(FindPath(root->right,x,path))
return true;
path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> pPath,qPath;
FindPath(root,p,pPath);
FindPath(root,q,qPath);
while(pPath.size() != qPath.size())
{
if(pPath.size() > qPath.size())
pPath.pop();
else
qPath.pop();
}
while(pPath.top() != qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top();
}
};
二叉搜索树与双向链表
class Solution {
public:
void InOrderConvert(TreeNode* cur,TreeNode*& prev)
{
if(cur == nullptr)
return;
InOrderConvert(cur->left, prev);
cur->left = prev;
if(prev)
prev->right = cur;
prev = cur;
InOrderConvert(cur->right, prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
TreeNode* prev = nullptr;
InOrderConvert(pRootOfTree,prev);
TreeNode *head = pRootOfTree;
while(head && head->left)
head = head->left;
return head;
}
};
根据前序和中序构造二叉树
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& previ,int inbegin,int inend) {
if(inbegin > inend)
return nullptr;
TreeNode* root = new TreeNode(preorder[previ++]);
int ini = inbegin;
while(ini <= inend)
{
if(inorder[ini] == root->val)
break;
else
++ini;
}
root->left = _buildTree(preorder,inorder,previ,inbegin,ini-1);
root->right = _buildTree(preorder,inorder,previ,ini+1,inend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int i = 0;
return _buildTree(preorder,inorder,i,0,inorder.size()-1);
}
};
根据中序和后序构造二叉树
class Solution {
public:
TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder,int& posti,int inbegin,int inend)
{
if(inbegin > inend)
return nullptr;
TreeNode* root = new TreeNode(postorder[posti--]);
// 找到根节点在中序的位置
int ini = inbegin;
while(ini <= inend)
{
if(inorder[ini] == root->val)
break;
ini++;
}
//
root->right = _buildTree(inorder,postorder,posti,ini+1,inend);
root->left = _buildTree(inorder,postorder,posti,inbegin,ini-1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int i = postorder.size()-1;
return _buildTree(inorder,postorder,i,0,inorder.size()-1);
}
};
前序遍历的非递归写法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> st;
TreeNode* cur = root;
while(cur || !st.empty())
{
// 先访问左路结点
while(cur)
{
v.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
TreeNode* top = st.top();
st.pop();
cur = top->right;
}
return v;
}
};
中序遍历的非递归写法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
while(cur || !st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
TreeNode* top = st.top();
v.push_back(top->val);
st.pop();
cur = top->right;
}
return v;
}
};
后序遍历的非递归写法
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> st;
TreeNode* prev = nullptr;
TreeNode* cur = root;
while(cur || !st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
TreeNode* top = st.top();
if(top->right == nullptr || top->right == prev)
{
v.push_back(top->val);
prev = top;
st.pop();
}
else
{
cur = top->right;
}
}
return v;
}
};