深入解析二叉查找树
正文
所谓二叉查找树,实质上是按二叉树的结构来组织的,这样的树可以用链表结构来表示,其中每一个节点都是一个对象。
二叉查找树中元素(也可称为关键字)的存储方式总是满足以下几个性质:
1.若二叉树的左子树不为空,则左子树上所有节点的值均不大于其根节点的值;
2.若二叉树的右子树不为空,则右子树上所有节点的值均不小于其根节点的值;
3.该二叉树的左子树和右子树也是二叉查找树。
本文将讨论二叉查找树的创建、查询、插入、删除等操作。
第一节 二叉查找树的创建
创建一颗二叉查找树相对于创建二叉树来说是比较麻烦的,因为在创建二叉查找树时,必须遵循其性质。但是就是在这样的前提下,我们仍然会遇到麻烦。我们先来看一对二叉查找树,如下图:
正文
所谓二叉查找树,实质上是按二叉树的结构来组织的,这样的树可以用链表结构来表示,其中每一个节点都是一个对象。
二叉查找树中元素(也可称为关键字)的存储方式总是满足以下几个性质:
1.若二叉树的左子树不为空,则左子树上所有节点的值均不大于其根节点的值;
2.若二叉树的右子树不为空,则右子树上所有节点的值均不小于其根节点的值;
3.该二叉树的左子树和右子树也是二叉查找树。
本文将讨论二叉查找树的创建、查询、插入、删除等操作。
第一节 二叉查找树的创建
创建一颗二叉查找树相对于创建二叉树来说是比较麻烦的,因为在创建二叉查找树时,必须遵循其性质。但是就是在这样的前提下,我们仍然会遇到麻烦。我们先来看一对二叉查找树,如下图:
如图中所示,这两颗二叉查找树都有6个节点,且包含同样的关键字,唯一不同的是这两颗查找树的高度不同。a图的高度是2,b图的高度是4。怎么会出现这种情况呢?这就涉及到根节点关键字的选择了。其实根节点关键字的选择比较随意,不同的根节点就可能使创建的二叉查找树的高度不同。
二叉查找树的类型描述如下:
二叉查找树的查询操作:查找某一个关键字、查找最小和最大元素、查找前驱和后继。
1.查找某一个关键字
法则:该过程从树的根节点开始查找,并沿着树下降。设要找的那个数为k,对碰到的每个节点x,就比较k和key[x]。如果相同,则查找结束。如果k小于key[x],则继续查找x的左子树(由二叉查找树的性质知,k不可能在x的右子树中)。同理,如果k大于key[x],则在x的右子树中继续查找。例如下图:
二叉查找树的类型描述如下:
typedef int ElemType;
typedef struct BSTNode{
ElemType key; //关键字
struct BSTNode *left; //左儿子
struct BSTNode *right; //右儿子
struct BSTNode *p; //父节点
}BSTNode, *BSTree;
创建二叉查找树的节点如下代码:
BSTree CreateNode(ElemType keynum)
{
BSTree tree = NULL;
tree = (BSTree)malloc(sizeof(BSTNode));
tree->key = keynum;
tree->left = NULL;
tree->p = NULL;
tree->right = NULL;
return tree;
}
第二节 二叉查找树的查询
二叉查找树的查询操作:查找某一个关键字、查找最小和最大元素、查找前驱和后继。
1.查找某一个关键字
法则:该过程从树的根节点开始查找,并沿着树下降。设要找的那个数为k,对碰到的每个节点x,就比较k和key[x]。如果相同,则查找结束。如果k小于key[x],则继续查找x的左子树(由二叉查找树的性质知,k不可能在x的右子树中)。同理,如果k大于key[x],则在x的右子树中继续查找。例如下图:
好,我们来看看查找某个关键字的算法:
①.查找最小元素:从根节点开始,沿着各节点的left指针查找下去,直至遇到NULL时为止。如下图:
BSTree SearchNode(BSTree tree, ElemType k)
{
while(tree != NULL && k != tree->key)
{
if(k < tree->key) //在左子树中找
tree = tree->left;
else
tree = tree->right; //在右子树中找
}
return tree;
}
2.查找最小和最大元素
①.查找最小元素:从根节点开始,沿着各节点的left指针查找下去,直至遇到NULL时为止。如下图:
继续,我们来写一下算法:
BSTree MinNode(BSTree tree)
{
while(tree->left != NULL)
tree = tree->left;
return tree;
}
②.查找最大元素:从根节点开始,沿着各节点的right指针查找下去,直至遇到NULL时为止。如下图:
查找最大元素的算法:
①.查找后继元素
定义:如果x有右子树,则x的后继是右子树的最小关键字;否则查看x的祖先节点,直到某个结点y满足此性质:x出现在y的左子树中,如果y存在,则y是x的后继,否则x没有后继。如下图所示:
BSTree MaxNode(BSTree tree)
{
while(tree->right != NULL)
tree = tree->right;
return tree;
}
3.查找前驱和后继
①.查找后继元素
定义:如果x有右子树,则x的后继是右子树的最小关键字;否则查看x的祖先节点,直到某个结点y满足此性质:x出现在y的左子树中,如果y存在,则y是x的后继,否则x没有后继。如下图所示:
查找后继元素的算法:
定义:如果x有左子树,那么前驱是左子树中的最大关键字;否则查看x的祖先节点,直到某个节点y满足此性质:x出现在y的右子树中。如果y存在,则y就是x的前驱,否则x没有前驱。如下图所示:
BSTree TreeSuccessor(BSTree tree)
{
BSTree y;
if(tree->right != NULL) //该节点的右子树不为空,找到右子树的最小关键字
return TreeMinimum(tree->right);
y = tree->p;
while(y != NULL && tree == y->right)
{
tree = y;
y = y->p;
}
return y;
}
②.查找前驱元素定义:如果x有左子树,那么前驱是左子树中的最大关键字;否则查看x的祖先节点,直到某个节点y满足此性质:x出现在y的右子树中。如果y存在,则y就是x的前驱,否则x没有前驱。如下图所示:
查找前驱元素的算法:
1.插入:当讲一个新值x插入到二叉查找树中的适当位置时,设当前插入的位置为z,需满足:z->key = x, z->left = NULL, z->right = NULL。如下图所示:
插入算法如下:
①.没有子女节点:将父节点对应子女节点的指针置为NULL。如下图:
BSTree TreePrecursor(BSTree tree)
{
BSTree y;
if(tree->left != NULL)
return TreeMaximum(tree->left);
y = tree->p;
while(y != NULL && tree == y->left)
{
tree = y;
y = y->p;
}
return y;
}
第三节 二叉查找树的插入和删除
1.插入:当讲一个新值x插入到二叉查找树中的适当位置时,设当前插入的位置为z,需满足:z->key = x, z->left = NULL, z->right = NULL。如下图所示:
int InsertNode(BSTree *tree, BSTree z)
{
BSTree x, y;
y = NULL;
x = *tree;
while(x != NULL)
{
y = x;
if(z->key < x->key)
x = x->left;
else
x = x->right;
}
z->p = y;
if(y == NULL)
*tree = z;
else if(z->key < y->key)
y->left = z;
else
y->right = z;
return 0;
}
2.删除:从二叉查找树中删除某一个节点元素可根据其是否有子女节点来操作:
①.没有子女节点:将父节点对应子女节点的指针置为NULL。如下图:
②.有一个子女节点:将其父节点的指针指向其子女节点。如下图:
③.有两个子女节点:
法一:将当前结点与其左子树中最大的元素交换,然后删除当前结点。
法二:将当前结点与其右子树中最小的元素交换,然后删除当前结点。
法一:将当前结点与其左子树中最大的元素交换,然后删除当前结点。
法二:将当前结点与其右子树中最小的元素交换,然后删除当前结点。
最后我们来看看删除操作的算法:
第四节 结束语
想想、写写、画画......
int TreeDelete(BSTree *tree, BSTree z)
{
BSTree x, y;
if(z->left == NULL || z->right == NULL) //有一个或者没有子女节点
y = z;
else
y = TreeSuccessor(&(**tree)); //有两个子女节点
if(y->left != NULL)
x = y->left;
else
x = y->right;
if(x != NULL)
x ->p = y->p;
if(y->p == NULL)
(*tree) = x;
else if(y == y->p->left)
y->p->left = x;
else
y->p->right = x;
if(y != z)
z->key = y->key;
free(y);
y = NULL;
return 0;
}
二叉查找树的操作就说完了。
第四节 结束语
想想、写写、画画......