二叉搜索树性质:
1、左子树上所有结点的值均小于或等于它的根结点的值。
2、右子树上所有结点的值均大于或等于它的根结点的值。
最大堆性质:
1、两个子树上所有节点的值均不大于它的根结点的值。
1、前驱与后继:
对一棵二叉树进行中序遍历(不是层序),遍历后的顺序,当前节点的前一个节点为该节点的前驱节点;当前节点的后一个节点为该节点的后继节点。
1.1 对于二叉搜索树:
结点x
的前驱节点:小于x.key
的最大关键字节点(6的前驱4,7的前驱6,17的前驱15);
结点x
的后继节点:大于x.key
的最小关键字节点(6的后继7,7的后继9,13的后继15);
后继伪代码:
int TreeSuccessor(node x)
{
if(x.right != NULL) return Tree_minmum(x.right);
y = x.p;
while(y!=NULL && x ==y.right){
//若x为右节点,则后继为最低的祖先且其左孩子也是祖先(13的后继15)
x = y;
y = y.p;
}
return y; //若x为左节点,则返回父节点
}
2、插入与删除:
2.1、插入:
新插入的节点永远为叶子节点,因此,找到的合适位置为空,记录该空节点的父节点用于插入。
插入伪代码:
void TreeInsert(Tree T, node z)
{
y = NULL;
x = T.root; //x用于找位置指导为空即找到,y为x的父节点
while(x != NULL){
y = x;
if(z.key<x.key){
x = x.left;
}else{
x = x.right;
}
}
z.p = y;
if(y == NULL){
// 树为空
T.root = z;
}
else if (z.key<y.key){
y.left = z;
}
else{
y.right = z;
}
}
13插入到15下边:
2.2、删除:
分为三种情况:
a)简单情况: 只有一个孩子或没有孩子,则用另一个孩子或NULL代替z
b)复杂情况: 有两个孩子,分为右孩子为其后继与否两种情况。
宗旨:代替z
的为其后继y
(大于z
的最小值)
b1)右孩子为其后继:用后继y
代替z
右孩子为后继(大于他的最小值),表明该右孩子没有左孩子。
b2)右孩子不为其后继:用后继y
的右孩子代替y
,y
代替z
伪代码:
// v数代替u树的小函数
void Transplant(Tree T, node u,node v)
{
if(u.p = NULL) t.root = v;
else if(u = u.p.left) u.p.left = v;
else u.p.right = v;
if(v != NULL) v.p = u.p;
}
删除节点函数:
**参考资料:**算法导论:12.2节 查询二叉搜索树 与 12.3节 插入与删除
总结:
1、二叉搜索树性质:左子树上所有节点小于根节点(右子树大于)。
2、前驱与后继指的是中序遍历后的节点。
3、二叉搜索树的后继(前驱)为大于(小于)该节点的最小(最大)节点。
4、二叉搜索树中新插入的节点永远为叶子节点,从根节点开始比较直到为空的叶节点。(和堆中插入节点不一样,堆中先放在堆的末尾,然后和根节点比较,大则上移)。
5、二叉搜索树中删除:只有一个孩子或没有孩子,则用另一个孩子或NULL代替删除的节点z
;有两个孩子,则用其后继代替。
6、二叉搜索树上基本操作(插入、删除、前驱、后继、最大、最小)时间与树的高度成正比,因为期望高度为lg(n),因此,平均运行时间为lg(n)。
7、红黑树最坏运行时间为lg(n)。