您的位置:首页 > 其它

[置顶] 二叉搜索树的搜索、插入、后继、删除

2016-02-17 15:13 323 查看
二叉搜索树

二叉搜索树是以二叉树是形式表示的,这样就可以使用一个二叉链表来表示二叉搜索树了。
对于任何结点x,其左子树的关键字的值不大于x.key,右子树的关键字不小于x.key。


二叉搜索树支持Insert、Delete、Search、Maximum、MInimum、Predecessor(前驱结点)、Successor(后继结点)等操作。二叉搜索树的基本操作与树的高度成正比,对于一个有n个结点的完全二叉树来说,这些操作的最坏运行时间为O(lg n)。若要将n个结点组织成一个线性链,最坏运行时间将达到O(n)。

下面是二叉搜索树的定义:

typedef struct Binary_Search_Tree bTree;
struct struct Binary_Search_Tree
{
int key;
bTree *left;
bTree *right;
bTree *parent;//指向父结点
}*pNode;


遍历

由于左子树 <= 结点 <= 右子树,所以就可以用中序遍历就可以将一棵二叉搜索树按顺序输出。


Inorder_bTree(pNode *root)
{
if(root != NULL)
{
Inorder_bTree(root->left);
printf("%d ", root->key);
Inorder_bTree(root->right);
}
}


查找

从根结点开始进行比较,若给定结点的key大于根结点,则沿右子树继续查找;若小于根结点的key,则沿着左子树查找。直到找到返回该结点为止,否则返回NULL。

bTree Search(pNode *root, int key)
{
if(root == NULL || root->key == key)
return root;
if(key < root->key)
return Search(root->left, key);
else
return Search(root->right, key);

}


上面是递归实现的Search,下面是迭代实现的:<
4000
/p>

bTree Search(pNode *root, int key)
{
while(root != NULL && root->key != key)
{
if(key < root->key)
root = root->left;
else
root = root->right;
}
if(root->key == key)
return root;
else
return root;
}


除了上述查找操作,还有Maximum和Minimum,即查找以为某一结点为根的子树中的最小结点(仅以Minimum为例)。

bTree Minimum(pNode *root)
{
while(root->left != NULL)
root = root->left;
return root;
}


前驱和后继

给定一个结点x,大于x.key的最小关键字结点就是x的后继结点。如果后继存在,就返回x的后继结点;若x的关键字就是这棵树中最大的,就返回NULL。


寻找后继结点分两种情况:

结点x的右子树非空。则x的后继几点恰好是x的右子树中的最左的结点,也就是x右子树中的最小结点。(例如根结点15的后继是17)

结点x的右子树为空,并且x有一个后继y。为了找到y,只要从x开始沿树而上,直到遇到一个其双亲有左孩子的结点。(13的后继为15)



pTree Successor(pNode *root)
{
//  if(root == Maximum(root))
//      return NULL;
if(root->right != NULL)
return Minimum(root->right);

pNode *y;
y = root->parent;
while(y != NULL && root == y->right)
{
root = y;
y = y->parent;
}
return y;
}


以上是二叉搜索树的几个简单基本操作。Insert操作仅需要按照二叉搜索树的性质将结点插入相应位置即可。

下面是Insert的代码:

Insert(pNode *root, bTree t)//t为待插入结点
{
bTree *y, *x;
y = NULL;
x = root;
while(x != NULL)//找到t的双亲结点y
{
y = x;
if(t->key < x->key)
x = x->left;
else
x = x->right;
}
t->parent = y;

if(y == NULL)//待插入二叉树为空
root = t;
else if(t->key < y->key)/*判断待插入结点是作为双亲结点的左子树还是右子树*/
y->left = t;
else
y->right = t;
}


最复杂就是删除结点的操作,要用到以上相关操作…………下次再写Delete

————————————————————————————————————

接着昨天未完成的Delete函数

对于删除一棵二叉搜索树的节点x来说,分以下三种情况:

被删除结点为叶子结点。一种可能该结点是根结点,直接删除,并修改指针即可;另一种可能该结点为非根叶子结点,这样直接将其双亲的左(右)孩子置为空。

只有一个孩子结点(左孩子 or 右孩子)。以仅有左孩子为例,将x的左孩子的双亲指向x的双亲结点,将x的双亲的孩子结点指向x的左孩子。

x的左右孩子结点都存在。这种情况下,先找到x的右子树中最小的结点y(y无左子树)。若y的双亲结点是x,那么用y.key替换x.key,再删除y结点街可以了;若y的双亲结点不为x,则让y的双亲的孩子指向y的右孩子,y的右孩子的双亲指向y的双亲,最后用y.key替换x.key,再free掉y即可。

下面是Delete函数:

void Delete(pNode *root, bTree x)
{
if(x == NULL)
return;
if(x->left == NULL && x->right == NULL)//x是叶子结点的情况
{
if(x->parent == NULL)//x是根结点
{
free(x);
(*root)=NULL;
}
else  //x是非根叶子结点
{
if(x->parent->left == x)
x->parent->left=NULL;
else
x->parent->right=NULL;
free(x);
}
}
else if(x->right != NULL && x->left== NULL)//只有右子树的情况
{
x->right->parent = x->parent;
if(x->parent == NULL)
*root=x->right;
else if(x->parent->left == x)
x->parent->left = x->right;
else
x->parent->right = x->right;
free(x);
}

else if(x->left != NULL && x->right == NULL)//只有左子树的情况
{
x->left->parent = x->parent;
if(x->parent == NULL)
*root=x->left;
else if(x->parent->left == x)
x->parent->left = x->left;
else
x->parent->right = x->left;
free(x);
}
else//左右孩子都存在的情况
{
bTree temp = Minimum(x->right);//找到x的右子树中key最小的结点
if(temp->parent == x)//temp为x的右子树
{
x->right = NULL;
x->right->parent = NULL:
x->key = temp->key;
}
else
{
temp->parent->left = temp->right;
temp->right->parent = temp->parent;
x->key = temp->key
}
free(temp);
}
}


总结:本来Delete函数思路不是特别复杂,但是在修改指针时还要判断被删结点x是其双亲结点的左孩子还是右孩子,所以在只有一个子树的情况中,代码显得有些冗杂。

在《算法导论》中,关于二叉搜索树的删除给出了一个Transplant函数作为Delete的内部函数使用,但还是觉得不如自己思路来的清晰。有兴趣的可以查阅该书(第三版)的168页来研究一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: