数据结构和算法学习记录——认识二叉搜索树及二叉搜索树的查找操作(递归以及迭代实现-查找操作、查找最大和最小元素)

简介: 数据结构和算法学习记录——认识二叉搜索树及二叉搜索树的查找操作(递归以及迭代实现-查找操作、查找最大和最小元素)

二叉搜索树

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

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

1.非空左子树的所有键值小于其根节点的键值。

2.非空右子树的所有键值大于其根节点的键值。

3.左、右子树都是二叉搜索树。

二叉搜索树的一些操作函数

  • Position Find (ElementType x,BinTree BST);

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

  • Position FindMin(BinTree BST);

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

  • Position FindMax(BinTree BST);

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

  • BinTree Insert(ElementType x,BinTree BST);

在二叉搜索树BST中插入一个值为x的节点。

  • BinTree Delete(ElementType x,BinTree BST);

在二叉搜索树BST中删除值为x的节点。

二叉搜索树的查找操作Find

二叉搜索树的查找思路很简单:

查找从根节点开始,如果树为空,返回NULL;

若搜索树非空,则根节点关键字和x进行比较,并进行不同处理:

  1. 若x小于根节点键值,只需在左子树中继续搜索;
  2. 若x大于根节点键值,则在右子树中进行继续搜索;
  3. 如果两者比较的结果是相等,搜索完成,返回指向此节点的指针。

递归实现

使用递归的方法实现的话,代码很简单,直接判断键值大小,进行尾递归。(即在程序要返回值时进行递归)

Position Find(ElementType x,BinTree BST)
{
  if (!BST)
  {
    return NULL;  //为空则查找失败
  }
  if (x > BST->data)
  {
    return Find(x, BST->Right);  //在右子树中继续查找
  }
  else if (x < BST->data)
  {
    return Find(x, BST->Left);   //在左子树中继续查找
  }
  else   //x == BST->data
  {
    return BST;                  //查找成功,返回结点地址
  }
}

但递归实现的方法效率不是很高,从编译的角度来看,尾递归可以用循环的方式来实现了。

所以我们将递归函数改为迭代函数

迭代实现

与递归函数同样的一步是,先判断节点是否为空,为空表示查找失败,非空则开始查找;

如果x的值大于根节点,就将节点的指针指向右子树继续循环查找;

如果x的值小于根节点,则将节点的指针指向左子树继续循环查找;

如果相等,则说明查找成功了。

Position 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;                 //查找失败
}

但是这个查找的效率决定于树的高度。


如果二叉搜索树排成了一条链,即斜二叉树,他的这颗树的高度就为n-1,这样最坏的情况下要找到一个节点就需要n-1次了。算法的时间效率就只有O(N)了,而达不到我们想要的log2n,要解决这个问题就要涉及到以后要讲的平衡二叉树了。


查找最大和最小元素

因为二叉搜索树的特殊性,比根节点小的在左子树,比根节点大的在右子树。

所以,

最大元素一定是在树的最右分枝的端节点上; 最小元素一定是在树的最左分枝的端节点上。

函数的代码实现也没什么难点,按照思路,在递归的方法实现中(查找最小元素):节点的左子树不为空就进行递归,直到某一个节点的左子树为空就返回。

查找最小元素的递归函数

Position FindMin(BinTree BST)
{
  if (!BST)
  {
    return NULL;                  //空的二叉搜索树,返回NULL
  }
  else if (!BST->Left)
  {
    return BST;                   //找到最左的叶节点并返回
  }
  else
  {
    return FindMin(BST->Left);    //沿左分支继续查找
  }
}

在迭代的方法实现中:直接走到最左端或者最右端的节点即是最小元素或者最大元素。

查找最大元素的迭代函数

Position FindMax(BinTree BST)
{
  if (BST)
  {
    while (BST->Right)
    {
      BST = BST->Right;  //沿右分支一直查找,直到最右叶节点
    }
  }
  return BST;
}

end



目录
相关文章
|
机器学习/深度学习 算法 数据挖掘
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
349 0
|
12月前
|
机器学习/深度学习 运维 算法
【微电网多目标优化调度】多目标学习者行为优化算法MOLPB求解微电网多目标优化调度研究(Matlab代码实现)
【微电网多目标优化调度】多目标学习者行为优化算法MOLPB求解微电网多目标优化调度研究(Matlab代码实现)
405 1
|
算法 数据可视化 开发者
为什么要学习数据结构与算法
今天,我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心,更是每一位开发者从“小白”迈向“高手”的必经之路。
为什么要学习数据结构与算法
|
负载均衡 算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
3162 11
架构学习:7种负载均衡算法策略
|
资源调度 算法 数据可视化
基于IEKF迭代扩展卡尔曼滤波算法的数据跟踪matlab仿真,对比EKF和UKF
本项目基于MATLAB2022A实现IEKF迭代扩展卡尔曼滤波算法的数据跟踪仿真,对比EKF和UKF的性能。通过仿真输出误差收敛曲线和误差协方差收敛曲线,展示三种滤波器的精度差异。核心程序包括数据处理、误差计算及可视化展示。IEKF通过多次迭代线性化过程,增强非线性处理能力;UKF避免线性化,使用sigma点直接处理非线性问题;EKF则通过一次线性化简化处理。
616 14
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
480 59
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1228 77
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
322 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。