数据结构-二叉搜索树

二叉搜索树的定义

二叉搜索树,也称有序二叉树,排序二叉树,是指一棵空树或者具有下列性质的二叉树:

  1. 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

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

  3. 任意节点的左、右子树也分别为二叉查找树。

  4. 没有键值相等的节点。

这里写图片描述
二叉树搜索树的基本操作

  1. 二叉树搜索树插入(递归与迭代)

  2. 二叉树搜索树查找(递归与迭代)

  3. 二叉树搜索树删除(递归与迭代)

  4. 二叉树搜索树销毁


搜索二叉树的定义及初始化


typedef struct SearchTreeNode
{
      SearchTreeNode* rchild;
      SearchTreeNode* lchile;
      SearchTreeType data;
}SearchTreeNode;
//因为会修改根节点的指向,所以使用二级指针
void SearchTreeInit(SearchTreeNode** root)   
{
      if(root==NULL)
      {
            return ;
      }
      *root=NULL;
      return ;
}

二叉树搜索树创建新节点与

SearchTreeNode* SearchTreeCreateNode(SearchTreeType value)
{
      SearchTreeNode* newnode=(SearchTreeNode*)malloc(sizeof(SearchTreeNode));
      newnode->data=value;
      newnode->lchild=NULL;
      newnode->rchild=NULL;
      return newnode;
}


搜索二叉树销毁节点

void DelNodeSearchTree(SearchTreeNode* value)
{
      free(value);
      return ;
}

二叉搜索数插入新节点

这里写图片描述

//递归版本
void SearchTreeInsert(SearchTreeNode** root, SearchTreeType value)
{
      if(root==NULL)
      {
            return ;
      }
      if(*root==NULL)
      {
            //空树,直接插入
            SearchTreeNode* newnode=SearchTreeCreateNode(value);
            *root=newnode;
            return ;
      }
      //对于非空,采用递归插入
      SearchTreeNode* cur=*root;
      if(value<cur->data)
      {
            //递归插入左子树
            SearchTreeInsert(&cur->lchild,value);
      }
      if(value>cur->data)
      {
            //递归插入右子树
            SearchTreeInsert(&cur->rchild,value);
      }
      else
      {
            //关于等于的处理有很多,以下可以采用一种约定
            //我们约定这个二查搜索树不能重复
            //一旦重复,直接返回,插入失败
            return;
      }
      return ;



}

//非递归版本

void SearchTreeInsert(SearchTreeNode** root,SearchTreeType value)
{
      if(root==NULL)
      {
            return ;
      }
      //空树
      if(*root==NULL)
      {
            *root=SearchTreeCreateNode(value);
            return ;
      }
      //对于非空树,要找到先找到插入的位置
      SearchTreeNode* cur=*root;

      //把插入元素的父节点记录下来
      SearchTreeNode* pre=NULL;
      //此循环只是为了找到插入位置
      while(1)
      {
            if(cur==NULL)
            {
                //找到插入位置,跳出循环
                  break;
            }
            if(value<cur->data)
            {
                  pre=cur;
                  cur=cur->lchild;
            }
            else if(value>cur->data)
            {
                  pre=cur;
                  cur=cur->rchild;
            }
            else
            {
                  //发现有一个重复元素,则插入失败
                  return ;
            }
      }
      SearchTreeNode* new_node=SearchTreeCreateNode(value);
      //pre一定不为空,如果pre为空
      //意味着前面的while循环一进来就命中了:cur==NULL
      //然而cur为空,在最开始就排除了
      if(new_node->data<pre->data)
      {
            pre->lchild=new_node;
      }
      else if(new_node->data>pre->data)
      {
            pre->rchild=new_node;
      }
      return;


}

二叉搜索数的查找

这里写图片描述

SearchTreeNode* SearchTreeFind(SearchTreeNode* root, SearchTreeType to_find)
{
      if(root==NULL)
      {
            return ;
      }
      if(to_find<root->data)
      {
            //递归查找左子树
            return SearchTreeFind(root->lchild,to_find);
      }
      else if(to_find>root->data)
      {
            return SearchTreeFind(root->rchild,to_find);
      }
      return root;
}
//非递归

SearchTreeNode*  SearchTreeFind(SearchTreeNode* root,SearchTreeType value)
{
      if(root==NULL)
      {
            return NULL ;
      }
      SearchTreeNode* cur=root;
      while(cur!=NULL)
      {

            if(value<cur->data)
            {
                  cur=cur->lchild;
            }
            else if(value>cur->data)
            {
                  cur=cur->rchild;
            }
            else 
            {
                  break;
            }
      }
      return cur;
}

二叉搜索数的删除

(分三种情况进行处理):

  1.p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a。

  2.p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可;(注意分是根节点和不是根节点);如图b。

  3.p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。

