数据结构——再赏“树“,关于搜索二叉树(BST树)和平衡二叉树(AVL树)那点事儿~(1)

简介: 数据结构——再赏“树“,关于搜索二叉树(BST树)和平衡二叉树(AVL树)那点事儿~(1)

二叉搜索树(BST树)


什么是二叉搜索树


二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树


二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:


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

举个栗子:

二叉搜索树:

微信图片_20221017163132.jpg


非二叉搜索树:微信图片_20221017163141.png

二叉搜索树的特别函数


Find函数


从二叉搜索树BST中查找元素X,返回其所在结点的地址


实现逻辑:


⏩从根节点开始,树为空,返回NULL


⏩若树不为空,将根节点和传入的数据X进行比较:


  1. 若X小于根节点的键值,进入左子树继续搜索
  2. 若X大于根节点的键值,进入右子树继续搜索
  3. 若两者比较结果相等,搜索完成,返回此结点的指针

常言道:一图值前言

微信图片_20221017163147.jpg

所需结构体

//为了简化后文重复代码的书写,用typedef对指向树结构的指针取两个别名位置类型的"Position"和搜索二叉树类型的"SearchTree"
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;
struct TreeNode{
    ElementType Element;
    SearchTree Left;
    SearchTree Right;
};

具体实现的参考代码

Position Find(ElementType X, SearchTree T)
{
    if(T == NULL) return NULL;
    if(X < T->Element) return Find(X,T->Left);//带着X进入左子树
    if(X > T->Element) return Find(X,T->Right);//带着X进入右子树
    else return T;//搜索成功
}

Find函数的性质挖掘


从二叉搜索树的特性来看,它的左子树总是比根节点的键值小,所以最小值只能出现在左子树,一个结点它下面没有比它小的左子树了,那它就是最小的了。最大值同理,右子树总是比根节点的键值大,一个结点它下面没有比它大的右子树了,那它就是最大的了。


FindMin函数


从二叉搜索树BST中查找并返回最小元素所在结点的地址。


具体实现的参考代码


Position FindMin(SearchTree T)
{
    if(T == NULL) return NULL;
    else
    {
        if(T->Left == NULL) return T;//搜索成功,没有比它小的了
        else return FindMin(T->Left);//继续到左子树的左子树中查找
    }
}

FindMax函数


从二叉搜索树BST中查找并返回最大元素所在结点的地址。


具体实现的参考代码


上述两份代码都是使用的递归的方式实现的。这里变一下~。用函数迭代法

//函数迭代法
Position FindMax(SearchTree T)
{
    if(T == NULL) return NULL;
    while(T->Right) return T->Right;
    return T;
}

闲谈一刻


递归常常被称为—— “一种优雅的解决问题的方式”。对于它的态度,分为了三种阵营,恨之入骨,爱不释手,恨了又爱


因为递归写代码可以让方案更整洁喔。比如在dfs中,只用写好当前这一步的逻辑,下一步则递归进去,实现和当前这步一样的操作就好。但是递归在性能上是不及循环的喔~!


每个递归函数都有两部分,基线条件和递归条件。递归条件是指函数自己调用自己,而基线条件(⊙o⊙)…,则是指函数不再自己调用自己了,可以获得其他值回溯回去了,从而避免形成无限循环。典型例子:斐波那契数列


Insert函数


因为二叉搜索树独特的性质,导致了插入一个数据以后,仍然要保证左子树比根节点小,右子树比根节点大。所以对于插入而言,最主要的确认插入的位置

微信图片_20221017163633.jpg

具体实现的参考代码


SearchTree Insert(ElementType X,SearchTree T)
{
    //如果树为空,生成并返回一个有一个结点的二叉搜索树
    if(T ==NULL)
    {
        T = malloc(sizeof(struct TreeNode));
        T->Element = X;
        T->Left = T->Right = NULL;
    }else
    {
     if(X < T->Element) T->Left = Insert(X,T->Left);//如果比根节点小,就向左子树的方向进行插入
     else 
     if(X > T->Element) T->Right = Insert(X,T->Right);//如果比根节点大,就向右子树的方向进行插入
    }
     return T;
}

Delete函数(难点)


情况一


要删除的是叶结点:直接删除,并再修改其父结点指针—置为NULL

微信图片_20221017163802.jpg

情况二


要删除的结点只有一个孩子结点: 将其父结点的指针指向要删除结点的孩子结点

微信图片_20221017163815.jpg

情况三


要删除的结点有左、右两棵子树: 用另一结点替代被删除结点:用 右子树的最小元素 或者 左子树的最大元素

微信图片_20221017163815.jpg

具体实现的参考代码


SearchTree Delete(ElementType X, SearchTree T)
{
  Position TmpCell;
  //找到要删除的位置在哪里 
  if(T == NULL) printf("Element not found\n");
  else
  if(X < T->Element) T->Left = Delete(X,T->Left);
  else
  if(X > T->Element) T->Right = Delete(X,T->Right) ;
  //具体的开始执行删除操作了 
  //两个儿子都存在的情况
  else
  if(T->Left && T->Right)  //两个孩子
  {
    TmpCell = FindMin(T->Right) ;//找到右子树的最小值或者左子树的最大值来代替那个要被删除的节点
    T->Element = TmpCell->Element;
    //删除右边那个被抽上去替代的节点 
    T->Right =  Delete(T->Element,T->Right);
  }else //一个或者没有孩子 
  {
    TmpCell = T;
    if(T->Left == NULL) T = T->Right;//结合上图,只有一个孩子的时候,是让这孩子挪到要删除结点的位置,也就实现了 将其父结点的指针指向要删除结点的孩子结点
}
    else if(T->Right == NULL) T = T->Left;
    free(TmpCell);
  }
  return T;
}
相关文章
|
2月前
|
算法
数据结构之博弈树搜索(深度优先搜索)
本文介绍了使用深度优先搜索(DFS)算法在二叉树中执行遍历及构建链表的过程。首先定义了二叉树节点`TreeNode`和链表节点`ListNode`的结构体。通过递归函数`dfs`实现了二叉树的深度优先遍历,按预序(根、左、右)输出节点值。接着,通过`buildLinkedList`函数根据DFS遍历的顺序构建了一个单链表,展示了如何将树结构转换为线性结构。最后,讨论了此算法的优点,如实现简单和内存效率高,同时也指出了潜在的内存管理问题,并分析了算法的时间复杂度。
65 0
|
5天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
31 12
|
5天前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
31 10
|
5天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
24 2
|
19天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
108 4
|
2月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
161 8
|
3月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
37 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
3月前
|
存储 算法 数据管理
数据结构与算法学习二零:二叉排序树(BST)、平衡二叉树(AVL)
这篇文章通过需求分析、代码实现和测试验证,详细介绍了二叉排序树的创建、遍历和删除操作,以及二叉平衡树(AVL)的自平衡特性和单旋转操作,旨在提高树结构在数据管理中的效率和性能。
68 0
数据结构与算法学习二零:二叉排序树(BST)、平衡二叉树(AVL)
|
3月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