1 二叉搜索树简介
二叉搜索树(Binary Search Tree)一般结构:
struct TreeNode {
int val; //节点值
TreeNode *parent;//指向父节点
TreeNode *left; //指向左子树
TreeNode *right; //指向右子树
TreeNode(int x) : val(x), parent(NULL),left(NULL), right(NULL) {}
};
性质:对于任意节点,左子树的节点值都小于该节点值,右子树的节点值均大于该节点值。
根据该性质,对二叉搜索树做中序遍历便可以顺序(由大到小或由小到大)输出树中所有节点值。
对于一颗高为
h
的二叉搜索树,对二叉树的操作查询、最小、最大、后继、前驱、插入、删除的时间复杂度均为
2 查询
查询某元素、最大最小值、前驱后继
2.1 查询某元素
在二叉搜索树中查询某个元素k。
- 若k大于当前节点值,则搜索其右子树;
- 若k小于当前节点值,则搜索其左子树;
- 若k等于当前节点值或当前节点为空,返回当前节点。
递归版本伪代码:
node初始化为树的根节点root
//输入:node为当前子树的根节点,k为待搜索的节点值
//输出:搜索到的节点
TREE-SEARCH(node,k)
if(node==NULL || k==node->val)
then return node
if(k<node->val)
return TREE-SEARCH(node->left,k)
else
return TREE-SEARCH(node->right,k)
非递归版本伪代码:
TreeNode* ITERATIVE-TREE-SEARCH(TreeNode *node,int k)
while(k!=node->val && node != NULL)
if(k<node->val)
node=node->left
else node=node->right
return node
2.2 最大最小节点值
最大节点值:二叉搜索树最右下方节点值即是树中最大节点值
最小节点值:二叉搜索树最左下方节点值即使树中最小节点值
最大节点值伪代码:
//输入:node为当前子树的根节点
//输出:该子树最大值节点
TreeNode* TREE-MINIMUM(TreeNode* node)
while(node->right != NULL)
node=node->right
return node
最小节点值伪代码:
//输入:node为当前子树的根节点
//输出:该子树最小值节点
TreeNode* TREE-MINIMUM(TreeNode* node)
while(node->left != NULL)
node=node->left
return node
2.3 前驱和后继
后继:指节点值大于某节点值的节点中,节点值最小的节点。即,对树进行中序遍历,节点值紧随其后的节点。
求某个节点的后继分为两种情况:
- 该节点有右子树,则其后继是其右子树最左下方的节点;
- 该节点无右子树,则其后继节点满足:该节点在后继节点的左子树中,且后继节点是符合条件的最低的父节点。
求节点后继的伪代码:
//输入:待寻找其后继节点的节点
//输出:后继节点
TREE-SUCCESSOR(node)
if(node->right != NULL)
return TREE-MINIMUN(node->right)
tmp=node->parent;
while(tmp!=NULL and node==tmp->right)
node=tmp
tmp=tmp->parent
return tmp
3 插入和删除
插入和删除需要在保持二叉搜索树性质的情况下,对树进行修改。
3.1 插入
若插入节点小于当前节点值,则插入其左子树,大于则插入其右子树,直至当前节点为叶节点,则将插入节点变为当前节点的左或右子节点。
向树中插入节点的伪代码:
//输入:root为树的根节点指针,node为当前待插入节点
//输出:更新后树的根节点
TREE-INSERT(root,node)
cur=root
leafNode=NULL
while(cur!=NULL)
leafNode=cur
if(node->val>=cur->val)
cur=cur->right
else
cur=cur->left
node->parent=leafNode//此处本应使用取值符
if leafNode==NULL
root=node
else
if(node->val>=leafNode->val)
leafNode->right=node
else
leafNode->left=node
3.2 删除
相比插入,删除操作较为复杂。删除操作分为三种情况:
- 若删除节点为叶子节点,则直接将其删除;
- 若删除节点只有一个子节点,则用子节点替代删除节点的位置,此处称该子节点为“替代节点”;
- 若删除节点有两个子节点,则使用其后继节点替代删除节点的位置;
如下图所示的树,若要删除节点8(叶节点),则直接将其删除即可,若删除节点11(一个子节点),则使用其子节点**12替代其位置即可,若要删除节点13(两个子节点),则使用其后继节点**15替代替位置即可。

删除节点伪代码:
/*
* 输入:root为树根节点,node为待删除元素
* 输出:替待元素
*/
TREE-DELETE(root,node)
//删除替代节点
if(node->left==NULL or node->right==NULL)//当前节点无子节点或只有一个子节点
tmp=node
else //当前节点有两个子节点
tmp=TREE-SECCESSOR(node) //后继节点无子节点
if(tmp->left!=NULL)
childNode=tmp->left //childNode为删除节点的子节点
else
childNode=tmp->right
if(childNode!=NULL) //替换节点为后继节点的情况
childNode->parent=node->parent//设置替代节点的父节点,将替代节点删除
if(node->parent==NULL)
root=childNode
else if(tmp==tmp->parent->left)
childNode=tmp->parent->left
else
childNode=tmp->parent->right
if(tmp->val!=node->val)
node->val=tmp->val//拷贝节点的值
return tmp
参考资料
《算法导论》第12章 二叉查找树