//递归版本
void SearchTreeRemove(SearchTreeNode** root, SearchTreeType value)
{
      if(root==NULL)
      {
           //非法输入
            return ;
      }
      if(*root==NULL)
      {
           //空树
            return ;
      }
      //1.找到to_remove所在的位置
      SearchTreeNode* cur=*root;
      if(value<cur->data)
      {
           //递归删除左子树
            SearchTreeRemove(&cur->lchild,value);
            return ;
      }
      else if(value>cur->data)
      {
            //递归删除右子树
            SearchTreeRemove(&cur->rchild,value);
            return ;
      }
      //找到要删除元素,分情况讨论
      else
      {
           SearchTreeNode* to_remove_node=cur;
          //要删除的元素没有子树
           if(cur->lchild==NULL&&cur->rchild==NULL)
           {
                *root=NULL;
                DelNodeSearchTree(to_remove_node);
                return ;
           }
           //要删除的元素只有左子树
           else if(cur->lchild!=NULL&&cur->rchild==NULL)

           {
                 //把当前节点的左孩子托付给该点的父节点
                 *root=to_remove_node->lchild;
                 DelNodeSearchTree(to_remove_node);
                 return ;
           }
           else if(cur->lchild==NULL&&cur->rchild!=NULL)
           {
                 //把当前节点的右孩子托付给该点的父节点
                 *root=to_remove_node->rchild;
                 DelNodeSearchTree(to_remove_node);
                 return ;
           }
           //要删除的元素既有左子树,又有右子树
           {
                 //找到右子树中的最小节点
                 SearchTreeNode* min=to_remove_node->rchild;
                 while(min->lchild!=NULL)
                 {
                       min=min->lchild;
                       //循环结束,找到了最小节点
                 }
                 //将最小值覆盖要删除当前节点值
                 to_remove_node->data=min->data;
                 //尝试递归删除min->data
                 SearchTreeRemove(&to_remove_node->rchild,min->data);
                 return ;
           }

      }
      return ;
}
//非递归版本
void SearchTreeRemove(SearchTreeNode** root,SearchTreeType value)
{
      if(root==NULL)
      {
            return ;

      }
      if(*root==NULL)
      {
            return ;
      }
      //先找到要删除的节点是谁
      SearchTreeNode* to_remove_node=*root;
      //找到删除节点的父节点
      SearchTreeNode* pre=NULL;
      while(1)
      {
            if(to_remove_node==NULL)
            {
                //没找到要删除的元素,直接返回
                  return ;
            }
            //在左子树中查找
            if(value<to_remove_node->data)
            {
                  pre=to_remove_node;
                  to_remove_node=to_remove_node->lchild;

            }
            //在右子树上查找
            else if(value>to_remove_node->data)
            {

                  pre=to_remove_node;
                  to_remove_node=to_remove_node->rchild;
            }
            else
            {
                //找到了
                  break;
            }
      }
      //找到了,分四种情况讨论
      //没有子树
      if(to_remove_node->lchild==NULL&&to_remove_node->rchild==NULL)
      {
            //此处先判定删除节点是否是根节点
            if(to_remove_node==*root)
            {
                  //删除节点是根节点
                  *root=NULL;
            }
            else
            {

                //删除节点不是根节点
                if(to_remove_node->data<pre->data)
                {
                     pre->lchild=NULL; 
                }
                else 
                {
                      pre->rchild=NULL;
                }
            }
            //统一释放内存
            DelNodeSearchTree(to_remove_node);
            return ;

      }
      //只有左子树
      else if(to_remove_node->lchild!=NULL&&to_remove_node->rchild==NULL)
      {
          //删除节点是根节点
          if(*root=to_remove_node)
          {
                *root=to_remove_node;
          }
          //不是根节点
          else
          {
               //要删除节点是父节点的左子树
               if(to_remove_node->data<pre->data)
               {
                   //把当前节点的左子树托付给父节点的左子树(该点只有左子树,没有右子树) 
                     pre->lchild=to_remove_node->lchild;
               }
               //要删除节点是父节点的左子树
               if(to_remove_node->data<pre->data)
               {
                   //把当前节点的左子树托付给父节点的右子树 
                     pre->rchild=to_remove_node->lchild;
               }
          }
          DelNodeSearchTree(to_remove_node);
          return ;

      }
       //只有右子树
      else if(to_remove_node->lchild==NULL&&to_remove_node->rchild!=NULL)
      {
            //要删除元素是根节点
            if(*root==to_remove_node)
            {
                  *root=NULL;
            }
            //要删除元素不是根节点(只有右子树)
            else
            {
                  //要删除元素是父节点的左子树,则把要删除元素的右子树托付给父节点的左孩子
                  if(to_remove_node->data<pre->data)
                  {
                        pre->lchild=to_remove_node->rchild;
                  }
                  //要删除元素是父节点的右子树,则把要删除元素的右子树托付给父节点的右孩子
                  if(to_remove_node->data>pre->data)
                  {
                        pre->rchild=to_remove_node->rchild;
                  }
            }
            DelNodeSearchTree(to_remove_node);
            return ;
      }
      //既有左子树又有右子树
      else
      {
          SearchTreeNode* min=to_remove_node->rchild;
          SearchTreeNode* min_pre=to_remove_node;
          while(min->lchild!=NULL)
          {
                min_pre=min;
                min=min->lchild;
          }
          //循环结束,min就指向了to_remove_node右子树的最小值
          to_remove_node->data=min->data;
          if(min->data<min_pre->data)
          {
                //min 是min_pre的左子树
                //min一定没有左子树
                min_pre->lchild=min->rchild;

          }
          else
          {
                //通常情况下mins是min_pre的左子树
                //但是c初始情况下除外
                min_pre->rchild=min->rchild;

          }
          DelNodeSearchTree(min);
          return ;

      }
      return ;

}



搜索二叉树的销毁

void SearchTreeDestory(SearchTreeNode** root)
{
      if(root==NULL)
      {
            return ;
      }
      if(*root==NULL)
      {
            return ;
      }
      SearchTreeNode* cur=*root;
      SearchTreeDestory(&cur->lchild);
      SearchTreeDestory(&cur->rchild);
      DelNodeSearchTree(cur);
      //防止变成野指针
      *root=NULL;
      return ;

}

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值