二叉搜索树是一种常用的数据结构,其也是面试中常被问到的问题,因此掌握二叉搜索树十分必要。
本文就二叉搜索树上的一些常见操作进行较为详细的介绍。
二叉搜索树
首先看一下二叉搜索树的定义:
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。——百度百科
凭借二叉搜索树的性质,其可以用做字典树和优先队列。
二叉搜索树上的常见操作主要有查找,插入,删除,求最大值,最小值,求一个节点的前驱结点和后继节点,前驱结点和后继节点我会单独写文章来介绍。上述所有的操作都能在O(lgN)时间内完成,也就是树的高度。
操作
我们首先定义二叉搜索树节点的结构:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
可以看出我们不考虑有父亲指针。
查找
二叉搜索树的查找类似于二分查找。给定一个value值,我们从根节点root开始,
- 若当前节点的val值和value相等,那么就返回该节点
- 若val值小于value,就查找当前节点的右子树
- 若val值大于value,就查找当前节点的左子树
整体思路非常简单,实现代码如下:
TreeNode* TreeSearch(int val,TreeNode* root)
{
TreeNode* cur = root;
while(cur)
{
if(cur->val == val) return cur;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
return cur;
}
插入
插入过程和查找过程比较像,从根节点开始不断向下查找插入的位置,过程一直记录当前节点的父亲节点,最后完成插入操作。代码如下:
TreeNode* TreeInsert(int val,TreeNode* root)
{
TreeNode* cur = root;
TreeNode* parent =NULL;
while(cur)
{
parent = cur;
if(cur->val == val) return cur;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
TreeNode* newNode = new TreeNode(val);
if(NULL != parent)//非空树
{ if(parent->val > val)
parent->left = newNode;
else
parent->right = newNode;
}
return newNode;
}
删除节点
删除节点是这几个操作中最复杂的一个操作,要分3中不同的情况讨论。
根据给定val值找到对应的节点,若
- 该节点没有左子树也没有右子树,那么直接删除该节点
- 该节点只有左子树或者只有右子树,那么可以通过其父亲节点和其子节点建立一条连接来删除目标节点
- 该节点既有左子树又有右子树,那么首先要删除当前节点的后继节点m(该节点是val值大于当前节点val值中最小的节点,该节点一定没有左子树),再用m的内容来替代当前节点
下面盗用算法导论中图来进行直观的解释…
没有左右子树的情况.
只有左子树或者只有右子树的情况
左右子树都有的情况
实现代码如下:
TreeNode* getNextNode(TreeNode* root)
{
if(NULL == root) return root;
while(root->left)
{
root = root->left;
}
return root;
}
void TreeDelete(int val, TreeNode* root)
{
TreeNode* node= root;
TreeNode* parent =NULL;
while(cur)
{
parent = cur;
if(cur->val == val) break;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
if(NULL == node) return;
if(!node->left && !node->right)//左右子树都没有的情况
{ delete node; return;}
if(node->left && node->right)//左右子树都有
{
//找到node节点的后继节点 即是右子树中val最小的节点(求后继节点的一种情况)
TreeNode* next = getNextNode(node->right);
int newVal = next->val;
TreeDelete(next->val,root);//删除后继节点
node->val = newVal;//重新设定当前节点的val值
}else{
//只有左子树或者右子树
TreeNode* child = node->left ? node->left : node->right;
if(parent->left == node)
parent->left = child;
else
parent->right =child;
delete node;
}
}
最大值最小值
返回一个二叉搜索树中的最大值最小值可以说是最简单的一个操作,最大值是树中“最右边”的节点的val值,最小值是“最左边”的节点的val值,实现代码如下:
int getMax(TreeNode* root)
{
if(NULL == root) return -1;//?并不是很合适,假设树中元素值都为正..
while(root->right)
root = root->right;
return root->val;
}
int getMin(TreeNode* root)
{
if(NULL == root) return -1;//?并不是很合适,假设树中元素值都为正..
while(root->left)
root = root->left;
return root->val;
}
本文总结了二叉搜索树的常见操作,这些操作包括:查找,插入,删除,最大最小值。这些操作的时间复杂度都为O(lgN),即为树的高度。求一个节点的后继和前驱节点会在另外一篇文章中给出..本文有点类似搬运工的感觉,但是自己总结一遍总会比不总结强,代码自敲,有问题麻烦请指出哈。