查找-二叉排序树

版权声明:转载请注明出处。 https://blog.csdn.net/baidu_38304645/article/details/83057399

动态查找表:表结构本身是在查找过程中动态生成的,即对于给定值key,若表中存在其关键值等于key的记录,则查找成功返回,否则插入关键字等于key的记录。

二叉排序树或者是一颗空树,或者是具有下列性质的二叉树:

1、若他的左子树不为空,则左子树上所有结点的值均小于它的根结点的值。

2、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

3、它的左右子树也分别为二叉排序树。

二叉排序树又称二叉查找树,根据上述定义的结构特点可见,它的查找过程与次优二叉树类似,即当二叉排序树不空时,首先将给定值和根结点的关键字比较,若相等,则查找成功,否则将根据给定值和根结点的关键字之间的大小关系,分别在左子树或右子树上继续进行查找。通常,可取二叉链表作为二叉排序树的存储结构。

首先是辅助宏的定义:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define UNDERFLOW -2
#define NULL 0
#define LH 1 //左高
#define EH 0  //等高
#define RH -1 //右高
typedef int Status;
typedef int KeyType;
typedef char* InfoType;

二叉排序树的存储结构定义:

//二叉排序树的存储结构定义
typedef struct{
   KeyType key;
   InfoType otherinfo; //附加信息
}ElemType;
typedef struct BiTNode{ 
   ElemType data;
   struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
typedef BiTNode BSTNode;
typedef BiTree BSTree; 

查找(递归实现):

算法思想:

若为空,返回NULL。

不空时,

若查找的关键字等于跟结点,返回T;

否则

    若小于根,查其左子树,返回查找结果;

    若大于根,查其右子树,返回查找结果;

BSTree SearchBST(BSTree T,KeyType key){
	/*查找(递归实现)
	若为空 返回NULL 不空时,若查找的关键字等于根结点
	返回T 否则若小于根 查其左子树 返回查找结果 若大
	于根 查其右子树 返回查找结果*/
   if(!T)
	   return NULL;
   else if(T->data.key==key)   //在左子树继续查找
	   return T; 
   else if(T->data.key<key)    //在右子树继续查找
       return SearchBST(T->rchild,key);
   else
       return SearchBST(T->lchild,key);
}

查找(非递归实现):

p最初指向根结点,只要p不空且p所指结点不是所求则根据比较结果令p变为当前结点的左孩子或右孩子。

如此重复直到p空或者找到。

BSTNode *Search(BSTree T,KeyType key){
	/*查找(非递归实现)
	p指向根结点 只要p不空且p所指结点不是所求则根据比较
	结果令 p变为 当前结点的左孩子 或右孩子。如此重复则
	最终p空或者找到*/
   BSTNode *p=T;
   while(p!=NULL&&p->data.key!=key){
       if(key<p->data.key)
		   p=p->lchild;
       else
           p=p->rchild;
   }
   return p;
}

判断一颗给定二叉树是否是二叉排序树。

运用递归的思想,空树是二叉排序树;当树不空时,先判断左右子树是否是二叉排序树,只要有一个不是则原树不是二叉排序树,如果左右子树都是二叉排序树,看看左子树中最大的是否比根结点小,右子树中最小的是否比根结点大即可。

Status IsBST(BSTree &T)
{
	//判断T是否为二叉排序树
   BSTNode *p;
   if(!T)
	   return TRUE;
   else
   {
       if(!IsBST(T->lchild))
		   return FALSE;
       if(!IsBST(T->rchild))
		   return FALSE;
       if(T->lchild)
	   {
		   //找左子树中最大的与根进行比较
	      p=T->lchild;
	      while(p&&p->rchild)
			  p=p->rchild;
		  if(p->data.key>T->data.key)
			  return FALSE;
	   }
       if(T->rchild)
	   {
		   //找右子树中最大的与根进行比较
	      p=T->rchild;
	      while(p&&p->lchild)
			  p=p->lchild;
		  if(p->data.key<T->data.key)
			  return FALSE;
	   }
	   return TRUE;
   }
}

二叉排序树的插入.

若二叉排序树为空,则插入结点应为根结点。

否则,在已有二叉排序树中查找是否存在相等结点。

若树中已有,不再插入。

若树中没有,则查找过程中必然会落空,该落空的位置就是新结点应该在的位置。

插入的过程与查找基本类似。新插入的结点一定在叶结点上。

Status InsertBST(BSTree &T,KeyType key)
{
	/*二叉排序树的插入
	若二叉排序树为空 则插入结点为根结点
	否则 在已有二叉排序树中查找是否存在相等结点
	若树中已有 不在插入
	若树中没有 则查找过程中必然会落空 该落空的位
	置就是新结点应该在的位置*/
   if(!T)
   {
      BSTNode *s;
	  if(!(s=(BSTNode *)malloc(sizeof(BSTNode))))
         exit(OVERFLOW);
	  s->data.key=key;
	  s->lchild=s->rchild=NULL;
	  T=s;
	  return OK;
   }
   else if(key==T->data.key)
	  return ERROR;
   else if(key<T->data.key)
      return InsertBST(T->lchild,key);
   else
      return InsertBST(T->rchild,key);
}

二叉排序树的创建:

从空树出发,依次按照结点插入方法插入各结点。

Status CreateBST(BSTree &T)
{  
	//二叉排序树的创建
	//从空树出发,依次按照结点插入方法插入各结点即可
   T=NULL;
   KeyType key;
   scanf("%d",&key);
   while(key)
   {
      if(InsertBST(T,key))
	     scanf("%d",&key);
      else
	     return ERROR;
   }
   return OK;
}

二叉排序树的删除:

首先定位被删除的结点及其双亲,如果不存在关键字等于key的结点,则直接返回。

否则,根据被删结点特性分类处理,保证删除后仍是二叉排序树。

1、被删除的结点是叶子结点。若有双亲则将双亲结点中相应指针域的值置空,否则T置空。

2、被删结点只有左子树或右子树。双亲结点相应指针指向被删结点的唯一子树。

3、被删结点既有左子树也有右子树。找左子树最大的元素代替被删除元素,归结为前一情况。

void DeleteBFT(BSTree &T,KeyType key)
{
   /*二叉排序树的删除
   首先定位被删除的结点及其双亲
   如果不存在关键字等于key的结点则直接返回
   否则 根据被删除结点特性分类处理 保证删后
   仍是二叉排序树 
   被删除的是叶子结点 若有双亲 则将双亲结点中相
   应指针域的值置空 否则T置空
   被删的结点只有左子树或右子树 双亲结点相应指针
   域指向被删结点的唯一子树
   被删结点既有左子树也有右子树 找左子树中最大的
   代替删除元素
   */
   BSTNode *p=T,*f=NULL,*q;
   while(p&&p->data.key!=key)
   {
      if(key<p->data.key)
	  {
	     f=p;
	     p=p->lchild;
	  } 
      else
	  {
	     f=p;
         p=p->rchild;
	  }
   }
   if(!p)
	  return;
   if(!p->lchild&&!p->rchild)
   {
	  if(!f)
	  {
	     free(p);
		 T=NULL;
		 p=NULL;
	  }
	  else if(f->lchild==p)
		 f->lchild=NULL;
	  else
	     f->rchild=NULL;
      free(p);
      p=NULL;
   }
   else if(!p->lchild)
   {
	  if(!f)
	     T=p->rchild;
	  else if(f->lchild==p)
         f->lchild=p->rchild;
	  else
         f->rchild=p->rchild;
      free(p);
      p=NULL;
   }
   else if(!p->rchild)
   {
	  if(!f)
	     T=p->lchild;
	  else if(f->lchild==p)
         f->lchild=p->lchild;
	  else
         f->rchild=p->lchild;
      free(p);
      p=NULL;
   }
   else
   {
      f=p;  //f始终指向q的双亲 方便后面的删除
	  q=p->lchild; 
      while(q->rchild)  //q定位最大 f指向其双亲
	  {
		 f=q;
	     q=q->rchild;
	  }
      p->data.key=q->data.key; //左子树最大者的值填充到原本被删结点
      if(f->lchild==q) //删除左子树中最大者
         f->lchild=q->lchild;
	  else
         f->rchild=q->lchild;
	  free(q);
	  q=NULL;
   }
}

二叉排序树查找的性能分析:

平均查找长度和二叉树的形态有关,即,

最好 logN。形态均匀,与二分查找的判定树相似。

最坏:(n+1)/2

猜你喜欢

转载自blog.csdn.net/baidu_38304645/article/details/83057399