二叉搜索树要么是棵空树,要么满足以下性质:(1)若左子树不为空,左子树上所有节点的键值小于根节点的键值;(2)若右子树不为空,右子树上所有节点的键值大于根节点的键值;(3)左右子树都是二叉搜索树。
通过中序遍历可以得到升序序列,因此二叉搜索树又叫二叉排序树。因其方便查找,又叫做二叉查找树。
非递归版本
//节点
template
struct TreeNode
{
TreeNode(const K&key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
TreeNode* _left;
TreeNode* _right;
K _key;
};
//二叉搜索树
template
class BSTree
{
public:
typedef TreeNode Node;
//查找
//key大于根节点的键值就往右边找,小于根节点的键值就往左边找
//找到了返回节点地址,找不到返回nullptr
Node* Find(const T& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
cur = cur->_right;
}
else if (key < cur->_key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//插入
//若为空树,直接申请根节点;若不为空,查找合适的位置插入
//插入成功,返回true;当已有相同的key值,插入失败,返回false
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);//new = operator new + 自定义类型调用构造函数
return true;
}
Node* parent = _root;
Node* cur = _root;
//找到合适的位置
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//插入
if (key < parent->_key)
{
parent->_left = new Node(key);
}
else if (key > parent->_key)
{
parent->_right = new Node(key);
}
return true;
}
//删除
//删除共有三种情况:(1)当要删除节点的左孩子不为空(2)当要删除节点的右孩子不为空,
//(3)当要删除节点的左右孩子都不为空
bool Erase(const K& key)
{
//先找到要删除的节点
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到了考虑三种情况
//右孩子为空
if (cur->_right == nullptr)
{
//考虑到要删除的节点是根节点
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
//左孩子为空
else if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
}
//左右孩子都不为空
//用替换法,找到左子树的最大节点(最右节点),或者找到右子树的最小节点(最左节点),
//然后替换要删除的节点的键值。最后再删除节点
else
{
Node* LeftMax = cur->_left;
Node* parentMax;
while (LeftMax->_right)
{
parentMax = LeftMax;
LeftMax = LeftMax->_right;
}
swap(LeftMax->_key, cur->_key);
if (parentMax->_left == LeftMax)
{
parentMax->_left = LeftMax->_left;
}
else
{
parentMax->_right = LeftMax->_left;
}
cur = LeftMax;
}
delete cur;
return true;
}
}
return false;
}
//交换
void swap(K& k1, K& k2)
{
std::swap(k1, k2);
}
//中序遍历
//_root是private成员,在类外无法访问
void _Inorder(Node* _root)
{
if (_root == nullptr)
{
return;
}
Inorder(_root->_left);
cout << _root->_key << " ";
Inorder(_root->_right);
}
void Inorder()
{
_Inorder(_root);
}
private:
Node* _root;
};
递归版本补充
template
class BSTree
{
public:
typedef TreeNode Node;
//查找
//key大于根节点的键值就往右边找,小于根节点的键值就往左边找
//找到了返回节点地址,找不到返回nullptr
Node* Find(const T& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
cur = cur->_right;
}
else if (key < cur->_key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//查找
Node* 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);
}
//拷贝构造
//就是复制一个相同的二叉搜索树
BSTree(const BSTree& b)
{
_root = _Copy(b._root);
}
//=重载
BSTree& operator=(BSTree b)
{
//_root指向b的空间,b指向_root原来的旧空间,函数结束后销毁
swap(_root, b._root);
return *this;
}
//析构
~BSTree()
{
Destroy(_root);
}
private:
Node* _root;
//查找
Node* _FindR(Node* root ,const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if ( key == root->_key )
{
return root;
}
else if (key > root->_key)
{
return _FindR(root->_right,key);
}
else
{
return _FindR(root->_left,key);
}
}
//插入
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
//传引用的好处,不用记住其父节点,返回之后自动链接到二叉搜索树
root = new Node(key);
return true;
}
if (key == root->_key)
{
return false;
}
else if (key > root->_key)
{
return _InsertR(root->_right, key);
}
else
{
return _InsertR(root->_left, key);
}
}
//删除
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (key == root->_key)
{
//左孩子为空
if (root->_left == nullptr)
{
delete root;
root = root->_right;
}
//右孩子为空
else if (root->_right == nullptr)
{
delete root;
root = root->_left;
}
//都不为空
else
{
Node* LeftMax = root->_left;
while (LeftMax->_right)
{
LeftMax = LeftMax->_right;
}
swap(LeftMax->_key, root->_key);
//此时将左右孩子都不为空的问题,转换成右孩子为空的问题
return _EraseR(root->_left, key);
}
}
else if (key > root->_key)
{
return _EraseR(root->_right, key);
}
else
{
return _EraseR(root->_left, key);
}
}
//拷贝
Node* Copy(Node* root)
{
if (root == nullptr)
{
return root;
}
_root = new Node(b->_key);
_root->_left = Copy(_root->_left);
_root->_right = Copy(root->_right);
}
//销毁
void Destroy(Node*& root)
{
if (root == nullptr)
{
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
};
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
V _value;
};
template
class BSTree
{
typedef BSTreeNode Node;
public:
BSTree() : _root(nullptr) {}
Node* Find(const K& key)
{
return _FindR(_root,key);
}
bool Insert(const K& key, const V& value)
{
return _InsertR(_root, key, value);
}
bool Erase(const K& key)
{
return _EraseR(_root, key);
}
private:
Node* _root;
//查找
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (key == root->_key)
{
return root;
}
else if (key > root->_key)
{
return _FindR(root->_right, key);
}
else
{
return _FindR(root->_left, key);
}
}
//插入
bool _InsertR(Node*& root, const K& key, const V& value)
{
if (root == nullptr)
{
root = new Node(key,value);
return true;
}
if (key == root->_key)
{
return false;
}
else if (key > root->_key)
{
return _InsertR(root->_right, key, value);
}
else
{
return _InsertR(root->_left, key, value);
}
}
//删除
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (key == root->_key)
{
//左孩子为空
if (root->_left == nullptr)
{
delete root;
root = root->_right;
}
//右孩子为空
else if (root->_right == nullptr)
{
delete root;
root = root->_left;
}
//都不为空
else
{
Node* LeftMax = root->_left;
while (LeftMax->_right)
{
LeftMax = LeftMax->_right;
}
swap(LeftMax->_key, root->_key);
swap(LeftMax->_value, LeftMax->_value);
return _EraseR(root->_left, key);
}
}
else if (key > root->_key)
{
return _EraseR(root->_right, key);
}
else
{
return _EraseR(root->_left, key);
}
}
};
void test()
{
BSTree dict;//词典
dict.Insert("string", "字符串");
dict.Insert("tree", "树");
dict.Insert("left", "左边、剩余");
dict.Insert("right", "右边");
dict.Insert("sort", "排序");
// 插入词库中所有单词
string str;
while (cin>>str)
{
BSTreeNode* ret = dict.Find(str);
if (ret == nullptr)
{
cout << "单词拼写错误,词库中没有这个单词:" <_value << endl;
}
}
}
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
//本题的重点在于括号的省略
//1. 左边为空,右不为空。左边括号保留。
//2. 左边为空,右边为空。括号不保留。
//3. 左边不为空,右边为空。右边括号不保留。
class Solution {
public:
string tree2str(TreeNode* root) {
if(root==nullptr)
{
return "";
}
string str = to_string(root->val);
if(root->left!=nullptr || root->right!=nullptr)
{
str+="(";
str+=tree2str(root->left);
str+=")";
}
if(root->right!=nullptr)
{
str+="(";
str+=tree2str(root->right);
str+=")";
}
return str;
}
};
法1
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
bool FindTreeNode(TreeNode*root,TreeNode*x)
{
if(root==nullptr)
{
return false;
}
if(root->val==x->val)
{
return true;
}
return FindTreeNode(root->left,x)||FindTreeNode(root->right,x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//思路:如果root是p和q的公共节点,那么p和q一定在root的左右子树,如果在同一棵树,判断在哪棵树,进行递归,直到找到为止。
if(root==p||root==q)
{
return root;
}
if(root == nullptr)
{
return nullptr;
}
//判断p在哪棵树
int pInLeft = FindTreeNode(root->left,p);
int pInRight = FindTreeNode(root->right,p);
//判断q在哪棵树
int qInLeft = FindTreeNode(root->left,q);
int qInRight = FindTreeNode(root->right,q);
//
if(pInLeft==1 && pInLeft == qInLeft)
{
return lowestCommonAncestor(root->left,p,q);
}
else if(pInRight ==1 && pInRight == qInRight)
{
return lowestCommonAncestor(root->right,p,q);
}
else
{
return root;
}
}
};
//这种方法适用于普通二叉树,时间复杂度是O(N)。
//若是二叉搜索树,则只需比较大小,就能判断两个节点是在左树还是右树,
//不用遍历找节点,时间复杂度是O(N)。
法2
//思路:将问题转换成链表的相交问题,用两个栈来存储p和q的路径,再找出第一个相交的节点。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//创建两个栈,用来存放p和q的两条路径
stack pPath;
stack qPath;
//找到p和q的节点前,将路径上的节点入栈
FindPath(pPath,root,p);
FindPath(qPath,root,q);
//将两条路径变得一样长
while(pPath.size()>qPath.size())
{
pPath.pop();
}
while(pPath.size()& Path,TreeNode*& root,TreeNode* x)
{
if(root==nullptr)
{
return false;
}
//先进栈,再判断
//因为root可能是路径上的节点
Path.push(root);
if(root == x)
{
return true;
}
//往左子树找
if(FindPath(Path,root->left,x))
{
return true;
}
//往右子树找
if(FindPath(Path,root->right,x))
{
return true;
}
//找不到就将节点弹出
Path.pop();
return false;
}
};
struct TreeNode
{
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x)
:val(x)
,left(NULL)
,right(NULL)
{}
};
class Solution {
public:
//prev记得引用接收,因为prev要发生变化
void inorder(TreeNode*root,TreeNode*&prev)
{
if(root==nullptr)
{
return;
}
inorder(root->left,prev);
root->left = prev;
//将上一个与现在链接起来,如果是null就不用
if(prev)
{
prev->right = root;
}
prev = root;
inorder(root->right,prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
//有序->中序遍历
//在双向链表中,left表示prev,right表示next
//在中序遍历中,顺便链接双向链表
//来一个prev记录前一个节点
TreeNode*prev = nullptr;
inorder(pRootOfTree,prev);
TreeNode*head = pRootOfTree;
//还要防止pRootOfTree为null的情况
while(head&&head->left)
{
head = head->left;
}
return head;
}
};
class Solution {
public:
TreeNode*build(vector&preorder,vector&inorder,int& previ,int inbegin, int inend)
{
if(inbegin>inend)
{
return nullptr;
}
TreeNode* root = new TreeNode(preorder[previ]);
int rooti = 0;
while(inorder[rooti]!=preorder[previ])
{
++rooti;
}
++previ;
root->left = build(preorder,inorder,previ,inbegin,rooti-1);
root->right = build(preorder,inorder,previ,rooti+1,inend);
return root;
}
TreeNode* buildTree(vector& preorder, vector& inorder) {
//思路:前序遍历确定根结点,用来构建二叉树
//中序遍历用来分割区间
//用递归来实现
//细节:(1)需要一个下标来实时记录在前序列表中的位置,
//方便遍历
//(2)需要一个区间来分割中序列表
int index = 0;
int begin = 0;
int end = inorder.size()-1;
return build(preorder,inorder,index,begin,end);
}
};
class Solution {
public:
TreeNode* build(vector&inorder,vectorpostorder,int& posti,int inbegin,int inend)
{
if(inbegin>inend)
{
return nullptr;
}
TreeNode*root = new TreeNode(postorder[posti]);
int rooti = 0;
while(inorder[rooti]!=postorder[posti])
{
++rooti;
}
--posti;
root->right = build(inorder,postorder,posti,rooti+1,inend);
root->left = build(inorder,postorder,posti,inbegin,rooti-1);
return root;
}
TreeNode* buildTree(vector& inorder, vector& postorder) {
//思路:后序遍历:从后往前,可以确定根结点
//中序可以用来分割
//构造二叉树时,用一个从后往前的下标,且先构造右子树
int begin = 0;
int end = inorder.size()-1;
int index = postorder.size()-1;
return build(inorder,postorder,index,begin,end);
}
};
class Solution {
public:
vector preorderTraversal(TreeNode* root) {
//思路:将这棵树分为左路节点和右子树,再创建一个栈,
//将左路节点入栈,同时节点的键值加到vector,直到左路节点为空,
//再出栈,从出栈节点开始,分为左路节点和右子树,继续入栈,
//一直循环,直到栈为空
vector vt;
stack st;
TreeNode*cur = root;
while(cur||!st.empty())
{
while(cur)
{
vt.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
TreeNode* tmp = st.top();
st.pop();
cur = tmp->right;
}
return vt;
}
};
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
//思路:依次将左路节点全部入栈后,再出栈将数据入vector,相当于先访问左子树,再访问右子树,一直循环,直到栈为空。
vector vt;
stack st;
TreeNode*cur = root;
while(cur||!st.empty())
{
while(cur)
{
//入栈
st.push(cur);
cur = cur->left;
}
TreeNode*tmp = st.top();
vt.push_back(tmp->val);
st.pop();
cur = tmp->right;
}
return vt;
}
};
class Solution {
public:
vector postorderTraversal(TreeNode* root) {
//该节点已经访问右子树,那么就将入vector
//如果没有访问右子树就访问右子树。
//用一个指针来表示有没有访问
vector vt;
stack st;
TreeNode* cur = root;
TreeNode* prev = nullptr;
while(cur||!st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
TreeNode*tmp = st.top();
//如果右子树为空,或者右子树已经访问,就直接入vector
if(tmp->right==nullptr||prev==tmp->right)
{
vt.push_back(tmp->val);
st.pop();
prev = tmp;
}
else
{
cur = tmp->right;
prev = tmp;
}
}
return vt;
}
};