二叉查找树(二叉搜索 / 排序树):
前情提要:因为蒟蒻所以就写个伪代码,代码后续补上(好难写呜呜呜还是编辑器用的不太熟练)
一:定义
是一颗空树或者是具有下列性质的树:
(1)若任意节点的左子树非空,则左子树上所有的结点的值都小于它的根结点的值。
(2)若任意节点的右子树非空,则右子树上所有的结点的值都大于它的根节点的值。
(3)左右子树都是二叉搜索树。
(4)树中的每个节点的值都不相同。(有需要也可以将相同的值插入树中)
二:基本操作
基本操作有:遍历(walk),查找(search),最小 / 最大关键字(minimum / maximum)
后继 / 前驱 , 插入 / 删除。
0.搜索树的结点。
包含左孩子指针lchild,
右孩子指针rchild,
父节点指针parent,
关键值key。
定义一个节点类bst来表示结点。
class Bst
{
private:
Bst *lchild;//left
Bst *rchild;//right
Bst *parent;//父节点
int key;//关键字
friend class bstree;
public:
Bst(int k=0,Bst *l=NULL,Bst *r=NULL,Bst *p=NULL)
{
key=k;
lchild=l;
rchild=r;
parent=p;
}
};
1.遍历(walk)
分为先序,中序,后序遍历。
先序:根节点—>左子树—>右子树。
中序:左子树—>根节点—>右子树。
后序:左子树—>右子树—>根节点。
先序遍历:通过递归实现。输出的关键字在其左右子树之前
伪代码:
prewalk(x)
if(x!=NULL)
{
printf x->key;
prewalk(x->lchild);
prewalk(x->rchild);
}
中序遍历:同理,输出的关键字在左右子树中间
伪代码:
midwalk(x)
if(x!=NULL)
{
midwalk(x->lchild);
printf x->key;
midwalk(x->rchild);
}
后序遍历:
伪代码:
powalk(x)
if(x!=NULL)
{
powalk(x->lchild);
powalk(x->rchild);
printf x->key;
}
举例子:
先序:5,3,2,4,6,7
中序:2,3,4,5,6,7
后序:2,4,3,7,6,5
2.查找(search):
需输入一个指向树根的指针x,一个关键字k。
关键字存在,返回一个指向k的结点的指针,否则返回NULL;
具体搜索过程;
(1)从树根开始查找,沿着树的一条路径。
(2)若为空树,则树中没有关键字k,返回NULL;
(3)若不是空树;
(i)关键字k小于结点x,则在结点x的左子树进行查找;
(ii)关键字k大于结点x,则在结点x的右子树进行查找;
(iii)关键字k等于结点x,查找结束,返回指向结点x的指针。
(iiii)若查找到结束也没有k,则返回NULL。
递归查找:
由具体搜索过程:
伪代码:
search(x,k)
if(x->key==k || x==NULL)
{
return x;
}
if(x->key<k) return search(x->lchild,k);
else return search(x->rchild,k);
3.最小关键字:(minimum)
就是求树中最小值,因为二叉查找树可知左子树是小于右子树的,
所以一直沿着左子树进行查找直到遇到NULL,
就可以找到最小关键字。
以下是伪代码:
tree_minimum(x)
{
while(x->key !=NULL)
{
x=x->lchild;
}
}
4.最大关键字(maximum)
同理,就是一直沿着右子树进行查找直到遇到NULL;
伪代码:
tree_maximum()
{
while(x->key!=NULL)
{
x=x->rchild;
}
}
5.后继(successor),前驱(predecessor)
后继就是在中序遍历后,相对当前点x的后一个位置;
前驱就是在中序遍历后,相对当前点x的前一个位置;
查询后继,有如下分类:
(1)x的右子树非空:则后继为右子树的最小关键字;
(2)x的右子树为空:
(i)x为父节点的左孩子:后继为父节点;
(ii)x为父节点的右孩子:后继为x的最低层祖先,最底层祖先满足其左子树为x的祖先;
查询前驱,(与后继对称),有如下分类:
(1)x的左子树非空:前驱为左子树的最大关键字;
(2)x的左子树为空:
(i)x为父节点的右孩子:前驱为父节点;
(ii)x为父节点的左孩子:前驱为x的最低层祖先,并且最低层祖先满足其右子树为x的祖先。
后继伪代码:
successor(x)
if x->rchild!=NULL;
return tree_minimum(x->rchild);
y=x->parent;
while(y!=NULL && x==y->rchild)
x=y;
y=y->parent;
return y;
前驱伪代码:
predecessor(x)
if x->lchild!=NULL;
return tree_maximum(x->lchild);
y=x->parent;
while(y!=NULL && x==y->lchild)
x=y;
y=y->parent;
return y;
举个例子:如下图:
求后继:
对于第一个x右子树非空情况:假设要求7的后继,其右子树的最小关键字为8,故7的后继为8。
对于第二个x右子树为空的情况:
(1)要求8的后继,因为8是9的左孩子,故8的后继为父节点9。
(2)要求10的后继,我们可以看到10的祖先有7,6,13。因为祖先要满足祖先是最低层的并且其左子树为x的祖先,我们可以发现,7没有左子树pass掉,6的左子树为2不是10的祖先所以也Pass掉,最后只剩13,13的左子树为6,为10的祖先,所以10的后继为13。
求前驱:同理。
6.插入(insert)
插入要保证插入之后二叉搜索树的性质不被破坏。
插入要先从树根开始遍历,沿着树向下运动。
通过比较当前结点x的关键字与待插入z的关键字进行比较,决定是向左还是向右移动。
直到x移动到NULL时,将z输入此位置。
伪代码如下:
tree_insert(T,z)//T为根节点,z为待插入的值
{
y=NULL;
x=T;
while(x!=NULL)
{
y=x;
if z->key < x->key
x=x->lchild;
else
x=x->rchild;
}
z->parent=y;
if y==NULL;
T=z;
else if(z->key < y->key)
y->lchild=z;
else
y->rchild=z;
}
脑袋没了,人傻了。
有什么错误和建议还请大佬一一指出(~)