二叉树的基本操作(FID)

二叉搜索树

首先回顾一下:
查找问题:

  1. 静态查找与动态查找;
  2. 针对动态查找,数据如何组织;
  • 静态查找:要查找的元素是不动的,就是在一个集合中,主要做的是find操作,没有delete 和 insert 操作的发生;
  • 动态查找:集合中,除了有find操作,还经常伴随着delete 和 insert 操作。

对于静态查找,我们会经常使用一个非常高效的查找算法----二分查找。它将查找的时间复杂度从 O(n) 直接降为 O(log n)。

二分查找要求集合要有序

为什么二分查找的效率这么好?

因为二分查找事先将要查找的数据进行了有序化操作。这样就把一个线性操作转化为一个类似于树的操作,查找的效率就是树的高度。从这里我们可以得到启发,能不能将数据直接存到树上,而不是将数据存入数组中,然后再采用类似于树的操作,即:减少了中间过程。PS: 树的动态性好,方便进行insert 和 delete 。

将数据直接放在树上面肯定是可行的,但是我们应该如何组织这棵树呢?我们可以尝试,让一个结点满足下列要求:

  1. 所有的左子树都小于该结点;
  2. 所有的右子树都大于该节点;

这样我们的查找过程就变成了一个对当前结点的一个判断。这样就大大缩小我们的查找范围。

我们上面期望的那种树在数据结构中早有定论:二叉搜索树

二叉搜索树的概念

二叉搜索树(BST,Binary Search Tree),亦称为:二叉排序树 or 二叉查找树。

二叉搜索树可以为空。如果不为空,则应满足:

  1. 非空左子树的所有键值 小于 其根节点的键值;
  2. 非空右子树的所有键值 大于 其根节点的键值;
  3. 左、右子树都是二叉搜索树;

二叉搜索树的常用操作:

  1. 查找操作:
  • position Find(ElementType X, BinTree BST):从二叉搜索树BST中查找元素X,返回其所在结点的地址;

查找原理:查找从根节点开始,如果树为空,返回NULL; 若搜索树非空,则根节点和X进行比较,并进行不同的处理:1.若X < 根节点键值,只需左子树中继续搜索;2.如果X大于根节点的键值,在右子树中进行继续搜索;3.若两者比较结果是相等,搜索完成,返回指向此节点的指针。代码如下:

BinTree Find(ElementType X,BinTree BST){
    if(!BST) return NULL ;
    if(X > BST ->Data){
        // 继续查找右子树
        return Find(X,BST->Right);
    }else(X < BST -> Left){
        // 继续查找左子树
        return Find(X,BST-Left);
    }else{
        // 查找成功
        return BST;
    }
}

我们可以发现上面的代码实现方式是递归实现,且都是尾递归实现

尾递归:在程序即将返回的时候才进行递归;且尾递归都可以采用循环来实现

由于非递归函数的执行效率高,可将"尾递归"改为"迭代"函数

BinTree iterFind(ElementType X ,BinTree BST){
    while(BST){
        if(X > BST ->Data){
            BST = BST -> Right; 
        }else if(X <  BST -> Data){
            BST = BST -> Left;
        }else{
            return BST;
        }
    }
    return NULL ;
}

查找的效率决定于树的高度,如果都在左边 or 都在右边形成一条链,那么查找的效率就是O(n-1),我们的目标是O(log n),这就需要树的结构尽量左右对称,也就是以后要谈的平衡二叉树

  • position FindMin(BinTree BST): 从二叉搜索树BST中查找并返回最小元素所在结点的地址;

最小元素一定是在树的最左分支的端节点上;(min 即最左叶子结点)

/*尾递归 查找最小元素*/
BinTree FindMin(BinTree BST){
    if(BST == NULL)  return  NULL;

    if(BST->Left  != NULL){
        return FindMin(BST -> Left);
    } else{
        return BST;
    }
}

改为:迭代函数

BinTree FindMin(BinTree BST){
    if(BST != NULL){
        while(BST -> Left != NULL){
            BST = BST -> Left;
        }
    return BST;
    }
}
  • position FindMax(BinTree BST): 从二叉搜索树BST中查找并返回最大元素所在结点的地址;

最大元素一定是在树的最右分支的端结点上;(max 即最右叶子结点)

/*尾递归 查找最大元素*/
BinTree FindMax(BinTree BST){
    if(BST == NULL)  return  NULL;

    if(BST->Right  != NULL){
        return FindMax(BST -> Right);
    } else{
        return BST;
    }
}

改为:迭代函数

BinTree FindMax(BinTree BST){
    if(BST != NULL){
        while(BST -> Right != NULL){
            BST = BST -> Right;
        }
    return BST;
    }
}
  1. 插入操作:
  • BinTree Insert(ElementType X,BinTree BST):

二叉搜索树的插入:关键是要找到元素应该插入的位置,可以采用与Find类似的方法

/*二叉搜索树的插入算法*/
BinTree Insert(ElementType X,BinTree BST){
    if(BST == NULL){
        /*若原树为空,生成并返回一个结点的二叉搜索树*/
        BST = malloc(sizeof(struct TreeNode));
        BST -> Data = X ;
        BStT -> Left  = BST -> Right = NULL;
    }else{
        if(X < BST -> Data){
            // 递归 插入左子树
            BST -> Left = Insert(X,BST -> Left);
        }else if(X > BST -> Data){
            // 递归 插入右子树
            BST -> Right = Insert(X,BST -> Right);
        }
    }
    return BST;
}
  1. 删除操作:
  • BinTree Delete(ElementType X,BinTree BST):

二叉树的删除分为多种情况:

  1. 要删除的是叶子结点

直接删除,并再修改其父结点指针---置为NULL


二叉树的基本操作(FID)_第1张图片
要删除的是叶子结点
  1. 要删除的结点只有一个孩子结点:

将其父结点的指针指向要删除结点的孩子结点

二叉树的基本操作(FID)_第2张图片
要删除的结点只有一个孩子结点

二叉树的基本操作(FID)_第3张图片
del31.png
  1. 要删除的结点有左右两颗子树:

用另一结点替代被删除结点:右子树的最小元素 or 左子树的最大元素

复杂的问题简单化,将此种情况转化为上述两种情况之一

二叉树的基本操作(FID)_第4张图片
取右子树中的最小元素替代del31.png

二叉树的基本操作(FID)_第5张图片
取左子树中的最大元素替代
BinTree Delete(ElementType X,BinTree BST){
    Position Tmp;
    if(BST == NULL) printf("要删除的元素未找到");

  if(X < BST -> Data){
      // 左子树递归删除
      BST -> Left = Delete(X, BST -> Left);
  }else if(X > BST -> Data){
      // 右子树递归删除
      BST -> Right = Delete(X,BST -> Right);
  }else{
      // 找到要删除的结点
      if(BST -> Left != NULL && BST -> Right != NULL){
          //被删除结点有左右两个子结点
          Tmp = FindMin(BST -> Right);
          // 策略1:在右子树中找最小的元素替代被删除结点
          BST -> Data = Tmp -> Data;
          // 在删除结点的右子树中删除最小元素  
          BST -> Right = Delete(BST -> Data,BST -> Right);
      }else{
          // 被删除结点有一个or无子结点
          Tmp = BST;
          if(BST -> Left == NULL){
              // 有右孩子or 无子结点
              BST = BST -> Right;
          }else if(BST -> Right == Right){
              // 有左孩子or 无子结点
              BST = BST -> Left;
          }
          free(Tmp);
      }
  }
  return BST;
}

你可能感兴趣的:(二叉树的基本操作(FID))