最近想写一些关于红黑树的博客,既想写的全面,又直观,但是又不知道从哪里入手。斟酌再三,还是从最简单的二叉排序树开始写。
二叉排序树(Binary Sort Tree)又叫二叉查找树。它是一种特殊结构的二叉树。其或为空树,或具备下列性质:
(1)若它的左子树不为空,则左子树上所有结点的值均小于它的根节点的值。
(2)若它的右子树不为空,则左子树上所有结点的值均大于它的根节点的值。
显然,它的中序遍历就是一个递增的有序序列。定理:在一棵高度为h的二叉搜索树上,动态集合上的操作serach,minmum,maxmum,successor,predecessor可以在O(h)时间内完成。
即查找给定结点,查找最大值最小值,以及给定结点的前驱后继,他们均可以在O(h)时间内完成。具体见算法导论第三版第12章
插入和删除
为了保持二叉排序树的有序性质,插入和删除操作会引起二叉排序树的动态变化,由于插入的结点总是成为叶子节点,而删除的结点还要修改其左右子树的指向,所以插入一个结点相比删除一个结点的处理相对复杂。
插入:首先要查找要插入节点的位置,然后插入即可。
删除:删除一个结点node要考虑三种情况:
1 node没有左右孩子节点,这种情况最为简单,直接删除它,再将父节点原来指向node的指针置NULL就行。
2 node只有一个孩子,那么将这个孩子提升到node的位置上,并修改node的父节点即可。
3 node有两个孩子。那么寻找node的后继y(中序遍历的下一个元素),让y占据node的位置。同时node原来的右子树部分称为y的新的右子树,
node的左子树称为y的新的左子树。简单点说:就是交换要删除的node节点与node节点右子树的最左边节点位置。这样满足中序遍历递增性质。
时刻记住二叉排序树中序是递增的,当删除一个结点时,该结点的位置就要被其后继,也就是要删除节点右子树的中序遍历第一个节点(同时也是右子树的最左节点)
下面举例说明,如图所示:
下面直接贴代码:
#include<stdio.h>
#include<stdlib.h>
#define error -1
#define OK 1
typedef struct BSTNode
{
struct BSTNode *lchild,*rchild;
int data;
}BSTNode,*BSTree;
/**
*@auther:jungege 2015 5.13
**/
//插入元素到二叉排序树中
BSTree InsertBST(BSTree bst,int key)
{
BSTree node,parent,new_node;
if(bst == NULL)//为空树,创建根节点
{
bst = (BSTree)malloc(sizeof(BSTNode));
bst->data = key;
bst->lchild = NULL;
bst->rchild = NULL;
return bst;
}
node = bst;//找到插入位置,保存bst
while(node != NULL)
{
if(node->data == key)
return bst;
else
{
parent = node;//记录父节点,最后保存的是插入节点的父节点
if(node->data < key)
node = node->rchild;
else
node = node->lchild;
}
}
//创建节点,将新key插入到指定位置,插入的节点一定是叶子节点
new_node = (BSTree) malloc (sizeof(BSTNode));
if(parent->data < key)//插入到parent右孩子
parent->rchild = new_node;
else
parent->lchild = new_node;
new_node->data = key;
new_node->lchild = NULL;
new_node->rchild = NULL;
return bst;
}
//删除二叉排序树中元素
int deleteBST(BSTree bst, int key)
{
BSTree node=bst,pre=NULL,nearestkey,nearest_pre;
if(bst == NULL)
return -1;//空树
//寻找删除节点位置
while(node != NULL && node->data != key)
{
pre = node;//保存父结点
if(node->data > key)
node = node->lchild;
else
node = node->rchild;
}
if(node == NULL)
return 0;//没有要删除的节点
//找到要删除的节点node和其父节点pre
if((node->lchild != NULL) && (node->rchild !=NULL))//***要删除节点有左右孩子
{
nearestkey = node->rchild;
nearest_pre = node;
while(nearestkey->lchild != NULL)
{
nearest_pre = nearestkey;//pre指向被nearestkey节点的父节点
nearestkey = nearestkey->lchild;
}
node->data = nearestkey->data;//直接赋值,避免交换节点
//处理nearestkey节点的子节点,显然其为最左节点,所以不存在左子树,只需处理其右子树
nearest_pre->lchild = nearestkey->rchild;
free(nearestkey);
}
else if(node->lchild == NULL && node->rchild !=NULL)//***要删除节点只有右孩子
{
if(pre->rchild == node)// node是其父节点的右子树
pre->rchild = node->rchild;
else
pre->lchild = node->rchild;
free(node);//释放节点
}
else if(node->lchild != NULL && node->rchild ==NULL)//***要删除节点只有左孩子
{
if(pre->rchild == node)// node是其父节点的右子树
pre->rchild = node->lchild;
else
pre->lchild = node->lchild;
free(node);//释放节点
}
else//***被删除节点没有子树
{
if(pre->lchild == node)
pre->lchild = NULL;
if(pre->rchild == node)
pre->rchild = NULL;
free(node);
}
return 1;//删除成功
}
//查找
int SearchBST(BSTree bst,int key)
{
printf("%d ",bst->data);
if(bst==NULL)
return error;
if(bst->data == key)
return OK;
else if(key < bst->data)
SearchBST(bst->lchild,key);
else
SearchBST(bst->rchild,key);
}
//中序遍历
void inorder(BSTree root)
{
if(root)
{
inorder(root->lchild);
printf("%d ",root->data);
inorder(root->rchild);
}
}
void main()
{
int a[10]={3,2,6,5,1,9,7,7,10,8};
int i;
BSTree root=NULL;//初始化非常重要,不然插入时就无法判断树空
//按数组构建二叉排序树
for(i=0;i<10;i++)
root = InsertBST(root,a[i]);
//中序遍历
inorder(root);
printf("\n删除节点6:");
deleteBST(root,6);
printf("\n删除节点6后中序遍历:");
inorder(root);
printf("\n");
}运行结果如下:
原文地址:http://blog.csdn.net/u010498696/article/details/45695377