二叉搜索树是比较常见的数据结构。可以说是所有的树结构中最基础的树结构。二叉搜索树最重要的特性是某结点左子树所有结点值小于该结点存放值,右子树所有结点值大于该结点存放值。
这也是二叉“搜索”树名字由来。如果没有这个特性,倘若我们要搜索某个值,需要遍历所有结点。而借助这个特性,我们可以减少许多没有意义的搜索,极大提高搜索效率。
至于二叉搜索树的实现,有几个比较关键点。
1>前序遍历,中序遍历,后序遍历,分别有递归版本和迭代版本。
递归版本很好理解。迭代版本就是利用了栈(先进后出嘛),实际上就是模拟递归过程。
层序遍历就很好理解,层层深入,利用队列(先进先出)。
void preOrder()//整棵树前序遍历
{
preOrder(root);
cout << endl;
}
void preOrderNR()//前序遍历迭代版
{
if (root == nullptr) return;
stack<Node<T>*> stk;
stk.push(root);
while (!stk.empty())
{
Node<T>* cur = stk.top();
stk.pop();
cout << cur->e << " ";
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
}
cout << endl;
}
void inOrder()//整棵树中序遍历
{
inOrder(root);
cout << endl;
}
void inOrderNR()//中序遍历迭代版
{
stack<Node<T>*> stk;
Node<T>* cur = root;
while (cur || !stk.empty())
{
while (cur)
{
stk.push(cur);
cur = cur->left;
}
if (!stk.empty())
{
cur = stk.top();
stk.pop();
cout << cur->e << " ";
cur = cur->right;
}
}
cout << endl;
}
void postOrder()//整棵树后序遍历
{
postOrder(root);
cout << endl;
}
void postOrderNR()//后序遍历迭代版
{
Node<T>* cur = root;
Node<T>* tem;
stack<Node<T>*> stk;
while (cur || !stk.empty())
{
while (cur)
{
stk.push(cur);
cur = cur->left;
}
if (!stk.empty())
{
cur = stk.top();
if (cur->right == nullptr || cur->right == tem)
{
cout << cur->e << " ";
stk.pop();
tem = cur;
cur = nullptr;
}
else cur = cur->right;
}
}
cout << endl;
}
void leverOrder()//层序遍历
{
if (root == nullptr) return;
queue<Node<T>* > que;
que.push(root);
while (!que.empty())
{
Node<T>* cur = que.top();
que.pop();
cout << cur->e << " ";
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
cout << endl;
}
void preOrder(Node<T>* node)//前序遍历递归版
{
if (node == nullptr) return;
cout << node->e << " ";
preOrder(node->left);
preOrder(node->right);
}
void inOrder(Node<T>* node)//中序遍历递归版
{
if (node == nullptr) return;
inOrder(node->left);
cout << node->e << " ";
inOrder(node->right);
}
void postOrder(Node<T>* cur)//后序遍历递归版
{
if (root == nullptr) return;
postOrder(cur->left);
postOrder(cur->right);
cout << cur->e << " ";
}
2>添加结点(add),这里我们重载了add函数。add(T e)是对外接口,而另一个add函数就是实现搜索插入过程。
Node<T>* add(Node<T> node, T e)//某个结点下添加元素
{
if (node == nullptr)
{
++size;
return new Node<T>(e);
}
if (node->e > e)
{
node->left = new Node<T>(e);
}
else node->right = new Node<T>(e);
return node;
}
void add(T e) //向整个树中添加某个元素
{
if (root == nullptr)
{
++size;
root = new Node<T>(e);
}
add(root, e);
}
3>查找最小结点和最大结点。同add操作一样,public和private分别重载。public中minmun(maxmun)调用private中min(max)找到最小(最大)结点指针。有同学可能疑惑为什么
要重载(上述add也是),其实原因在于,我们实现add和minmun,maxmun是对外的接口,可是如果我们要实现其功能,需要利用递归,可是仅仅凭public中这些函数的形参,根本无法实现递归,因为我使用public函数不可能传入具体哪个结点,比如增添一个值为e的结点,我们只会add(e),
正常人思路不会是add(root,e)。所以private中函数实际上才是public中这些函数的“真正实现”
T minmun()//返回树中最小值
{
assert(size > 0);
return min(root)->e;
}
T maxmun()//返回树中最大值
{
assert(size > 0);
return max(root)->e;
}
Node<T>* min(Node<T>* cur)//返回最小值结点
{
if (cur->left == nullptr) return cur;
return min(cur->left);
}
Node<T>* max(Node<T>* cur)//返回最大值结点
{
if (cur->right == nullptr) return cur;
return max(cur->right);
}
4>删除(remove)操作。删除最小结点(removeMin)和删除最大结点(removeMax)和上述函数一样,需要借助private中函数实现功能。具体看代码。
T removeMin()//删除最小结点并返回最小值
{
T ret = minmun();
root = removeMin(root);
return ret;
}
T removeMax()//删除最大结点并返回最大值
{
T ret = maxmun();
root = removeMax(root);
return ret;
}
Node<T>* removeMin(Node<T>* node)//删除以node为根结点子树的最小结点,并返回删除后剩下子树根节点
{
if (node->left == nullptr)
{
Node<T>* right_node = node->right;
delete node;
--size;
return right_node;
}
node->left = removeMin(root->left);
return node;
}
Node<T>* removeMax(Node<T>* node)//删除以node为根结点子树的最大结点,并返回删除后剩下子树根结点
{
if (node->right == nullptr)
{
Node<T>* left_node = node->left;
delete node;
--size;
return left_node;
}
node->right = removeMax(node->right);
return node;
}
难理解的是remove(T e)函数(删除掉具体某个值)。前面递归部分很好理解。问题在于这个函数最后的一种情况,也就是当前遍历的这个结点node的e值正好等于这个值,并且左右儿子都有,那么这个删除操作比前面只有一个儿子情况麻烦一些。其实这里用两种方法,我们这里举其中一种。
我们要删除这个结点node,删除简单,问题是删除之后剩下的左右子树如何调整构成新的二叉搜索树。这时我们要利用二叉搜索树特性,左边永远小,右边永远大。那么这里我们可以找到node右边最小结点right_min作为删除node后新子树的根结点,这时node左边的子树此时照搬给right_min作为左二子(因为right_min是原来node右边,肯定比node左边所有结点大),然后将node->right删除掉right_min后剩下的子树作为right_min的右儿子(right_min是原来node右子树中最小的结点),满足了二叉搜索树特性,因此这种做法是可行的。
同理我们也可以让node左子树中最大的结点为新的根节点,也满足二叉树特性。
void remove(T e)//删除结点值为e的结点
{
root = remove(root, e);
}
Node<T>* remove(Node<T>* node, T e)//删除以node为根节点子树中e值结点,并返回删除后剩余子树根节点
{
if (node == nullptr) return nullptr;
if (e < node->e)
{
node->left = remove(node->left, e);
return node;
}
else if (e > node->e)
{
node->right = remove(node->right, e);
return node;
}
else
{
if (node->left == nullptr)
{
Node<T>* right_node = node->right;
delete node;
--size;
return right_node;
}
else if (node->right == nullptr)
{
Node<T>* left_node = node->left;
delete node;
--size;
return left_node;
}
else
{
Node<T>* right_min = new Node<T>(min(node->right)->e);
right_min->right = removeMin(node->right);
right_min->left = node->left;
node->left = node->right = nullptr;
delete node;
--size;
return right_min;
}
}
}
};
完整代码如下,话都在酒。。。哦,是在代码里。
#include<iostream>
#include<stack>
#include<queue>
#include<cassert>
using namespace std;
template<class T>
class Node
{
public:
T e;
Node<T>* left, right; //左右儿子
Node(T e)
{
this->e = e;
left = nullptr;
right = nullptr;
}
};
template<class T>
class BST
{
public:
BST()//构造函数
{
root = nullptr;
size = 0;
}
int get_size()//大小
{
return size;
}
bool is_empty()//判断是否为空
{
return size == 0;
}
Node<T>* add(Node<T> node, T e)//某个结点下添加元素
{
if (node == nullptr)
{
++size;
return new Node<T>(e);
}
if (node->e > e)
{
node->left = new Node<T>(e);
}
else node->right = new Node<T>(e);
return node;
}
void add(T e) //向整个树中添加某个元素
{
if (root == nullptr)
{
++size;
root = new Node<T>(e);
}
add(root, e);
}
bool contains(T e)//整个树中是否包含某个元素
{
return contains(root, e);
}
void preOrder()//整棵树前序遍历
{
preOrder(root);
cout << endl;
}
void preOrderNR()//前序遍历迭代版
{
if (root == nullptr) return;
stack<Node<T>*> stk;
stk.push(root);
while (!stk.empty())
{
Node<T>* cur = stk.top();
stk.pop();
cout << cur->e << " ";
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
}
cout << endl;
}
void inOrder()//整棵树中序遍历
{
inOrder(root);
cout << endl;
}
void inOrderNR()//中序遍历迭代版
{
stack<Node<T>*> stk;
Node<T>* cur = root;
while (cur || !stk.empty())
{
while (cur)
{
stk.push(cur);
cur = cur->left;
}
if (!stk.empty())
{
cur = stk.top();
stk.pop();
cout << cur->e << " ";
cur = cur->right;
}
}
cout << endl;
}
void postOrder()//整棵树后序遍历
{
postOrder(root);
cout << endl;
}
void postOrderNR()//后序遍历迭代版
{
Node<T>* cur = root;
Node<T>* tem;
stack<Node<T>*> stk;
while (cur || !stk.empty())
{
while (cur)
{
stk.push(cur);
cur = cur->left;
}
if (!stk.empty())
{
cur = stk.top();
if (cur->right == nullptr || cur->right == tem)
{
cout << cur->e << " ";
stk.pop();
tem = cur;
cur = nullptr;
}
else cur = cur->right;
}
}
cout << endl;
}
void leverOrder()//层序遍历
{
if (root == nullptr) return;
queue<Node<T>* > que;
que.push(root);
while (!que.empty())
{
Node<T>* cur = que.top();
que.pop();
cout << cur->e << " ";
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
cout << endl;
}
T minmun()//返回树中最小值
{
assert(size > 0);
return min(root)->e;
}
T maxmun()//返回树中最大值
{
assert(size > 0);
return max(root)->e;
}
T removeMin()//删除最小结点并返回最小值
{
T ret = minmun();
root = removeMin(root);
return ret;
}
T removeMax()//删除最大结点并返回最大值
{
T ret = maxmun();
root = removeMax(root);
return ret;
}
void remove(T e)//删除结点值为e的结点
{
root = remove(root,e);
}
private:
Node<T>* root;//根结点
int size;//树的大小
bool contains(Node<T>* node, T e)//实现public中contains功能
{
if (node == nullptr) return false;
else if (node->e == e) return true;
else if (node->e > e) return contains(node->left, e);
else return contains(node->right, e);
}
void preOrder(Node<T>* node)//前序遍历递归版
{
if (node == nullptr) return;
cout << node->e << " ";
preOrder(node->left);
preOrder(node->right);
}
void inOrder(Node<T>* node)//中序遍历递归版
{
if (node == nullptr) return;
inOrder(node->left);
cout << node->e <<" ";
inOrder(node->right);
}
void postOrder(Node<T>* cur)//后序遍历递归版
{
if (root == nullptr) return;
postOrder(cur->left);
postOrder(cur->right);
cout << cur->e << " ";
}
Node<T>* min(Node<T>* cur)//返回最小值结点
{
if (cur->left == nullptr) return cur;
return min(cur->left);
}
Node<T>* max(Node<T>* cur)//返回最大值结点
{
if (cur->right == nullptr) return cur;
return max(cur->right);
}
Node<T>* removeMin(Node<T>* node)//删除以node为根结点子树的最小结点,并返回删除后剩下子树根节点
{
if (node->left == nullptr)
{
Node<T>* right_node = node->right;
delete node;
--size;
return right_node;
}
node->left = removeMin(root->left);
return node;
}
Node<T>* removeMax(Node<T>* node)//删除以node为根结点子树的最大结点,并返回删除后剩下子树根结点
{
if (node->right == nullptr)
{
Node<T>* left_node = node->left;
delete node;
--size;
return left_node;
}
node->right = removeMax(node->right);
return node;
}
Node<T>* remove(Node<T>* node, T e)//删除以node为根节点子树中e值结点,并返回删除后剩余子树根节点
{
if (node == nullptr) return nullptr;
if (e < node->e)
{
node->left = remove(node->left, e);
return node;
}
else if (e > node->e)
{
node->right = remove(node->right, e);
return node;
}
else
{
if (node->left == nullptr)
{
Node<T>* right_node = node->right;
delete node;
--size;
return right_node;
}
else if (node->right == nullptr)
{
Node<T>* left_node = node->left;
delete node;
--size;
return left_node;
}
else
{
Node<T>* right_min = new Node<T>(min(node->right)->e);
right_min->right = removeMin(node->right);
right_min->left = node->left;
node->left = node->right = nullptr;
delete node;
--size;
return right_min;
}
}
}
};