二叉搜索树

二叉搜索树是一种常用的数据结构,其也是面试中常被问到的问题,因此掌握二叉搜索树十分必要。
本文就二叉搜索树上的一些常见操作进行较为详细的介绍。


二叉搜索树

首先看一下二叉搜索树的定义:

二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。——百度百科

凭借二叉搜索树的性质,其可以用做字典树和优先队列。

二叉搜索树上的常见操作主要有查找,插入,删除,求最大值,最小值,求一个节点的前驱结点和后继节点,前驱结点和后继节点我会单独写文章来介绍。上述所有的操作都能在O(lgN)时间内完成,也就是树的高度。

操作

我们首先定义二叉搜索树节点的结构:

struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };

可以看出我们不考虑有父亲指针。

查找

二叉搜索树的查找类似于二分查找。给定一个value值,我们从根节点root开始,

  • 若当前节点的val值和value相等,那么就返回该节点
  • 若val值小于value,就查找当前节点的右子树
  • 若val值大于value,就查找当前节点的左子树

整体思路非常简单,实现代码如下:

TreeNode* TreeSearch(int val,TreeNode* root)
{
   TreeNode* cur = root;
   while(cur)
   {
      if(cur->val == val)  return cur;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
  return cur;
}

插入

插入过程和查找过程比较像,从根节点开始不断向下查找插入的位置,过程一直记录当前节点的父亲节点,最后完成插入操作。代码如下:

TreeNode* TreeInsert(int val,TreeNode* root)
{
    TreeNode* cur = root;
    TreeNode* parent =NULL;
     while(cur)
   {
      parent = cur;
      if(cur->val == val)  return cur;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
   TreeNode* newNode = new TreeNode(val);

   if(NULL != parent)//非空树
   {   if(parent->val > val)
            parent->left = newNode;
        else
            parent->right = newNode; 
   }
   return newNode;
}

删除节点

删除节点是这几个操作中最复杂的一个操作,要分3中不同的情况讨论。
根据给定val值找到对应的节点,若

  • 该节点没有左子树也没有右子树,那么直接删除该节点
  • 该节点只有左子树或者只有右子树,那么可以通过其父亲节点和其子节点建立一条连接来删除目标节点
  • 该节点既有左子树又有右子树,那么首先要删除当前节点的后继节点m(该节点是val值大于当前节点val值中最小的节点,该节点一定没有左子树),再用m的内容来替代当前节点

下面盗用算法导论中图来进行直观的解释…

没有左右子树
没有左右子树的情况.

这里写图片描述
只有左子树或者只有右子树的情况

这里写图片描述
左右子树都有的情况

实现代码如下:


TreeNode* getNextNode(TreeNode* root)
{
   if(NULL == root) return root;
   while(root->left)
   {
      root = root->left;
   }
   return root;
}

void TreeDelete(int val, TreeNode* root)
{
    TreeNode* node= root;
    TreeNode* parent =NULL;
     while(cur)
   {
      parent = cur;
      if(cur->val == val)  break;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
    if(NULL == node) return;
    if(!node->left && !node->right)//左右子树都没有的情况
     {     delete node; return;}
    if(node->left && node->right)//左右子树都有
    {
       //找到node节点的后继节点 即是右子树中val最小的节点(求后继节点的一种情况)
       TreeNode* next = getNextNode(node->right);
       int newVal = next->val;
       TreeDelete(next->val,root);//删除后继节点
       node->val = newVal;//重新设定当前节点的val值

    }else{
    //只有左子树或者右子树
       TreeNode* child = node->left ? node->left : node->right;
       if(parent->left == node)
          parent->left = child;
       else
          parent->right =child;
       delete node;
    }
}

最大值最小值

返回一个二叉搜索树中的最大值最小值可以说是最简单的一个操作,最大值是树中“最右边”的节点的val值,最小值是“最左边”的节点的val值,实现代码如下:

int getMax(TreeNode* root)
{
   if(NULL == root) return -1;//?并不是很合适,假设树中元素值都为正..
   while(root->right)
      root = root->right;
   return root->val;
}

int getMin(TreeNode* root)
{
   if(NULL == root) return -1;//?并不是很合适,假设树中元素值都为正..
   while(root->left)
      root = root->left;
   return root->val;
}

本文总结了二叉搜索树的常见操作,这些操作包括:查找,插入,删除,最大最小值。这些操作的时间复杂度都为O(lgN),即为树的高度。求一个节点的后继和前驱节点会在另外一篇文章中给出..本文有点类似搬运工的感觉,但是自己总结一遍总会比不总结强,代码自敲,有问题麻烦请指出哈。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值