二叉搜索树(Binary Sort Tree)

     在操作数据库的时候,经常进行大量的插入删除操作,我们希望达到的效果是能够对其中的某些元素操作,而不影响其他元素的位置,使表中的元素的移动达到最小,这里面一般使用的数据结构就是动态查找表。什么结构能够实现这样的效果呢?这里首先想到的是链表,对数据的组织形式选用最简单的树形结构。下面我们对二叉排序树进行讨论。二叉排序树一般具备以下几个特点:

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉搜索树;
(4)没有键值相等的节点。
下面我们利用C语言来构建二叉排序树。首先是其数据结构,保存到文件header.h中:

#ifndef _BITREE_TYPE
#define _BITREE_TYPE

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<time.h>

typedef struct BiTNode{
	int data;
	struct BiTNode *lchild,*rchild;
}BiTNode,*Bitree;
#endif
然后,在构建二叉树之前,我们需要思考的一个问题是,怎么进行合理的构建?思路是这样的,我们先定义一个二叉树结构,先在树中进行搜索,查看是否有元素存在。存在则不插入,不存在则进行插入,循环进行插入,这样就生成了一棵二叉树。这里涉及到几个函数,保存到function.h文件中。定义如下:

#ifndef BITREE_FUNC
#define  BITREE_FUNC
/**declear functions here***/
int searchBST(Bitree T,int key,Bitree f,Bitree *p);
int insertBST(Bitree *T,int key);
void preordertraverse(Bitree T);
void midordertraverse(Bitree T);
void postordertraverse(Bitree T);

/*delete tree*/
int Delete(Bitree *p);
int DeleteBST(Bitree *T,int key);
#endif
首先我们查看生成二叉排序树的两个函数:searchBST与insertBST。保存到algorithm.c文件中,其实现如下:

#include"header.h"
#include"function.h"
int searchBST(Bitree T,int key,Bitree f,Bitree *p)
{
	if(!T){
		*p=f;
		return 0;
	}
	else if(key == T->data){
		*p=T;
		return 1;
	}
	else if(key<T->data)
         return searchBST(T->lchild,key,T,p);
	else 
		 return searchBST(T->rchild,key,T,p);
}
int insertBST(Bitree *T,int key)
{
	Bitree p,s;
	if(!searchBST(*T,key,NULL,&p)){
		s=(Bitree)malloc(sizeof(BiTNode));
		s->data=key;
		s->lchild=s->rchild=NULL;
		if(!p)
			*T=s;
		else if(key<p->data)
			p->lchild=s;
		else 
			p->rchild=s;
		return 1;
	}
	else
		return 0;
}
生成二叉树之后,我们需要对生成的树进行遍历操作以完成对树中数据的读取。这里主要研究树的三种遍历方式,主要是前序遍历,中序遍历,后续遍历。其三种方式的操作分别如下:

前序遍历:先从树根出发,访问树根,然后是左子树,然后是右子树。

中序遍历:从树的最左子树出发,先访问左子树,然后是与之相连的树根,最后是这个右子树。

后续遍历:从叶子出发,先到最左子树,先左子树,再右子树,然后再根。

接下来,对这三种遍历进行实现,并添加到algorithm.c文件中。

void preordertraverse(Bitree T)
{
	if(T==NULL)
		return ;
	printf("%d\t",T->data);
	preordertraverse(T->lchild);
	preordertraverse(T->rchild);
}
void midordertraverse(Bitree T)
{
	if(T==NULL)
		return ;
	midordertraverse(T->lchild);
        printf("%d\t",T->data);
	midordertraverse(T->rchild);
}
void postordertraverse(Bitree T)
{
	if(T==NULL)
		return ;
	postordertraverse(T->lchild);
	postordertraverse(T->rchild);
	printf("%d\t",T->data);
}
讨论完二叉排序树的生成与遍历,还有一个重要的东西就是二叉排序树的删除。这里需要我们考虑几个问题,进行一下分类。首先是最简单的情况,如果要删除的节点仅仅只有一个左子树或者右子树。或者节点子树为左斜树或者右斜树(PS:这种情况是子树无两个分叉)。这样可以考虑使用链表的删除操作。如果待删除节点含有两个子树,这时候删除节点前,需要为节点寻找一个新的继承节点。那怎么寻找这个节点?我们注意到,这个新的节点要满足大于左子树的所有节点,而小于右子树的所有节点。这里我们的思路就是寻找左子树的子树内的最右节点,或者寻找右子树的子树内的最左节点。这里我们是以前一种思路为主,寻找左子树中的最右节点,下面为代码实现:

int Delete(Bitree *p)
{
	Bitree q,s;
	if((*p)->rchild==NULL){
		q=*p;
		*p=(*p)->lchild;
		free(q);
	}
	else if((*p)->lchild==NULL){
		q=*p;
		*p=(*p)->rchild;
		free(q);
	}
	else{
		q=*p;
		s=(*p)->lchild;
		while(s->rchild){
			q=s;
			s=s->rchild;
		}
		(*p)->data=s->data;
		if(q!=*p)
			q->rchild=s->lchild;
		else
			q->lchild=s->lchild;
		free(s);
	}
	return 1;
}
int DeleteBST(Bitree *T,int key)
{
	if(!*T)
		return 0;
	else{
		if(key==(*T)->data)
			return Delete(T);
		else if(key<(*T)->data)
			return DeleteBST(&(*T)->lchild,key);
		else
			return DeleteBST(&(*T)->rchild,key);
	}
}
然后我们在创建主函数进行测试:
#include"header.h"
#include"function.h"

#define DEBUGPRINT  printf("file_name:%s;\ncurrent_line:%d;\ncompile_time:%s:%s;\n\nprogram start...:\n",__FILE__,__LINE__,__DATE__,__TIME__)

int main()
{
	DEBUGPRINT;
	Bitree T=NULL;
	srand(time(0));
	printf("please input treenode number:\n");
    int n,i;
	scanf("%d",&n);
	for(i=0;i<=n;i++)
	{
		insertBST(&T,rand()%100);
	}
	printf("preordertraverse,root->lchild->rchild.:\n");
	preordertraverse(T);
	printf("\n");
	printf("midordertraverse,lchild->root->rchild.:\n");
	midordertraverse(T);
	printf("\n");
	printf("postordertraverse,visit leaves firstly.lchild->rchild.:\n");
	postordertraverse(T);
	printf("\n");
	printf("delete element of tree...\n");
	if(DeleteBST(&T,99)>0){
       midordertraverse(T);
	   printf("\n");
	}
	else 
		printf("element does not in this tree\n");
	return 0;
}
测试结果如下:











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值